1 분 소요

1편에서는 쿼리 튜닝과 호출 방식 개선을 통해 과도한 row 스캔 문제를 해결했습니다.
이번 글에서는 그 구조를 더 확장하여, 쿼리 호출 횟수 자체를 줄이는 방향으로 개선한 사례를 다룹니다._


🧩 남아 있던 문제

1차 개선 이후, 배지 조회 쿼리의 성능 자체는 충분히 가벼워졌습니다.
하지만 여전히 상품 수만큼 반복 호출된다는 구조적 한계가 있었습니다.

  • 예: 상품 목록에 100개의 상품이 있을 경우 → 쿼리 100회
  • 조회 1회당 부하는 작지만, 호출 자체가 반복되며 리소스를 지속적으로 점유

즉, “가벼운 쿼리”를 “많이” 날리고 있는 상황이었습니다.
이 반복을 없앨 수는 없을까?가 이번 개선의 출발점이었습니다.


🛠 개선 아이디어: 조회 구조 자체를 일괄 처리 방식으로 전환

기존 구조 (AS-IS)

  • 상품 1개 → 배지 쿼리 1회
  • 상품 100개 → 배지 쿼리 100회

개선 구조 (TO-BE)

  • 상품 100개 → prod_code를 모아 한 번에 조회
  • 배지 전체를 한 번에 가져온 뒤, 로직에서 상품별로 매칭
// prod_code를 최대 100개 단위로 나눠 일괄 조회
const chunks = split(prodCodes, 100);
for (const chunk of chunks) {
  fetchBadgesByProdCodes(chunk);
}

주의사항: IN 조건의 한계 대응

디자인모드 상으로는 한 페이지 당 최대 98개의 상품이 호출가능하므로 현실적으로는 문제가 없지만 향후 유연성을 확보하기 위해 청크 처리 방식을 도입했습니다.

  • prod_code가 100개 이상일 경우 → 100개씩 분할하여 순차 처리
  • 안전성과 확장성을 동시에 확보

✅ 개선 효과

항목 개선 전 개선 후
쿼리 호출 횟수 상품 수만큼 호출 (예: 100회) 최대 1~2회로 일괄 조회
DB/네트워크 부하 상품 수에 비례하여 선형 증가 거의 고정 수준 유지
로직 복잡도 상품마다 개별 처리 로직 필요 일괄 조회 후 매핑 처리
확장성 상품 수 증가 시 부하 동반 증가 상품 수 무관하게 일정
장애 가능성 과도한 호출로 인한 커넥션 부담 위험 호출 횟수 최소화로 안정

마무리하며…

이번 개선은 단순히 쿼리 성능을 높이는 수준을 넘어,
조회 구조 자체를 재설계하여 호출 횟수를 원천적으로 줄인 사례였습니다.

이러한 구조적 접근은 단기적인 효과뿐 아니라
서비스가 확장될 때 발생할 수 있는 잠재적인 병목을 사전에 차단할 수 있다는 점에서 더욱 의미가 큽니다.

앞으로도 반복 호출, 불필요한 쿼리 구조, 확장성에 영향을 주는 패턴들을 지속적으로 점검하고 개선해 나가야겠습니다.

(원래 시리즈로 할 생각이 없었는데, 한 아이템에서 두 개선건이 나오게되었네요 😅)

댓글남기기