Post

๐Ÿ“ƒ DATT 01 - ์„ฑ๋Šฅ ์ธก์ • ํ™˜๊ฒฝ ๊ตฌ์ถ• ๋ฐ ์‘๋‹ต ์‹œ๊ฐ„ ๊ณ„์ธก

๐Ÿ“ƒ 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
- ๊ฒ€์ƒ‰ ์‘๋‹ต ์‹œ๊ฐ„์ด ์ผ์ •ํ•˜์ง€ ์•Š์Œ
- ํŠน์ • ์ง€์—ญ ๊ฒ€์ƒ‰ ์‹œ ์‘๋‹ต ์‹œ๊ฐ„์ด ๊ธ‰๊ฒฉํžˆ ์ฆ๊ฐ€
- ์นดํ…Œ๊ณ ๋ฆฌ ๋ณ€๊ฒฝ ์‹œ ์žฌ๋กœ๋”ฉ์ด ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ฐœ์ƒ
- ์™ธ๋ถ€ ์‚ฌ์ดํŠธ ์‘๋‹ต ์ƒํƒœ์— ๋”ฐ๋ผ ์ „์ฒด ์‘๋‹ต ์‹œ๊ฐ„์ด ์˜ํ–ฅ์„ ๋ฐ›์Œ

๋ฐœ์ƒ ์กฐ๊ฑด

  • ์ง€์—ญ ๋ฒ”์œ„๊ฐ€ ๋„“์€ ๊ฒฝ์šฐ
  • ์™ธ๋ถ€ ์‚ฌ์ดํŠธ ์‘๋‹ต์ด ๋А๋ฆฐ ๊ฒฝ์šฐ
  • ํฌ๋กค๋ง ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์€ ๊ฒฝ์šฐ
  • ์นดํ…Œ๊ณ ๋ฆฌ ์ „ํ™˜์ด ๋ฐ˜๋ณต๋˜๋Š” ๊ฒฝ์šฐ

์˜ˆ์‹œ:

  • ์ธ์ฒœ
  • ๊ฐ•๋‚จ
  • ํ™๋Œ€

ํ˜„์žฌ ์˜์‹ฌ๋˜๋Š” ์›์ธ

ํ˜„์žฌ ์‹œ์ ์—์„œ ์˜์‹ฌ๋˜๋Š” ์›์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ์‹ค์‹œ๊ฐ„ ์™ธ๋ถ€ ํฌ๋กค๋ง I/O ๋ณ‘๋ชฉ
  2. ์™ธ๋ถ€ ์‚ฌ์ดํŠธ ์‘๋‹ต ์ง€์—ฐ
  3. ์‚ฌ์šฉ์ž ์š”์ฒญ ํ๋ฆ„ ๋‚ด๋ถ€์— DB ์ €์žฅ ๊ณผ์ • ํฌํ•จ
  4. ์š”์ฒญ๋งˆ๋‹ค ์ „์ฒด ํฌ๋กค๋ง์ด ์žฌ์ˆ˜ํ–‰๋˜๋Š” ๊ตฌ์กฐ

์ฃผ์˜

ํ˜„์žฌ ์›์ธ์€ ์•„์ง ํ™•์ •๋˜์ง€ ์•Š์•˜๋‹ค.

์œ„ ๋‚ด์šฉ์€:

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
๋ณ‘๋ชฉ์„ ์ถ”์ธก์ด ์•„๋‹ˆ๋ผ ์ˆ˜์น˜ ๊ธฐ๋ฐ˜์œผ๋กœ ํ™•์ธ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ

์ด๋‹ค.


์ ์šฉํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์˜ค๋Š˜ ์ง„ํ–‰ํ•  ์ž‘์—…:

  1. Spring Interceptor ๊ธฐ๋ฐ˜ API ์‘๋‹ต ์‹œ๊ฐ„ ์ธก์ •
  2. FastAPI ํฌ๋กค๋ง ์‹œ๊ฐ„ ๋กœ๊น… ์ถ”๊ฐ€
  3. DB Query ์‹œ๊ฐ„ ๋กœ๊น… ์ถ”๊ฐ€
  4. k6 ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ•
  5. ์„ฑ๋Šฅ ์ธก์ • ๊ธฐ์ค€ ์‹œ๋‚˜๋ฆฌ์˜ค ์ •์˜

์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ

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)์ด ๋œ๋‹ค.


๋‹ค์Œ ์ž‘์—… ์˜ˆ์ •

๋‹ค์Œ ๋‹จ๊ณ„:

  1. k6 ๊ธฐ๋ฐ˜ ์‹ค์ œ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰
  2. ์ง€์—ญ๋ณ„/์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์‘๋‹ต ์‹œ๊ฐ„ ์ธก์ •
  3. ๋ณ‘๋ชฉ ์œ„์น˜ ๋ถ„์„
  4. ๋ฐฐ์น˜ ๊ตฌ์กฐ ์ „ํ™˜ ์„ค๊ณ„ ์‹œ์ž‘
This post is licensed under CC BY 4.0 by the author.