반응형
JPA를 처음 공부할 때 가장 많이 헷갈리는 개념 중 하나가 fetchType.EAGER와 fetchType.LAZY다.
“언제 데이터를 가져온다”는 말은 많이 들어봤지만, 막상 코드에서 어떤 차이가 있고, 왜 실무에서는 특정 방식을 피하라고 하는지 이해하기 어렵다.
이 글에서는 FetchType의 역할과 EAGER / LAZY의 차이를 JPA 내부 동작 기준으로 정리한다.
Fetch Type이란?
Fetch Type은 JPA에서 제공하는 enum으로, 연관 엔티티를 언제 로딩할지에 대한 기본 전략을 의미한다. EAGER와 LAZY는 그 전략의 값이다.
public enum FetchType {
LAZY,
EAGER
}
FetchType은 쿼리 방식이 아니다
중요한 오해부터 정리해야 한다.
FetchType은 SQL을 어떻게 실행할지를 정하는 옵션이 아니다.
엔티티 매핑 단계에서 "연관 엔티티를 언제 가져올 것인가"에 대한 기본 정책일 뿐이다. 즉, 조회 시점의 쿼리 구조를 직접 제어하지 않는다.
FetchType.LAZY (지연 로딩)
개념
- 연관 엔티티를 즉시 조회하지 않는다
- 실제로 접근하는 시점에 DB에서 조회한다
- 처음에는 프록시 객체로 대체된다
@ManyToOne(fetch = FetchType.LAZY)
private Team team;
member.getTeam(); // 이 시점에 SELECT 실행
내부 동작 관점
- 엔티티 조회 시점에는 연관 엔티티 데이터 없음
- 접근 시점에 메모리가 없으면 DB 조회
특징
- 불필요한 데이터 로딩 방지
- 메모리 사용량 감소
- 연관 엔티티 접근이 많으면 쿼리가 늘어날 수 있음
FetchType.EAGER (즉시 로딩)
개념
- 엔티티를 조회할 때 연관 엔티티도 즉시 로딩
@ManyToOne(fetch = FetchType.EAGER)
private Team team;
EAGER는 '즉시 로딩하라'는 정책일 뿐, 어떤 SQL을 사용할지는 JPA 구현체인 Hibernate가 결정한다.
즉, JOIN으로 가져올 수도 있고, 추가 SELECT를 여러 번 실행할 수도 있다. 결과적으로는 쿼리 수가 예측 불가해지고, 성능 문제 발생 시 원인 파악이 어려우며, 연관관계가 늘어날수록 위험이 증가한다.
그래서 접근 시점에 로딩하는 거랑 즉시 로딩하는 거랑 뭔 차이임?
즉시 로딩 → 엔티티(직접 조회하라고 요청한 객체)를 가져오라고 한 순간, 연관 데이터(그 엔티티가 참조하고 있는 다른 엔티티)도 같이 가져옴
지연 로딩 → 엔티티만 가져오고, 연관 데이터는 실제로 쓰겠다고 요청할 때 가져옴