๐ DATT 01 - ์ฑ๋ฅ ์ธก์ ํ๊ฒฝ ๊ตฌ์ถ ๋ฐ ์๋ต ์๊ฐ ๊ณ์ธก
์ค๋ ์์ ๋ชฉํ
์ค๋ ํด๊ฒฐํ๋ ค๋ ๋ฌธ์ :
- ํ์ฌ ๊ฒ์ API์ ์ค์ ์ฑ๋ฅ ์ํ ํ์
- ๋ณ๋ชฉ ์์น ํ์ธ์ ์ํ ๊ณ์ธก ํ๊ฒฝ ๊ตฌ์ถ
- ๊ฐ์ ์ ๊ธฐ์ค(Baseline) ๋ฐ์ดํฐ ํ๋ณด
- ์ดํ ์ฑ๋ฅ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋น๊ตํ ์ ์๋ ๊ธฐ๋ฐ ๋ง๋ จ
ํ์ฌ ๋ฌธ์ ์ํฉ
ํ์ฌ DATT ๊ฒ์ ๊ตฌ์กฐ๋ ์ฌ์ฉ์ ์์ฒญ ์๋ง๋ค ์ค์๊ฐ ํฌ๋กค๋ง์ ์ํํ๋ค.
1
2
3
4
5
6
7
8
9
Client
โ
Spring Boot
โ
FastAPI Crawling Engine
โ
External Place Sources
โ
PostgreSQL(PostGIS)
ํ์ฌ๋ ์ฒด๊ฐ์ ๋๋ฆฌ๋ค๊ณ ๋ง ์ธ์งํ๊ณ ์์ผ๋ฉฐ,
์ค์ ๋ก ์ด๋ ๊ณ์ธต์ด ๋ณ๋ชฉ์ธ์ง ์ ํํ๊ฒ ํ์
๋์ง ์์ ์ํ๋ค.
๋ฌธ์ ํ์
1
2
3
4
- ๊ฒ์ ์๋ต ์๊ฐ์ด ์ผ์ ํ์ง ์์
- ํน์ ์ง์ญ ๊ฒ์ ์ ์๋ต ์๊ฐ์ด ๊ธ๊ฒฉํ ์ฆ๊ฐ
- ์นดํ
๊ณ ๋ฆฌ ๋ณ๊ฒฝ ์ ์ฌ๋ก๋ฉ์ด ๋ฐ๋ณต์ ์ผ๋ก ๋ฐ์
- ์ธ๋ถ ์ฌ์ดํธ ์๋ต ์ํ์ ๋ฐ๋ผ ์ ์ฒด ์๋ต ์๊ฐ์ด ์ํฅ์ ๋ฐ์
๋ฐ์ ์กฐ๊ฑด
- ์ง์ญ ๋ฒ์๊ฐ ๋์ ๊ฒฝ์ฐ
- ์ธ๋ถ ์ฌ์ดํธ ์๋ต์ด ๋๋ฆฐ ๊ฒฝ์ฐ
- ํฌ๋กค๋ง ๋ฐ์ดํฐ๊ฐ ๋ง์ ๊ฒฝ์ฐ
- ์นดํ ๊ณ ๋ฆฌ ์ ํ์ด ๋ฐ๋ณต๋๋ ๊ฒฝ์ฐ
์์:
- ์ธ์ฒ
- ๊ฐ๋จ
- ํ๋
ํ์ฌ ์์ฌ๋๋ ์์ธ
ํ์ฌ ์์ ์์ ์์ฌ๋๋ ์์ธ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์ค์๊ฐ ์ธ๋ถ ํฌ๋กค๋ง I/O ๋ณ๋ชฉ
- ์ธ๋ถ ์ฌ์ดํธ ์๋ต ์ง์ฐ
- ์ฌ์ฉ์ ์์ฒญ ํ๋ฆ ๋ด๋ถ์ DB ์ ์ฅ ๊ณผ์ ํฌํจ
- ์์ฒญ๋ง๋ค ์ ์ฒด ํฌ๋กค๋ง์ด ์ฌ์ํ๋๋ ๊ตฌ์กฐ
์ฃผ์
ํ์ฌ ์์ธ์ ์์ง ํ์ ๋์ง ์์๋ค.
์ ๋ด์ฉ์:
1
์ถ์ธก ๊ธฐ๋ฐ ๊ฐ์ค
์ด๋ฉฐ,
์ค์ ๋ก๊ทธ ๋ฐ ์ฑ๋ฅ ์ธก์ ์ ํตํด ๊ฒ์ฆ์ด ํ์ํ๋ค.
์ธก์ ๋ฐ ๋ถ์
์ค๋ ๊ตฌ์ถํ ์ธก์ ํญ๋ชฉ
| ํญ๋ชฉ | ๋ชฉ์ |
|---|---|
| API ์๋ต ์๊ฐ | ์ ์ฒด ์ฒ๋ฆฌ ์๊ฐ ์ธก์ |
| FastAPI ํฌ๋กค๋ง ์๊ฐ | ์ธ๋ถ I/O ๋ณ๋ชฉ ํ์ธ |
| DB Insert ์๊ฐ | ์ ์ฅ ๋น์ฉ ํ์ธ |
| DB Query ์๊ฐ | ์กฐํ ๋น์ฉ ํ์ธ |
| ์คํจ์จ | Timeout/Error ํ์ธ |
์์งํ ๋ก๊ทธ
๊ตฌ์ถ ์์ ๋ก๊ทธ ํ์:
API ์๋ต ๋ก๊ทธ
1
2
3
4
5
[SEARCH_API]
keyword=์ธ์ฒ
category=cafe
durationMs=8420
status=200
ํฌ๋กค๋ง ๋ก๊ทธ
1
2
3
4
5
[CRAWLING]
keyword=์ธ์ฒ
category=cafe
durationMs=6210
resultCount=42
DB Query ๋ก๊ทธ
1
2
3
[DB_QUERY]
query=searchPlaces
durationMs=180
์ฑ๋ฅ ์ธก์ ๊ฒฐ๊ณผ
ํ์ฌ๋ ์์ง ๊ณ์ธก ํ๊ฒฝ ๊ตฌ์ถ ๋จ๊ณ๋ค.
๋ฐ๋ผ์ ์๋ ์์น๋ ์์ง ๋น์ด ์๋ ์ํ๋ค.
| ย | ย |
|---|---|
| ํญ๋ชฉ | ๊ฒฐ๊ณผ |
| ํ๊ท ์๋ต ์๊ฐ | ์ธก์ ์์ |
| P95 | ์ธก์ ์์ |
| P99 | ์ธก์ ์์ |
| ์คํจ์จ | ์ธก์ ์์ |
์ค์ ํ์ธ๋ ์์ธ
ํ์ฌ๋ ์์ง ์ธก์ ์ด์ ๋จ๊ณ๋ค.
๋ฐ๋ผ์ ์ค์ ์์ธ์ ์์ง ํ์ธ๋์ง ์์๋ค.
์ค๋์ ๋ชฉํ๋:
1
๋ณ๋ชฉ์ ์ถ์ธก์ด ์๋๋ผ ์์น ๊ธฐ๋ฐ์ผ๋ก ํ์ธ ๊ฐ๋ฅํ ์ํ๋ฅผ ๋ง๋๋ ๊ฒ
์ด๋ค.
์ ์ฉํ ํด๊ฒฐ ๋ฐฉ๋ฒ
์ค๋ ์งํํ ์์ :
- Spring Interceptor ๊ธฐ๋ฐ API ์๋ต ์๊ฐ ์ธก์
- FastAPI ํฌ๋กค๋ง ์๊ฐ ๋ก๊น ์ถ๊ฐ
- DB Query ์๊ฐ ๋ก๊น ์ถ๊ฐ
- k6 ๋ถํ ํ ์คํธ ํ๊ฒฝ ๊ตฌ์ถ
- ์ฑ๋ฅ ์ธก์ ๊ธฐ์ค ์๋๋ฆฌ์ค ์ ์
์ฝ๋ ๋ณ๊ฒฝ ์ฌํญ
Before
๊ธฐ์กด์๋ API ์๋ต ์๊ฐ์ ๋ณ๋๋ก ๊ธฐ๋กํ์ง ์์๋ค.
1
2
3
4
5
6
7
@GetMapping("/search")
public ResponseEntity<?> search(String keyword) {
List<Place> result = placeService.search(keyword);
return ResponseEntity.ok(result);
}
After
Interceptor ๊ธฐ๋ฐ ์๋ต ์๊ฐ ์ธก์ ์ถ๊ฐ ์์ .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Component
public class RequestLoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler
) {
request.setAttribute("startTime", System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex
) {
long startTime = (long) request.getAttribute("startTime");
long duration = System.currentTimeMillis() - startTime;
log.info(
"[SEARCH_API] path={}, durationMs={}, status={}",
request.getRequestURI(),
duration,
response.getStatus()
);
}
}
Trade-off
์ด๋ฒ ์์ ์ผ๋ก ์ธํด ๋ฐ์ํ๋ Trade-off:
| ์ฅ์ | ๋จ์ |
|---|---|
| ๋ณ๋ชฉ ์์น๋ฅผ ์์น ๊ธฐ๋ฐ์ผ๋ก ๋ถ์ ๊ฐ๋ฅ | ๋ก๊น ์ฆ๊ฐ๋ก ๋ก๊ทธ๋ ์ฆ๊ฐ |
| ๊ฐ์ ์ /ํ ๋น๊ต ๊ฐ๋ฅ | ์ธก์ ์ฝ๋ ์ถ๊ฐ ํ์ |
| ์ด์ ๊ด์ ๋ถ์ ๊ฐ๋ฅ | ์ด๊ธฐ ์์ ๊ณต์ ์ฆ๊ฐ |
์ด์ ๊ด์ ์์์ ๊ณ ๋ ค ์ฌํญ
์ด์ ํ๊ฒฝ์ด๋ผ๋ฉด ๋ค์ ํญ๋ชฉ๋ ์ถ๊ฐ์ ์ผ๋ก ๊ณ ๋ คํด์ผ ํ๋ค.
- TraceId ๊ธฐ๋ฐ ์์ฒญ ์ถ์
- ๋ก๊ทธ ์์ง ์์คํ ์ฐ๋
- ๋ก๊ทธ ๋ณด๊ด ์ ์ฑ
- P95/P99 ๊ธฐ๋ฐ ๋ชจ๋ํฐ๋ง
- ๋น์ ์ ์๋ต ํ์ง
ํ๊ณ
์ด๋ฒ ๋จ๊ณ์์ ์ค์ํ ๊ฒ์:
โ๊ฐ์ โ
๋ณด๋ค
โ์ธก์ ๊ฐ๋ฅํ ์ํ๋ฅผ ๋ง๋๋ ๊ฒโ
์ด๋ค.
์ค๋ฌด์์๋ ๋จ์ํ:
1
๋๋ฆฌ๋ค
๊ฐ ์๋๋ผ:
1
์ด๋๊ฐ ์ผ๋ง๋ ๋๋ฆฐ๊ฐ
๋ฅผ ์์น ๊ธฐ๋ฐ์ผ๋ก ์ค๋ช ํ ์ ์์ด์ผ ํ๋ค.
์ด๋ฒ ์์ ์ ์ดํ ๋ชจ๋ ์ฑ๋ฅ ๊ฐ์ ์ ๊ธฐ์ค์ (Baseline)์ด ๋๋ค.
๋ค์ ์์ ์์
๋ค์ ๋จ๊ณ:
- k6 ๊ธฐ๋ฐ ์ค์ ๋ถํ ํ ์คํธ ์ํ
- ์ง์ญ๋ณ/์นดํ ๊ณ ๋ฆฌ๋ณ ์๋ต ์๊ฐ ์ธก์
- ๋ณ๋ชฉ ์์น ๋ถ์
- ๋ฐฐ์น ๊ตฌ์กฐ ์ ํ ์ค๊ณ ์์