Spring/Spring JPA
FetchType 간단히
개발자잡
2024. 12. 27. 21:12
FetchType은 JPA에서 엔티티 간 연관 관계를 가져올 때 데이터를 **즉시 로딩(EAGER)**할지, **지연 로딩(LAZY)**할지를 설정하는 옵션입니다. 이 옵션은 성능 최적화와 관련이 깊으며, 연관된 엔티티를 로딩하는 시점을 결정합니다.
FetchType.EAGER
설명
- 즉시 로딩 방식으로, 엔티티를 조회할 때 연관된 엔티티도 즉시 함께 로드됩니다.
- 기본적으로 연관된 데이터에 항상 접근할 필요가 있는 경우 유용하지만, 불필요한 데이터를 로드하여 성능 저하를 유발할 수 있습니다.
- 예를 들어, 부모 엔티티를 조회할 때 자식 엔티티를 항상 사용해야 하는 경우 적합합니다.
예제
@Entity
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
private List<Child> children;
}
- 위 코드에서는 Parent 엔티티를 조회하면 children 컬렉션에 포함된 모든 Child 엔티티가 즉시 로드됩니다.
FetchType.LAZY
설명
- 지연 로딩 방식으로, 연관된 엔티티는 실제로 접근할 때(예: getter 호출) 데이터를 로드합니다.
- 기본적으로 연관된 데이터를 사용할 가능성이 낮거나, 데이터 로딩이 비용이 클 경우 적합합니다.
- 데이터를 가져오기 위해 프록시 객체를 생성하며, 필요할 때만 초기화됩니다.
예제
@Entity
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Child> children;
}
- Parent 엔티티를 조회할 때 children 컬렉션은 로드되지 않습니다. 대신 프록시 객체가 생성되고, 실제로 children에 접근하면 데이터가 로드됩니다.
FetchType 기본 설정
JPA는 연관 관계의 종류에 따라 기본 FetchType을 다르게 설정합니다:
- @OneToOne
- 기본: FetchType.EAGER
- 이유: 보통 단일 연관 관계는 즉시 로드하는 것이 성능상 부담이 적기 때문입니다.
- @ManyToOne
- 기본: FetchType.EAGER
- 이유: ManyToOne은 관계된 데이터를 대부분 자주 사용하는 경우가 많습니다.
- @OneToMany
- 기본: FetchType.LAZY
- 이유: OneToMany는 데이터량이 많을 수 있으므로 기본적으로 지연 로딩됩니다.
- @ManyToMany
- 기본: FetchType.LAZY
- 이유: ManyToMany는 연관된 데이터가 많아질 가능성이 크기 때문입니다.
EAGER와 LAZY의 비교
특징 | FetchType.EAGER | FetchType.LAZY |
로딩 시점 | 엔티티를 조회할 때 즉시 로딩 | 실제로 접근할 때 데이터 로딩 |
장점 | 연관 데이터를 즉시 사용할 수 있음 | 초기 로딩 비용이 줄어듬 |
단점 | 불필요한 데이터를 로드할 수 있어 성능 저하 | 데이터 접근 시점에서 추가 쿼리 발생 가능 |
사용 사례 | 연관 데이터를 항상 사용하는 경우 | 연관 데이터를 사용하지 않을 가능성이 큰 경우 |
EAGER와 LAZY를 선택하는 기준
- 연관 데이터를 항상 사용하는가?
- EAGER: 항상 사용한다면 성능 이점이 있음.
- LAZY: 드물게 사용한다면 불필요한 데이터 로드를 방지.
- 연관된 데이터가 많아질 가능성이 있는가?
- EAGER: 데이터가 적다면 문제가 없음.
- LAZY: 데이터가 많다면 성능 저하를 방지.
- 애플리케이션의 복잡성
- LAZY를 사용하면 추가 쿼리가 발생할 가능성이 있으므로, 복잡한 쿼리 로직에서는 주의가 필요.
EAGER와 관련된 주의 사항
1. N+1 문제
- FetchType.EAGER로 설정된 엔티티를 조회할 때 연관된 데이터에 대해 다수의 추가 쿼리가 실행될 수 있습니다.
- 예를 들어, 부모 엔티티를 조회하면서 자식 엔티티를 개별적으로 로드하는 경우 발생.
해결 방법:
- @EntityGraph 또는 JOIN FETCH를 사용하여 한 번의 쿼리로 데이터를 로드합니다.
@Query("SELECT p FROM Parent p JOIN FETCH p.children WHERE p.id = :id")
Parent findByIdWithChildren(@Param("id") Long id);
2. 불필요한 데이터 로드
- EAGER를 남용하면 메모리 사용량과 네트워크 트래픽이 증가할 수 있습니다.
결론
- 기본적으로 FetchType.LAZY를 사용하고, 반드시 필요한 경우에만 FetchType.EAGER를 사용하도록 설계하는 것이 좋습니다.
- 프로젝트 초기에는 지연 로딩(LAZY)을 설정한 뒤, 요구 사항과 성능 문제를 분석하며 필요한 곳에만 즉시 로딩(EAGER)을 추가하는 것이 좋습니다.