Search

상품 저장 시스템 성능 최적화

개요

항목
내용
기간
2025.11
역할
병목 분석 및 개선 주도
기술 스택
Kotlin, Spring Boot, MongoDB, Kubernetes
핵심 성과
5만 건 대량 적재 기준 200초 → 2초 (100배 개선)

배경

커넥트웨이브(다나와)는 대규모 상품 데이터를 처리하는 가격비교 플랫폼입니다. 레거시 시스템에서 신규 플랫폼으로 마이그레이션하는 과정에서, 기존 작업자가 비즈니스 로직만 구현한 상태의 상품 저장 시스템을 인수받았습니다.
초기 상태에서 5만 건 저장에 약 200초가 소요되어, 실시간 상품 데이터 처리에 적합하지 않은 상황이었습니다.

문제 분석

1차 분석: Thread Dump

Pod 내부에 접근하여 Thread Dump를 수집
MongoDB 중첩 필드 처리 시 예외가 누적되는 병목 지점을 파악
flowchart LR
    A[API 요청<br>5만 건 저장] --> B[Spring Boot 앱]
    B --> C[MongoDB 드라이버]
    C --> D[중첩 필드 처리]
    D --> E[예외 누적 ❌]
    E --> F[200초 소요]
Mermaid
복사

병목 상세

중첩 필드의 이름을 기준으로 파싱하는 로직에서, 이름이 문자열로 되어있고, 유틸리티 함수에서 예외를 투척하며, 모든 필드에 대해 예외가 중첩되는 상황. 즉, 10개의 중첩 필드가 있고, 그 중첩 필드 내부에 중첩이 또 있으면 곱해져서 예외가 중첩되는 구조.
Thread Dump 에서 특정 예외가 수백개 이상 발생하고, 스텍트레이스를 로그로 찍는 행위 등이 반복되며 문자열 파싱 로직 에서 OOM 이 발생하기도 하는 것을 확인함.

무엇을 했나

1단계: 병목 제거 (200초 → 10초)

MongoDB 중첩 필드 처리 시 예외가 누적되는 로직을 수정
변환을 위한 필드 이름을 문자열로 관리하지 않고, 코틀린의 클래스 필드 이름을 사용하도록 하는 방식을 이용해 타입 안정성을 가져가는 방향으로 수정.
이 과정에서 리플랙션 관련 오버헤드가 추가되었으나, 문제의 본질은 예외 누적이었으므로 안정성을 위해 감내할 수 있는 부분이라 판단.

2단계: 추가 최적화 (10초 → 2초)

flowchart TD
    A[5만 건 저장 요청] --> B[Pod Scale-out]
    B --> C[여러 Pod에 분산]
    C --> D[Bulk 처리]
    D --> E[Update/Insert 분리]
    E --> F[2초 달성]
Mermaid
복사
Pod Scale-out: 기존 48개(카프카 파티션 개수) pod 대신 각 pod 의 스펙을 2배로 올리고 개수를 절반으로 줄이고, G1GC 를 활성화 하여 각 Pod 에서 JVM 오버헤드가 차지하는 비율을 줄여 자원 효율성과 성능을 모두 잡음.
Bulk 처리: 개별 insert를 MongoDB Bulk Operations로 전환하여 네트워크 라운드트립 감소
Update/Insert 분리: 기존 데이터 업데이트와 신규 데이터 삽입을 분리하여 불필요한 upsert 오버헤드 제거

결과

단계
소요 시간
개선율
초기 상태
200초
-
1단계 (병목 제거)
10초
20배
2단계 (추가 최적화)
2초
100배

배운 점

Thread Dump 활용법과 부하 테스트와 튜닝에서 목표 수치와 가정의 중요성
G1GC 의 활성화 조건
저성능 고 병렬 과 고성능 저 병렬 의 차이와 각각의 장단점