[스프링] 스프링 Data JPA(6) @EntityGraph

2025. 8. 21. 19:27·스프링 데이터 JPA

JPA를 사용하다 보면 가장 자주 겪는 성능 문제 중 하나가 바로 N+1 문제다.


회원(Member)과 팀(Team)이 지연 로딩 관계라고 할 때, 단순히 회원 목록을 조회했는데 이후 각 회원의 팀을 가져오는 순간마다 추가 쿼리가 실행된다. 회원이 100명이라면 1(회원 조회) + 100(팀 조회) = 101번의 SQL이 실행되는 꼴이다.

 

작은 데이터에서는 체감이 안 될 수 있지만, 실제 서비스에서는 쿼리 폭발로 이어져 치명적인 성능 문제가 된다.

 

N+1 문제 예시

@Test
public void findMemberLazy() throws Exception {
    Team teamA = new Team("teamA");
    Team teamB = new Team("teamB");
    teamRepository.save(teamA);
    teamRepository.save(teamB);

    memberRepository.save(new Member("member1", 10, teamA));
    memberRepository.save(new Member("member2", 20, teamB));
    em.flush();
    em.clear();

    List<Member> members = memberRepository.findAll();

    for (Member member : members) {
        member.getTeam().getName(); // 여기서 매번 추가 쿼리 발생
    }
}

 

실행되는 SQL 흐름

  1. memberRepository.findAll() → 회원 목록 조회 (SQL 1번)
  2. 반복문 돌면서 member.getTeam().getName() 실행 → 회원마다 팀을 지연 로딩 (SQL N번)

즉, 전체적으로 1 + N번의 쿼리가 실행된다. 회원이 100명이라면 101번의 SQL이 실행되는 것이다.
→ 이것이 바로 N+1 문제다.

 

해결책 1: JPQL 페치 조인

이 문제를 해결하는 전통적인 방법은 fetch join이다. (지연로딩과 조회 성능 최적화 장 참고)

@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();

이렇게 작성하면 Member와 Team을 한 번의 SQL로 가져올 수 있다.

 

실행되는 SQL

select m.*, t.* 
from member m
left join team t on m.team_id = t.id;

회원과 팀이 함께 조회되므로 N+1 문제가 발생하지 않는다.

 

해결책 2: @EntityGraph

JPQL을 직접 쓰지 않고도 fetch join을 적용할 수 있는 방법이 있다.
바로 스프링 데이터 JPA의 @EntityGraph다.

// 공통 메서드 오버라이드
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();

// JPQL + EntityGraph
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();

// 메서드 이름 기반 쿼리에도 적용 가능
@EntityGraph(attributePaths = {"team"})
List<Member> findByUsername(String username);
  • attributePaths = {"team"}을 지정하면 JPA가 내부적으로 LEFT OUTER JOIN FETCH를 붙여서 실행한다.
  • 코드에 join fetch를 직접 쓰지 않아도 되니 훨씬 간단하다.

 

왜 EntityGraph가 더 간편한가?

  1. JPQL을 직접 작성할 필요가 없다.
    → findAll, findById 같은 공통 메서드에도 바로 적용 가능하다.
  2. 가독성이 높다.
    → @EntityGraph(attributePaths = {"team"})만 봐도 어떤 연관관계를 함께 가져오는지 바로 알 수 있다.
  3. 중복 코드 감소
    → 여러 리포지토리 메서드에서 반복적으로 fetch join을 쓰는 대신 어노테이션만 붙이면 된다.

 

언제 EntityGraph, 언제 JPQL?

  • EntityGraph
    • 단순히 연관 엔티티 한두 개만 같이 조회하면 충분한 경우
    • 예: Member와 Team 같이 단순한 fetch join
  • JPQL fetch join
    • 조건이 복잡한 쿼리
    • 예: 회원 나이가 20 이상이고, 팀 이름이 'teamA'인 경우

즉, 간단한 경우는 EntityGraph, 복잡한 경우는 JPQL이 정석이다.

 

NamedEntityGraph 활용 (재사용)

엔티티에 그래프를 정의해두고 재사용할 수도 있다.

@NamedEntityGraph(
    name = "Member.all",
    attributeNodes = @NamedAttributeNode("team")
)
@Entity
public class Member {}

 

@NamedEntityGraph(
    name = "Member.all",
    attributeNodes = @NamedAttributeNode("team")
)
@Entity
public class Member {}

(하지만 실무에서 자주 사용되진 않는다 한다.)

 

마무리하며

N+1 문제는 JPA에서 가장 많이 마주치는 성능 이슈다.
이를 해결하기 위해 전통적으로는 JPQL fetch join을 사용했지만, 스프링 데이터 JPA는 이를 더 단순화한 @EntityGraph를 제공한다.

  • 단순한 연관관계 조회라면 @EntityGraph를 사용하는 것이 간단하고 직관적이다.
  • 조건이 복잡하거나 여러 엔티티를 한꺼번에 묶어야 한다면 JPQL fetch join이 여전히 필요하다.

즉, 두 기능은 서로 보완적인 도구이기에 상황에 맞게 선택하는 것이 중요할 것이다.

 

감사합니다.

 

'스프링 데이터 JPA' 카테고리의 다른 글

[스프링] 스프링 Data JPA(마지막) Auditing  (2) 2025.08.26
[스프링] 스프링 Data Jpa(7) 사용자 정의 리포지토리 구현  (1) 2025.08.26
[스프링] 스프링 Data JPA(5) 벌크성 수정쿼리  (0) 2025.08.21
[스프링] 스프링 Data JPA(4) JPA 페이징과 정렬  (0) 2025.08.21
[스프링] 스프링 Data JPA(3) @Query, 리포지토리 메소드에 쿼리 정의하기  (0) 2025.08.21
'스프링 데이터 JPA' 카테고리의 다른 글
  • [스프링] 스프링 Data JPA(마지막) Auditing
  • [스프링] 스프링 Data Jpa(7) 사용자 정의 리포지토리 구현
  • [스프링] 스프링 Data JPA(5) 벌크성 수정쿼리
  • [스프링] 스프링 Data JPA(4) JPA 페이징과 정렬
0kingki_
0kingki_
자바 + 스프링 웹 개발
  • 0kingki_
    0kingki_
    0kingki_
  • 전체
    오늘
    어제
    • 분류 전체보기 (134)
      • 코딩 테스트 (54)
      • 자바 (21)
      • 스프링 (27)
      • 타임리프 (16)
      • 스프링 데이터 JPA (8)
      • 최적화 (2)
      • QueryDSL (4)
      • AWS (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    mvc
    스프링
    Java
    LocalDateTime
    타임리프
    스프링 데이터 JPA
    JPA
    spring
    QueryDSL
    thymeleaf
    컬렉션
    불변객체
    예외처리
    코딩테스트
    쿼리
    fetch join
    백준
    SOLID
    쿼리dsl
    코딩 테스트
    BFS
    재귀
    SpringDataJpa
    자바
    dfs
    최적화
    객체지향
    스프링 컨테이너
    다형성
    예외 처리
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
0kingki_
[스프링] 스프링 Data JPA(6) @EntityGraph
상단으로

티스토리툴바