개요
항목 | 내용 |
기간 | 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 의 활성화 조건
•
저성능 고 병렬 과 고성능 저 병렬 의 차이와 각각의 장단점