JPA를 사용하는 백엔드 개발자라면 누구나 한 번쯤은 예상치 못한 쿼리 폭발 또는 무한 참조를 경험했을 겁니다. 대부분의 원인은 연관관계 설정 시 Fetch 전략을 정확히 이해하지 못한 데서 비롯됩니다.
FetchType.LAZY와 FetchType.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를 기본 전략으로 삼고, 필요한 곳만 명시적으로 조절하는 전략이 가장 안전합니다.
'개발 > JAVA' 카테고리의 다른 글
| [JAVA] Spring에서 트랜잭션 관리하기 – @Transactional 완벽 이해 (0) | 2025.10.16 |
|---|---|
| [JAVA] N+1 문제와 해결 방법 – Fetch Join과 @EntityGraph로 해결하기 (0) | 2025.10.15 |
| [JAVA] Entity 연관관계 완전 정복 – 1:N, N:M, Cascade까지 실무 중심 정리 (0) | 2025.10.13 |
| [JAVA] JPQL과 @Query 사용법 – Spring Data JPA에서 복잡한 쿼리 다루기 (0) | 2025.10.12 |
| [JAVA] JPA Repository 메서드 쿼리 작성법 – 실무 중심 가이드 (0) | 2025.10.11 |
