JAVA/JPA

JPA Fetch 전략 (EAGER vs LAZY)

algml0703 2026. 1. 13. 19:42
반응형
JPA의 FetchType은 연관 엔티티를 언제 DB에서 불러올지를 결정합니다. 잘못 선택하면 불필요한 쿼리가 폭발적으로 늘어나거나, 트랜잭션 바깥에서 예외가 터집니다. 실무에서는 기본적으로 LAZY를 사용하고, 필요한 경우에만 fetch join으로 함께 가져오는 것을 권장합니다.

EAGER vs LAZY 한눈에 보기

구분EAGERLAZY
로딩 시점엔티티 조회 즉시연관 엔티티에 실제 접근할 때
쿼리 예측어려움개발자가 제어 가능
대량 조회위험 (N+1 문제)안전
실무 권장❌✅
로딩 시점DB SELECT 쿼리가 실행되는 시점입니다. EAGER는 엔티티 조회 즉시 연관 엔티티도 함께 SELECT하고, LAZY는 본인 엔티티만 조회한 뒤 연관 엔티티에 접근할 때 SELECT합니다.

EAGER

엔티티를 조회할 때 연관된 엔티티를 모두 즉시 함께 조회하는 방식입니다. Spring Data JPA에서 @ManyToOne, @OneToOne의 기본값이 EAGER입니다.

아래처럼 Settlement에 Member가 EAGER로 매핑되어 있으면, Settlement를 조회하는 순간 Member도 연쇄 조회됩니다.

@ManyToOne(fetch = FetchType.EAGER)
private Member member;
// Settlement 조회 시 Member도 함께 SELECT
Settlement settlement = settlementRepository.findById(id);

발생 가능한 문제

  • 필요하지 않은 데이터까지 가져와 성능·메모리·DB 자원 낭비
  • 어떤 쿼리가 나갈지 예측하기 어려움
  • 목록 조회처럼 대량 건수를 처리할 때 N+1 문제 발생
  • JOIN을 개발자가 직접 제어할 수 없어 쿼리 튜닝이 사실상 불가능

LAZY

연관 엔티티를 조회 시점이 아니라, 실제로 접근하는 시점에 DB에서 불러오는 방식입니다. 처음에는 진짜 엔티티 대신 프록시 객체가 자리를 차지하고 있다가, getMember()처럼 해당 필드에 접근하는 순간 SELECT 쿼리가 나갑니다.

@ManyToOne(fetch = FetchType.LAZY)
private Member member;
Settlement settlement = repository.findById(id);
// ↑ SQL 1회 — Settlement만 조회, member는 프록시
//   Settlement
//    └── member → MemberProxy

settlement.getMember().getName();
// ↑ SQL 1회 추가 — 이 줄에서 Member SELECT 실행

주의 — LazyInitializationException

LAZY 사용 시 가장 흔하게 마주치는 예외입니다. 트랜잭션이 종료된 뒤, 즉 DB 세션이 닫힌 상태에서 프록시 객체에 접근하면 발생합니다.

Settlement settlement = repository.findById(id);
// 트랜잭션 종료

settlement.getMember().getName();
// ❌ LazyInitializationException 발생
원인 LAZY로 설정된 연관 엔티티는 프록시 상태로 남아 있다가, 실제 데이터가 필요할 때 DB 세션을 통해 초기화됩니다. 트랜잭션이 이미 닫혀 세션이 없으면 초기화가 불가능해 예외가 발생합니다.

해결 — @Transactional로 세션 유지

연관 엔티티 접근이 포함된 전체 로직을 하나의 트랜잭션 안에서 처리하면 됩니다. @Transactional을 붙인 서비스 메서드 내에서는 세션이 열려 있으므로 프록시 초기화가 정상적으로 이루어집니다.

@Transactional
public SettlementDto getSettlement(...) {
    Settlement s = repository.findById(id);
    return new SettlementDto(s.getMember().getName());
    // 트랜잭션 안이므로 getMember() 접근 시 정상 초기화
}
실무 권장 서비스 레이어 메서드에 @Transactional을 적용하는 것이 기본입니다. 연관 데이터를 항상 함께 가져와야 하는 경우라면, LAZY를 유지하면서 JPQL의 fetch join이나 @EntityGraph를 사용해 필요한 시점에만 즉시 로딩하는 방식을 권장합니다.
반응형