[JAVA] JPA Fetch 전략 완벽 가이드 – Lazy vs Eager 실무 적용법

JPA를 사용하는 백엔드 개발자라면 누구나 한 번쯤은 예상치 못한 쿼리 폭발 또는 무한 참조를 경험했을 겁니다. 대부분의 원인은 연관관계 설정 시 Fetch 전략을 정확히 이해하지 못한 데서 비롯됩니다.

FetchType.LAZYFetchType.EAGER의 동작 방식, 각각의 장단점, 그리고 실무에서 언제 어떤 전략을 써야 하는지를 예제를 통해 알아보겠습니다.

 

 

1. Fetch 전략이란?

JPA에서 연관된 엔티티를 언제 로딩할지 결정하는 정책을 Fetch 전략이라 부릅니다. 크게 두 가지 방식이 있습니다:

  • Lazy Loading (지연 로딩): 연관된 엔티티를 실제로 사용할 때 쿼리를 실행
  • Eager Loading (즉시 로딩): 엔티티를 조회할 때 연관된 엔티티도 함께 조회

 

2. 기본 전략: 단방향 vs 양방향

  • @ManyToOne, @OneToOne은 기본적으로 FetchType.EAGER
  • @OneToMany, @ManyToMany는 기본적으로 FetchType.LAZY

즉, 연관관계 방향에 따라 기본 전략이 다르므로 명시적으로 fetch = FetchType.LAZY를 설정하는 것이 안전합니다.

 

3. 예제로 보는 Lazy vs Eager

엔티티 설정

@Entity
public class Post {
    @Id @GeneratedValue
    private Long id;

    private String title;

    @OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
    private List<Comment> comments = new ArrayList<>();
}
@Entity
public class Comment {
    @Id @GeneratedValue
    private Long id;

    private String content;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "post_id")
    private Post post;
}

 

Lazy Loading 결과

아래 코드처럼 Post만 조회할 경우, comments는 실제 사용될 때까지 쿼리가 실행되지 않습니다.

Post post = postRepository.findById(1L).get();
// 쿼리: select * from post where id = 1
int size = post.getComments().size();
// 이 시점에서 쿼리: select * from comment where post_id = 1

 

Eager Loading 결과

fetch = FetchType.EAGER일 경우, Post 조회 시 댓글도 함께 불러옵니다.

select * from post left outer join comment on post.id = comment.post_id where post.id = 1

필요 없는 연관 데이터를 무조건 끌고 오기 때문에 성능 저하쿼리 복잡도 증가로 이어질 수 있습니다.

 

 

4. 실무 팁: Lazy가 기본, 필요한 곳만 Eager

  • Lazy를 기본값으로 두고, 필요한 경우 @EntityGraph, join fetch 등으로 컨트롤
  • API 응답 객체에 직렬화 시 무한 순환 참조 문제 발생 → DTO로 분리하거나 Jackson 설정 필요
  • 테스트나 디버깅 시 LazyInitializationException 발생 주의 (트랜잭션 밖에서 접근 시)

 

5. 성능 문제 예시: N+1 문제

Lazy 전략을 잘못 사용할 경우 다음과 같은 문제가 발생할 수 있습니다.

List<Post> posts = postRepository.findAll();
for (Post post : posts) {
    System.out.println(post.getComments().size());
}

위 코드는 Post 수만큼 추가 쿼리가 실행되는 N+1 쿼리 문제를 발생시킵니다. 해결하려면 join fetch 또는 @EntityGraph를 사용해야 합니다.

@Query("SELECT p FROM Post p JOIN FETCH p.comments")
List<Post> findAllWithComments();

 

결론

JPA를 사용할 때 Fetch 전략은 성능과 설계에 큰 영향을 미칩니다. 단순히 "편하니까 EAGER"로 가면, 장기적으로 시스템이 느려지고, 유지보수 난이도가 높아집니다. Lazy를 기본 전략으로 삼고, 필요한 곳만 명시적으로 조절하는 전략이 가장 안전합니다.