SSG.COM 기획전 API, 구조 변경 후 성능 개선기
기획전 API의 기존 구조는 단일 도큐먼트 내 중복 조회, 전체 데이터 조회, 중복 저장 등 성능 병목(Performance Bottleneck)을 야기함
상품 정보를 개별 도큐먼트로 분리하고 그룹 기획전 정보를 별도 컬렉션으로 분리하는 데이터 모델 재설계(Data Model Redesign)를 진행함
구조 변경 후 병렬 호출 부하, MongoDB의 `getMore`/`killCursor` 문제 등 예상치 못한 성능 이슈가 발생했으나, 조회량 최적화(Query Volume Optimization)로 해결함
분리 자체보다 쿼리 패턴과 운영 부하를 고려한 설계의 중요성을 재확인함
초기 데이터 모델의 문제점 분석
기존 기획전 API는 단일 도큐먼트(Single Document) 기반의 데이터 모델을 사용했습니다. 이 구조는 하나의 기획전 정보를 담는 `planshop_item_info` 컬렉션에서 모든 테마와 상품 목록을 관리했습니다. 이로 인해 API 내부에서 동일한 도큐먼트를 중복 조회하는 문제, 특정 테마의 상품만 필요한데도 전체 데이터를 가져와 후처리해야 하는 비효율, 그룹 기획전 정보가 각 기획전 마스터에 중복 저장되는 현상이 발생했습니다. 또한, 기획전 규모에 따라 도큐먼트 크기가 예측 불가능하게 증가하여 네트워크 전송량 및 메모리 사용량 증가의 잠재적 위험을 안고 있었습니다.
데이터 모델 재설계: 분리의 이점과 과제
개선 작업은 크게 두 가지 방향으로 진행되었습니다. 첫째, 상품 정보를 `planshop_theme_item` 컬렉션으로 분리하여 1 도큐먼트 = 1 상품 구조를 채택했습니다. 이를 통해 중첩 구조를 해체하고, aggregation 쿼리를 단순 find로 변경하여 필요한 데이터만 정확하게 가져올 수 있게 되었습니다. 둘째, 그룹 기획전 정보를 `planshop_group` 컬렉션으로 분리하여 중복 저장을 제거하고 그룹 수정 시 불필요한 전체 갱신을 방지했습니다. 이러한 데이터 모델 분리(Data Model Separation)는 데이터 역할의 명확성과 조회 유연성을 높였지만, 동시에 여러 도큐먼트를 조회하고 조립해야 하는 새로운 복잡성을 야기했습니다.
병렬 호출 부하 및 MongoDB Cursor 문제
새로운 구조 적용 후, 기획전 전체 상품을 가져오기 위해 여러 테마별로 상품 정보를 병렬 호출하는 과정에서 DB 커넥션 부하(DB Connection Load)가 급증했습니다. 특히 멀티 기획전 환경에서는 기획전 수 × 테마 수만큼의 동시 커넥션이 발생했습니다. 이를 완화하기 위해 기획전 ID 단위 병렬 호출 및 IN절 사용으로 변경했으나, 반환 도큐먼트 수 증가로 인해 MongoDB의 `getMore` 명령이 반복 발생하며 네트워크 왕복 횟수(Network Round Trips) 증가라는 또 다른 문제를 야기했습니다. 이는 `limit`과 `cursorBatchSize` 설정을 통해 해결하려 했으나, MongoDB Java Driver의 버그로 인해 추가적인 조정이 필요했습니다.
최종 성능 최적화: 조회량 감소 전략
근본적인 성능 개선을 위해, 실제 화면 노출 상품 수가 테마별 상품 수보다 훨씬 적다는 점에 착안하여 조회량 자체를 줄이는 전략을 채택했습니다. 멀티 기획전의 경우, 테마별 상품을 모두 가져오는 대신 기획전 ID 단일 조건으로 상위 50건의 상품만 조회하도록 변경했습니다. 이 방식은 병렬 호출이나 IN절 조건 없이 단순 조회로 가능했으며, 결과적으로 `getMore`나 `killCursor` 발생 가능성을 크게 낮추고 전체적인 응답 속도(Overall Response Time)를 향상시켰습니다. 이는 분리된 구조의 이점을 살리면서도 운영 부하를 최소화하는 균형 잡힌 접근이었습니다.
구조 설계 시 고려사항: 분리의 함정
이번 경험을 통해 데이터 분리(Data Separation)가 반드시 빠른 성능으로 이어지는 것은 아님을 확인했습니다. 분리는 데이터의 명확성과 조회 유연성을 제공하지만, 동시에 여러 도큐먼트 조회 및 조립, 인덱스 재설계, `getMore`/`killCursor`와 같은 예상치 못한 운영 이슈를 동반할 수 있습니다. 따라서 구조를 설계할 때는 분리의 이점뿐만 아니라, 해당 구조에서 발생할 쿼리 패턴(Query Patterns)과 운영상의 부하(Operational Load)까지 종합적으로 고려하는 것이 중요합니다. 최적의 분리 단위는 비즈니스 요구사항과 기술적 제약 사이의 균형점을 찾는 데 달려 있습니다.