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을 다르게 설정합니다:

  1. @OneToOne
    • 기본: FetchType.EAGER
    • 이유: 보통 단일 연관 관계는 즉시 로드하는 것이 성능상 부담이 적기 때문입니다.
  2. @ManyToOne
    • 기본: FetchType.EAGER
    • 이유: ManyToOne은 관계된 데이터를 대부분 자주 사용하는 경우가 많습니다.
  3. @OneToMany
    • 기본: FetchType.LAZY
    • 이유: OneToMany는 데이터량이 많을 수 있으므로 기본적으로 지연 로딩됩니다.
  4. @ManyToMany
    • 기본: FetchType.LAZY
    • 이유: ManyToMany는 연관된 데이터가 많아질 가능성이 크기 때문입니다.

 

EAGER와 LAZY의 비교

특징 FetchType.EAGER FetchType.LAZY
로딩 시점 엔티티를 조회할 때 즉시 로딩 실제로 접근할 때 데이터 로딩
장점 연관 데이터를 즉시 사용할 수 있음 초기 로딩 비용이 줄어듬
단점 불필요한 데이터를 로드할 수 있어 성능 저하 데이터 접근 시점에서 추가 쿼리 발생 가능
사용 사례 연관 데이터를 항상 사용하는 경우 연관 데이터를 사용하지 않을 가능성이 큰 경우

 

 

EAGER와 LAZY를 선택하는 기준

  1. 연관 데이터를 항상 사용하는가?
    • EAGER: 항상 사용한다면 성능 이점이 있음.
    • LAZY: 드물게 사용한다면 불필요한 데이터 로드를 방지.
  2. 연관된 데이터가 많아질 가능성이 있는가?
    • EAGER: 데이터가 적다면 문제가 없음.
    • LAZY: 데이터가 많다면 성능 저하를 방지.
  3. 애플리케이션의 복잡성
    • 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)을 추가하는 것이 좋습니다.