(Spring) 즉시 로딩(Eager Loading)과 지연 로딩(Lazy Loading) 배우기

  • by


JPA에서 데이터를 조회할 때 사용하는 방법인 즉시 로드 및 지연 로드에 대해 알아봅니다.

FetchType


FetchType은 단일 엔터티를 쿼리할 때 관련 엔터티를 데이터베이스에서 가져오는 방법을 결정하는 옵션입니다.

FetchType에는 두 가지 유형이 있습니다.

  • Eager
  • Lazy

@OneToOne과 @ManyToOne의 default FetchType은 Eager이며,

@ManyToOne 및 @ManyToMany의 기본 FetchType은 Lazy입니다.

즉시 로딩(Eager Loading)


즉각적인 로드는 한 엔티티를 조회할 때 모든 관련 엔티티를 한 번에 호출하는 것입니다.

  • ex) @ManyToOne (fetch = FetchType.EAGER)

(예)

회원과 리뷰에 관계를 생각해 봅시다.

한 멤버가 여러 리뷰를 남길 수 있기 때문에 멤버: 리뷰 관계는 1:N 관계입니다.

@Entity
@Getter
public class Member {

    @Id @GeneratedValue
    @Column(name = "memberId")
    private Long id;
    private String name;

    @JsonIgnore
    @OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
    private List<MemberReview> review = new ArrayList<>();
}
@Entity
@Getter
public class MemberReview {

    @Id @GeneratedValue
    @Column(name = "reviewId")
    private Long id;
    private String content;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "userId")
    private Member member;
}
MemberReview memberReview = em.find(MemberReview.class, reviewId);

리뷰 보기

select
    memberrevi0_.review_id as review_i1_7_0_,
    memberrevi0_.content as content2_7_0_,
    memberrevi0_.user_id as user_id3_7_0_,
    member1_.member_id as member_i1_6_1_,
    member1_.name as name2_6_1_ 
from
    member_review memberrevi0_ 
left outer join
    member member1_ 
        on memberrevi0_.user_id=member1_.member_id 
where
    memberrevi0_.review_id=?

검토를 조회할 때 검토와 연관된 구성원까지 데이터베이스에서 로드되는지 확인할 수 있습니다.

지연 로딩(Lazy Loading)


지연 로딩은 필요에 따라 관련 엔티티를 호출하는 것입니다.

  • ex) @MaynToOne (fetch = FetchType.LAZY)

위의 예에서 MemberReview의 FetchType만 LAZY로 변경되었습니다.

@Entity
@Getter
public class MemberReview {

    @Id @GeneratedValue
    @Column(name = "reviewId")
    private Long id;
    private String content;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userId")
    private Member member;
}
MemberReview memberReview = em.find(MemberReview.class, reviewId);
System.out.println(memberReview.getMember().getId());  //이때, 멤버 조회 쿼리 실행
System.out.println(memberReview.getMember().getName());
select
    memberrevi0_.review_id as review_i1_7_0_,
    memberrevi0_.content as content2_7_0_,
    memberrevi0_.user_id as user_id3_7_0_ 
from
    member_review memberrevi0_ 
where
    memberrevi0_.review_id=?
select
    member0_.member_id as member_i1_6_0_,
    member0_.name as name2_6_0_ 
from
    member member0_ 
where
    member0_.member_id=?

LAZY의 경우 멤버를 사용할 때 멤버 쿼리가 표시되는지 확인할 수 있습니다.

어느 것을 사용해야합니까?


실무에서는 지연 로딩(Lazy Loading)을 주로 사용합니다.

그 이유는 예를 들어 MemberReview와 연관된 엔티티가 하나가 아닌 N개라고 가정합니다.

즉시 로드하면 MemberReview 하나를 쿼리할 때마다 불필요한 다른 N개의 엔터티를 쿼리하는 쿼리 N개가 생성됩니다.

즉, 하나의 JPQL문에 대해 N+1개의 총 조회문이 실행됩니다.

이것을 N+1 문제라고 합니다.

이 경우 즉시 로딩은 성능이 매우 저하되므로 가능한 한 실무에서는 지연 로딩을 사용하는 것이 좋습니다!

참고 자료


JPA는 왜 지연 로딩을 사용합니까?

JPA JPA에서 테이블 간의 연관은 오브젝트 참조를 통해 이루어집니다.

서비스가 클수록 참조하는 개체가 많아지고 개체가 갖는 데이터의 양이 많아집니다.

이렇게 개체가 커질수록 DB에서

velog.io