Spring Boot/JPA(Java Persistence API)

N + 1 문제 해결

재윤 2025. 8. 28. 17:58
반응형
  • JPA에서 연관관계가 LAZY(지연 로딩)일 때 자주 발생
  • 예시: Team과 Member 관계 (1:N)
List<Team> teams = em.createQuery("select t from Team t", Team.class)
                     .getResultList();

for (Team team : teams) {
    System.out.println(team.getMembers().size());
}

실행 쿼리

  1. select * from team → 팀 목록(N개)
  2. 각 팀의 getMembers() 접근할 때마다:
  3. select * from member where team_id=? → 팀마다 N번 실행

최종적으로 1 + N번의 쿼리 발생 = N+1 문제

 

N+1 문제 해결 방법

1. Fetch Join (가장 많이 쓰는 방법)

  • JPQL에서 연관된 엔티티를 한 번에 함께 조회
// 팀 + 멤버를 한 번에 조회
List<Team> teams = em.createQuery(
    "select t from Team t join fetch t.members", Team.class)
    .getResultList();

실행 쿼리:

select t.*, m.*
from team t
join member m on t.id = m.team_id
  • 장점: 한 번에 가져와서 N+1 문제 해소
  • 단점: 조인 때문에 중복 데이터가 생길 수 있음 → DISTINCT 필요할 수도

2. EntityGraph (Spring Data JPA에서 자주 씀)

  • 어노테이션으로 Fetch Join 효과를 줌
@EntityGraph(attributePaths = "members")
@Query("select t from Team t")
List<Team> findAllWithMembers();

내부적으로 Fetch Join을 써서 한 번에 조회

3. Batch Fetching (hibernate.default_batch_fetch_size)

  • 지연 로딩을 쓰되, 한 번에 여러 건을 IN 쿼리로 묶어서 가져오기
  • Hibernate 설정:
spring.jpa.properties.hibernate.default_batch_fetch_size=100
  • 실행 쿼리 예시
select * from member where team_id in (1, 2, 3, ...);

즉, 팀을 10개 조회하면 각 팀 멤버를 하나씩 따로 안 불러오고,

한 번의 IN 쿼리로 최대 100개씩 모아서 가져옴

반응형

'Spring Boot > JPA(Java Persistence API)' 카테고리의 다른 글

영속성 컨텍스트  (0) 2025.08.28
쿼리 방법(JPQL/Criteria)  (1) 2025.08.28
매핑 규칙(어노테이션)  (1) 2025.08.28
EntityManager API  (0) 2025.08.28