FetchType은 JPA (Java Persistence API)에서 엔티티와 연관된 데이터들을 어떻게 로딩할지를 결정하는 전략을 정의하는 옵션입니다. JPA에서는 연관된 엔티티를 로딩할 때 두 가지 전략, EAGER 로딩과 LAZY 로딩을 제공합니다.
이 두 가지 전략은 연관 관계의 데이터를 언제 가져올지를 결정하는데, 성능 최적화와 메모리 사용량에 중요한 영향을 미칩니다. 각각의 동작 방식과 장단점을 이해하는 것이 효과적인 JPA 활용의 핵심입니다.
Eager 로딩 (즉시 로딩)
Eager 로딩은 연관된 엔티티를 즉시 로딩하는 방식입니다. 즉, 연관된 엔티티가 EAGER로 설정되어 있으면, 해당 엔티티가 조회될 때 관련된 모든 엔티티가 함께 조회됩니다.
• 예를 들어, User와 Profile이라는 두 개의 엔티티가 있고 User 엔티티의 Profile이 EAGER 로딩으로 설정되어 있다면, User를 조회할 때마다 해당 User의 Profile도 함께 조회됩니다.
Eager 로딩의 예
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Eager 로딩: User가 조회될 때 Profile도 즉시 로딩됨
@OneToOne(fetch = FetchType.EAGER)
private Profile profile;
}
Eager 로딩 동작 방식
• EAGER로 설정된 연관 관계를 가진 엔티티를 조회할 때, JPA는 해당 엔티티를 포함한 모든 연관된 엔티티를 한 번의 쿼리로 가져오거나(JOIN), 여러 쿼리를 동시에 실행하여 데이터를 가져옵니다(N+1 문제 발생 가능).
• 예를 들어, User 엔티티가 EAGER로딩으로 설정된 Profile 엔티티를 가질 경우, User를 조회할 때 JPA는 JOIN 쿼리를 사용하여 Profile 데이터도 함께 가져오게 됩니다.
Eager 로딩의 장단점
• 장점:
• 관련 엔티티가 항상 필요할 때, 즉시 데이터를 사용할 수 있어 편리합니다.
• 쿼리 작성 시 별도로 연관 데이터를 가져오는 쿼리를 작성할 필요가 없습니다.
• 단점:
• 필요하지 않은 데이터까지 불필요하게 조회할 수 있어 성능이 저하될 수 있습니다.
• 특히, N+1 문제가 발생할 수 있습니다. N+1 문제란, 연관된 엔티티가 여러 개 있을 때 JPA가 각 연관 엔티티를 각각 조회하여, 처음의 1개의 쿼리와 함께 N개의 추가 쿼리를 실행하게 되는 문제입니다.
• 복잡한 연관 관계일수록 불필요한 데이터 로딩이 많아져 메모리 사용량이 증가할 수 있습니다.
Lazy 로딩 (지연 로딩)
Lazy 로딩은 연관된 엔티티를 실제로 사용할 때까지 로딩을 지연시키는 방식입니다. 즉, 연관된 엔티티가 LAZY로 설정되어 있으면, 해당 엔티티를 참조하는 시점에 데이터를 가져옵니다. 초기에는 프록시 객체로 조회된 엔티티가 대체되고, 실제로 접근할 때 데이터베이스 쿼리가 발생합니다.
• 예를 들어, User 엔티티의 Profile이 LAZY 로딩으로 설정되어 있으면, User를 조회할 때 Profile은 실제로 조회되지 않고, Profile 객체에 접근하는 순간에 데이터베이스 쿼리가 발생하여 Profile을 로드하게 됩니다.
Lazy 로딩의 예
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Lazy 로딩: Profile 객체에 접근하는 순간 데이터베이스에서 로딩됨
@OneToOne(fetch = FetchType.LAZY)
private Profile profile;
}
Lazy 로딩 동작 방식
• LAZY로 설정된 연관 관계를 가진 엔티티를 조회할 때, JPA는 프록시 객체를 반환합니다. 이 프록시 객체는 실제 데이터베이스 쿼리를 수행하지 않고, 해당 엔티티의 필드에 접근할 때만 데이터베이스 쿼리를 실행하여 데이터를 가져옵니다.
• 예를 들어, User 엔티티가 LAZY로딩으로 설정된 Profile 엔티티를 가질 경우, User 조회 시 Profile 데이터는 조회되지 않고, user.getProfile()과 같은 메서드를 호출하여 Profile 객체에 접근할 때 데이터베이스 쿼리가 발생합니다.
Lazy 로딩의 장단점
• 장점:
• 필요한 경우에만 연관된 데이터를 조회하므로 메모리 사용량을 줄이고 성능을 최적화할 수 있습니다.
• 연관된 엔티티가 많을 경우, 필요한 데이터만 로딩하므로 불필요한 쿼리 실행을 방지할 수 있습니다.
• 단점:
• LazyInitializationException 발생 가능: 엔티티 매니저(EntityManager)가 닫힌 상태에서 LAZY로딩된 엔티티에 접근하려고 할 때 LazyInitializationException이 발생할 수 있습니다. 이는 주로 엔티티를 조회한 이후 트랜잭션이 종료된 상태에서 연관된 엔티티를 사용하려고 할 때 발생합니다.
• 복잡한 연관 관계에서는 지연 로딩 시점에 많은 쿼리가 실행되어 성능 저하가 발생할 수 있습니다.
Eager 로딩과 Lazy 로딩의 사용 예
EX) User와 Profile이라는 두 엔티티가 있고, User는 Profile과 1:1 관계를 가지는 상황.
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToOne(fetch = FetchType.LAZY) // 또는 FetchType.EAGER
private Profile profile;
}
@Entity
public class Profile {
@Id
@GeneratedValue
private Long id;
private String phoneNumber;
private String address;
}
FetchType.LAZY로 설정된 경우
User user = entityManager.find(User.class, 1L);
System.out.println(user.getName()); // User 조회 (쿼리 발생)
System.out.println(user.getProfile().getPhoneNumber()); // 이 시점에 Profile 조회 (쿼리 발생)
FetchType.EAGER로 설정된 경우
User user = entityManager.find(User.class, 1L); // User와 Profile이 함께 조회됨 (JOIN 쿼리 발생)
System.out.println(user.getName());
System.out.println(user.getProfile().getPhoneNumber()); // 이미 Profile이 로딩된 상태
언제 Eager와 Lazy를 사용할까?
1. Eager 로딩을 사용할 때:
• 연관된 데이터를 항상 필요로 하는 경우.
• 조회 성능이 중요한 상황에서 N+1 문제를 방지하기 위해.
2. Lazy 로딩을 사용할 때:
• 연관된 데이터를 자주 사용하지 않거나, 특정 조건에서만 필요한 경우.
• 메모리 사용량과 성능 최적화가 중요한 경우.
Default Fetch Type
Mapping | Default Fetch Type |
@OneToOne | FetchType.EAGER |
@OneToMany | FetchType.LAZY |
@ManyToOne | FetchType.EAGER |
@ManyToMany | FetchType.LAZY |
'Spring' 카테고리의 다른 글
Transactional 관심사의 분리를 통해 로그 기록 남기기 (0) | 2024.10.08 |
---|---|
단위테스트, 통합테스트 (1) | 2024.10.01 |
Error 메시지(enum 활용) (3) | 2024.09.13 |
Service 테스트 코드, controller 테스트 코드 (0) | 2024.09.13 |
AOP(Aspect-Oriented Programming) (0) | 2024.09.12 |