내 맴

[JPA] N+1 문제 발생 원인과 해결 본문

개발 공부/Database & JPA

[JPA] N+1 문제 발생 원인과 해결

뺙사우르수 2025. 4. 10. 12:56
728x90

✅ 정리

N+1 문제는 하나의 쿼리(N) 실행 후 연관된 데이터를 위해 추가로 N개의 쿼리가 발생하는 현상
연관 관계가 있는 엔티티 조회 시, 지연 로딩(fetch = LAZY)으로 인해 반복적으로 추가 쿼리 발생
→ 성능 저하 유발

 

✅ 상세 정리

1. 개념

  • N+1 문제:
    특정 엔티티 리스트를 조회(N)한 후, 각 엔티티의 연관 객체를 지연 로딩하면서 추가 쿼리(N) 발생 → 총 N+1개의 쿼리 실행됨.

 

2. 내부 동작

  • JPA에서 연관 관계는 기본적으로 LAZY로 설정됨.
  • 루트 엔티티 조회 시에는 1개의 쿼리만 실행됨.
  • 연관된 엔티티 접근 시점에 각각 쿼리가 발생하며, 루프를 돌며 연속 접근 시 N개의 추가 쿼리 실행됨.

 

3. 발생 예시

List<Team> teams = teamRepository.findAll(); // 1개의 쿼리 (N)
for (Team team : teams) {
    System.out.println(team.getMembers()); // 팀 수만큼 추가 쿼리 (N번)
}

 

4. 해결 방법

  • Fetch Join 사용
    연관된 엔티티를 한 번에 함께 조회
  • EntityGraph 사용
    JPQL이 아닌 메서드 쿼리에서 fetch 전략 설정
  • BatchSize 설정
    IN 절을 활용해 한 번에 여러 개를 조회하여 쿼리 수 줄임

 

5. 예외사항 및 주의점

  • Fetch Join 시에는 페이징 처리 제한 존재 (@OneToMany 관계에서 사용 시 주의)
  • 필요 없는 연관 객체까지 모두 로딩하면 과도한 메모리 사용 유발

 

✅ Java 코드 예시

1. N+1 문제 발생 코드

public List<Team> getTeams() {
    return entityManager.createQuery("SELECT t FROM Team t", Team.class)
                        .getResultList(); // 이후 getMembers() 접근 시 N개의 쿼리 추가 실행
}

 

2. Fetch Join으로 해결

public List<Team> getTeamsWithMembers() {
    return entityManager.createQuery(
        "SELECT t FROM Team t JOIN FETCH t.members", Team.class)
        .getResultList(); // 단 1개의 쿼리로 팀과 멤버 함께 조회
}
728x90

'개발 공부 > Database & JPA' 카테고리의 다른 글

[JPA] @OneToMany와 @ManyToOne의 차이  (0) 2025.04.09