JPA에서 @EntityGraph를 써야 하는 이유 (N+1 문제 해결하기)

김현정·2025년 4월 16일
0

1. N+1 문제란?

JPA를 사용할 때, 연관된 엔티티를 지연로딩(LAZY) 으로 설정하면 발생하는 대표적인 성능 문제이다.

예를 들어)
Todo엔티티가 User엔티티와 @ManyToOne(fetch = FetchType.LAZY)로 연관되어 있을 때

List<Todo> todos = todoRepository.findAll();
for (Todo todo : todos) {
    System.out.println(todo.getUser().getName());
}

이 코드를 실행하면 다음과 같은 쿼리가 발생한다.

  • SELECT * FROM todos → 1번

  • SELECT * FROM users WHERE id = ?todos.size()만큼 반복 (N번)

➡️ 총 1 + N번의 쿼리가 발생하므로, 이를 N+1 문제라고 한다.


2. 이대로 두면 생기는 문제

  • Todo가 100개만 있어도 User를 조회하는 쿼리가 100번 발생
  • 성능 저하, 응답 속도 지연
  • 데이터가 많아질수록 문제는 기하급수적으로 커짐

3. 해결 방법 1 : fetch join (JPQL)

JPA의 JPQL에서 JOIN FETCH 구문을 사용하면 한 번에 데이터를 가져올 수 있다.

@Query("SELECT t FROM Todo t JOIN FETCH t.user")
List<Todo> findAllWithUser();
  • Todo와 연관된 User를 한 번의 쿼리로 조회
  • N+1 문제 해결 가능

❗️하지만 단점

  • 매번 JPQL을 직접 작성해야 함.
  • 페이징(Pageable) 기능과 호환이 안 좋음.
    • JPA는 fetch join이 포함 된 JPQL에서 count 쿼리를 자동 생성하지 못함

4. 해결 방법 2 : @EntityGraph

@EntityGraph연관 된 엔티티를 함께 조회할 수 있도록 JPA에 알려주는 어노테이션

사용 예시

@EntityGraph(attributePaths = {"user"})
Page<Todo> findAllByOrderByModifiedAtDesc(Pageable pageable);

장점

  • fetch join과 같은 효과
  • JPQL 없이 깔끔하게 선언만으로 설정
  • Pageable 페이징 처리 완벽 호환
  • 유지보수가 훨씬 쉬움

5. 실전 적용 예시

기존 코드(JPQL)

@Query("SELECT t FROM Todo t LEFT JOIN FETCH t.user u ORDER BY t.modifiedAt DESC")
Page<Todo> findAllByOrderByModifiedAtDesc(Pageable pageable);

변경 후 코드 (@EntityGraph)

@EntityGraph(attributePaths = {"user"})
Page<Todo> findAllByOrderByModifiedAtDesc(Pageable pageable);
  • JPQL 제거
  • N+1 해결
  • 페이징 문제 없음

6. 연관 된 엔티티가 2개 이상일 경우?

@EntityGraph(attributePaths = {"user", "comments", "managers"})
Page<Todo> findAllByOrderByModifiedAtDesc(Pageable pageable);

단, List처럼 1:N 관계가 많을 경우 페이징 성능에 영향을 줄 수 있으므로 DTO Projection을 병행하는 것이 좋다.


7. 정리

구분          | 설명
--------------------------------------------------------------------
LAZY 연관 로딩 | 필요한 시점에 쿼리 발생 → N+1 문제 발생 가능
fetch join   | JPQL로 조인해서 한 번에 조회 가능 (단점: 페이징과 호환 어려움)
@EntityGraph | 선언형 방식으로 연관 로딩 + JPQL 없이 사용 가능 + 페이징 완벽 지원

8. 마무리

@EntityGraph는 JPA에서 성능 최적화와 유지보수성을 동시에 잡을 수 있는 강력한 도구이다.
특히 페이징이 필요한 서비스에서 연관 엔티티를 함께 로딩하고 싶다면, @EntityGraph를 적극 사용하는 것이 좋다.

0개의 댓글