JPA N+1 및 Cartesian Product 문제

Kim jisu·2025년 7월 27일
0

 Debugging Note

목록 보기
35/37

📅 발생 일시

  • 2025-07-27

🌐 영향 범위

  • /store/detail 페이지 접근 시 상품 리스트가 중복되어 출력되는 UI 오류 발생

🧩 문제 요약

✅ 확인된 이슈: JPA의 Fetch Join으로 인한 카티전 곱 (Cartesian Product)

  • 특정 Store 조회 시 상품과 이미지 리스트가 모두 중복되어 반환됨
  • 프론트엔드에서는 상품이 실제 개수보다 여러 번 렌더링되어 사용자 혼란 발생

🔍 원인 분석

🔥 핵심 원인: 다중 LEFT JOIN FETCH로 인한 카티전 곱

SELECT s FROM Store s
LEFT JOIN FETCH s.products
LEFT JOIN FETCH s.images
WHERE s.uuid = :id

⛔ 문제점

  • 해당 Store에:

    • 상품이 3개
    • 이미지가 2개 있는 경우
  • 위 쿼리는 내부적으로 3 × 2 = 6개의 행을 반환

  • 결과적으로 같은 상품이 이미지 수만큼 반복되어 조회됨

💥 증상

  • 프론트에서 같은 상품이 여러 번 출력됨
    → 예: 상품 A, 상품 A, 상품 B, 상품 B, 상품 C, 상품 C

🔧 조치 사항

✅ 해결 전략: 쿼리 분리 방식으로 Fetch Join 분리

📌 적용 쿼리

1. 상품 전용 쿼리

SELECT s FROM Store s
LEFT JOIN FETCH s.products
WHERE s.uuid = :id

2. 이미지 전용 쿼리

SELECT s FROM Store s
LEFT JOIN FETCH s.images
WHERE s.uuid = :id

🧠 병합 전략

  • 두 쿼리 결과를 Repository 구현체에서 수동 병합
  • 중복 제거를 위해 Set 또는 Stream distinct() 등 활용

✅ 적용 결과

항목적용 전적용 후
조회된 결과 row 수61 (각 컬렉션 별)
상품 중복있음 (N × M)없음
프론트 렌더링상품이 2~3회 반복상품 1회만 표시
구조 유지보수성JOIN 수 증가로 복잡로직 분리로 명확

📈 로그 예시

🔎 적용 전 로그

select store0_.id, product1_.id, image2_.id ...
-- 결과 row: 6

✅ 적용 후 로그

-- Query 1
select store0_.id, product1_.id ...
-- Query 2
select store0_.id, image2_.id ...
-- 결과 row: 각각 3, 2

🧪 테스트 결과

항목기대실제
중복 상품 제거OO
이미지 정상 출력OO
DB 부하감소 (JOIN 복잡도 ↓)O

🧠 회고 및 개선 방향

  • JPA 사용 시 다중 컬렉션 Fetch Join은 주의가 필요

    • 1\:N 관계가 여러 개일 경우 반드시 카티전 곱 여부를 테스트
  • 쿼리 분리 전략은 유지보수 및 성능 관점에서 유리

  • 향후 복잡한 관계는 @BatchSize, @EntityGraph 등의 대안도 고려


profile
Dreamer

0개의 댓글