#Backend

이상적인 구조가 빠른 성능은 아닙니다

이상적인 구조가 빠른 성능은 아닙니다
01

Summary

데이터 정규화가 성능 저하를 부른다? SSG.COM의 MongoDB 최적화 반전 드라마

이상적인 아키텍처보다 중요한 '조회 패턴' 중심의 실전 백엔드 개선 가이드

단순히 보기 좋은 구조가 빠른 성능을 보장하지 않는다는 실무적인 교훈을 전달하는 아티클입니다. SSG.COM 프론트서비스개발팀이 MongoDB의 거대 도큐먼트를 해체하며 겪은 예상치 못한 성능 이슈와 이를 해결하기 위한 기술적 사투를 담고 있습니다.

  • 01중첩 도큐먼트 구조 해체를 통한 데이터 중복 제거 및 데이터 모델링 최적화
  • 02MongoDB의 내부 동작 원리인 getMore와 cursorBatchSize를 활용한 네트워크 오버헤드 해결
  • 03Java Driver의 limit == cursorBatchSize 버그를 우회하는 실무적인 트릭 공유
  • 04병렬 호출로 인한 DB 커넥션 부하를 IN절과 조회 범위 제한으로 해결한 과정
  • 05비즈니스 요구사항을 재해석하여 쿼리 호출량 자체를 줄이는 전략적 접근

RECOMMENDATION

MongoDB를 사용하면서 데이터 모델링 변경을 고민 중이거나, 원인 모를 API 레이턴시 증가로 고통받는 백엔드 개발자에게 필독을 권장합니다.

The Problem

기존 기획전 API는 단일 MongoDB 도큐먼트에 모든 테마와 상품 데이터를 중첩 저장하는 구조로 인해, 불필요한 전체 데이터 조회와 중복 쿼리가 발생하고 있었습니다. 또한 기획전 규모에 따라 도큐먼트 크기가 비대해지면서 네트워크 전송량과 메모리 사용량이 예측 불가능하게 증가하는 위험 요소를 안고 있었습니다.

The Solution

데이터 구조를 '1 도큐먼트 = 1 상품' 단위로 세분화하여 필요한 데이터만 선택적으로 조회할 수 있도록 개선하고, 그룹 정보를 별도 컬렉션으로 분리했습니다. 성능 최적화를 위해 MongoDB의 cursorBatchSize와 limit 설정을 조정하여 getMore 발생을 억제하고, 멀티기획전 조회 시 비즈니스 요구사항에 맞춰 조회 범위를 제한하는 로직을 도입했습니다.

The Result

데이터 구조가 명확해지면서 기존의 복잡한 aggregation 쿼리를 단순 find로 대체할 수 있게 되었으며, 중복 저장 문제가 해결되었습니다. 배치 사이즈 최적화를 통해 네트워크 왕복 횟수를 줄여 API 레이턴시를 안정화하였고, 수치상 명확한 성능 개선 지표를 확보했습니다.

Trade-off

정규화된 구조로 변경하면서 기존 1회의 조회로 끝나던 작업이 다수의 도큐먼트 조회로 파편화되어 DB 커넥션 부하가 일시적으로 급증하는 부작용이 있었습니다. 또한 MongoDB Java Driver의 특정 버그로 인해 limit과 배치 사이즈 설정 시 추가적인 트릭이 필요하게 되는 등 관리 포인트가 늘어난 측면이 있습니다.

03

Key Concepts

Concept · 01

MongoDB cursorBatchSize

MongoDB 쿼리 결과를 클라이언트에 전송할 때 한 번의 네트워크 응답에 포함시킬 도큐먼트의 최대 개수입니다.

  • 기본 배치 사이즈 초과 시 추가 데이터를 가져오기 위한 getMore 명령이 발생하여 성능이 저하될 수 있습니다.
  • 본문에서는 limit 값과 배치 사이즈를 맞추어 한 번의 왕복으로 데이터를 모두 가져오도록 최적화했습니다.
Concept · 02

Aggregation Framework

MongoDB 내에서 여러 단계의 파이프라인을 거쳐 데이터를 가공하고 분석하는 강력한 연산 도구입니다.

  • 기존 구조에서는 중첩된 데이터를 추출하기 위해 필수적이었으나 연산 부하가 컸습니다.
  • 구조 개선 후에는 단순한 find 쿼리로 대체되어 DB 연산 효율을 높였습니다.
Concept · 03

Connection Pooling

데이터베이스와의 연결을 미리 생성하여 유지함으로써 매번 연결을 맺고 끊는 오버헤드를 줄이는 기법입니다.

  • 도큐먼트 분리 후 병렬 호출이 늘어나면서 순간적인 커넥션 부족 현상이 발생했습니다.
  • 조회 방식 변경(IN절 활용)을 통해 동시 커넥션 점유를 최적화했습니다.