어 나 갱수.

[JPA] 즉시로딩과 지연로딩 🐥 본문

JPA

[JPA] 즉시로딩과 지연로딩 🐥

김경수 2024. 2. 1. 16:25
728x90

오늘은 즉시 로딩과 지연 로딩에 대해 정리해 보겠습니다. 각각 어떤 상황에서 사용해야 하고 왜 사용해야 하는지 알아보겠습니다.

 

JPA는 데이터를 조회할 때, FetchType으로 즉시로딩(Eager)과 지연로딩(Lazy)가 있습니다.

Fetch Type이란 ?

Fetch Type이란, JPA에서 하나의 Entity를 조회할 때, 연관관계에 있는 객체들을 어떻게 가져올 것이냐를 나타내는 설정값입니다.

  • JPA는 ORM기술로, 사용자가 직접 쿼리문으로 쿼리를 생성하지 않고, JPA에서 JPQL을 이용해서 쿼리를 생성해 줍니다.
  • JPA에서 Entity의 필드와 객체를 보고 쿼리문을 생성합니다.
  • 따라서 Entity에 연관관계 매핑이 되어있으면 그 Entity들까지 함께 조회하게 되는데, 그 매핑되어 있는 Entity를 어떻게 가져올 것인가에 대한 설정입니다.

각 연관관계의 default 속성은 다음과 같습니다.

~One으로 끝나는 연관관계는 EAGER속성이 defalut이고 ~Many로 끝나는 연관관계는 LAZY속성이 default입니다.

  • @ManyToOne : EAGER
  • @OneToOne : EAGER
  • @ManyToMany : LAZY
  • @OneToMany : Lazy

즉시로딩 (Eager)

즉시로딩이란 Entity를 조회할 때 매핑되어있는 다른 Entity들을 한 번에 불러오는 것을 말합니다.

 

아래에는 Member 엔티티와 Team엔티티가 N:1 매핑으로 관계를 맺고 있습니다. 

Member 엔티티에서 FetchType을 EAGER로 설정하였습니다.

@Entity
public class Member {
    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}
@Entity
public class Team {

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

    private String name;
    
}

 

JPQL로 Member를 조회해 보겠습니다.

Member findMember = em.createQuery("select m from Member m", Member.class).getSingleResult();

 

위와 같이 JPQL로 member를 조회하는 쿼리문을 작성하면 

// 멤버를 조회하는 쿼리
select
    member0_.id as id1_0,
    member0_.team_id as team_id3_0_,
    member0_.username as username2_.0_
from
    Member member0_
    
// 팀을 조회하는 쿼리
select
    team0_.id as id1_3_0_,
    team0_.name as name2_3_0
from
    Team team0_
where
    team0_.id=?

 

다음과 같이 FetchType을 EAGER로 설정하면 Member를 조회할 때 Team 엔티티도 함께 조회해서 쿼리가 같이 발생하는 것을 볼 수 있습니다.

 

지연로딩 (Lazy)

지연로딩이란, 필요한 시점에 연관된 객체의 데이터를 불러오는 것을 말합니다.

 

위의 예제에서 Fetch Type만 바꿔서 실행해 보겠습니다.

 

@ManyToONe(fetch = FetchType.Lazy)


JPQL을 사용해서 똑같이 Member 객체를 조회해 보겠습니다.

Member findMember = em.createQuery("select m from Member m", Member.class).getSingleResult();
// Team을 조회하는 쿼리는 발생하지 않음!
select
    member0_.id as id1_0,
    member0_.team_id as team_id3_0_,
    member0_.username as username2_.0_
from
    Member member0_

위에 쿼리문을 보면 Team을 조회하는 쿼리문은 발생하지 않았습니다.

 

 

정리

정리하면 즉시로딩은 연관된 데이터를 모두 가져온다는 장점이 있지만, 실무에서 엔티티 간의 관계가 복잡해질수록 조인으로 인한 성능 저하를 피할 수 없고 JPQL에서 발생하는 N+1을 일으킵니다.

 

즉시로딩은 불필요한 조인까지 포함해 처리하는 경우가 많아서 가급적 `지연 로딩`을 권장합니다.

728x90