JPA N+1 문제 최종 정리
N+1 문제란?
**"한 번의 쿼리(1)로 N개의 데이터를 가져온 후, 각 데이터에 대해 추가 쿼리(N)를 실행하는 비효율적인 쿼리 문제"**를 말합니다.
발생 예시
List<Member> members = em.createQuery("SELECT m FROM Member m", Member.class).getResultList();
for (Member m : members) {
System.out.println(m.getTeam().getName());
}
- 위 코드 실행 시 다음과 같은 쿼리 발생:
- SELECT * FROM member → 1번 실행
- SELECT * FROM team WHERE id = ? → 멤버 수만큼 실행 (N번)
→ 총 1 + N번 쿼리 실행됨
원인
- 연관 관계가 fetch = LAZY일 경우, 연관 엔티티는 실제 접근 시점에 쿼리가 실행됩니다.
- 루프에서 .getTeam()과 같이 접근할 경우, 각 멤버마다 팀을 조회하는 쿼리가 추가로 발생합니다.
해결 방법
- Fetch Join 사용
- SELECT m FROM Member m JOIN FETCH m.team
- @EntityGraph 사용
- @EntityGraph(attributePaths = {"team"}) List<Member> findAll();
왜 EAGER는 비추천인가?
이유 설명
불필요한 데이터 조회 | 모든 연관 객체를 무조건 불러옴 |
쿼리 예측 어려움 | 내부적으로 조인 시점이 명확하지 않음 |
성능 저하 위험 | 연관 엔티티가 많을수록 느려짐 |
무한 루프 위험 | 양방향 관계 시 StackOverflow 발생 가능 |
→ 실무에서는 기본적으로 LAZY 사용을 권장하며, 필요한 시점에만 fetch join이나 @EntityGraph로 직접 조회하는 방식이 좋습니다.
기억할 핵심 포인트
- 기본은 LAZY로 설정하되,
- N+1 문제가 생길 때만 fetch join으로 최적화
- @ManyToOne(fetch = LAZY)는 실무에서 가장 안전한 기본 설계입니다.