즉시 로딩과 지연 로딩

Posted by sJun on November 07, 2020 ·

즉시 로딩과 지연 로딩을 알기 위해서는 프록시에 대한 개념이 필요합니다.

프록시의 개념은 여기

지연 로딩과 즉시 로딩은 다음과 같이 사용합니다.

@ManyToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.EAGER)

@XXXToOne 코드를 까보시면 fetch Type 디폴트로 EAGER로 되있는 걸 볼 수 있습니다.

    /** 
     * (Optional) Whether the association should be lazily 
     * loaded or must be eagerly fetched. The EAGER
     * strategy is a requirement on the persistence provider runtime that 
     * the associated entity must be eagerly fetched. The LAZY 
     * strategy is a hint to the persistence provider runtime.
     */
    FetchType fetch() default EAGER;

@XXXToOne이 아닌 부분은 디폴트 값이 LAZY로 되있습니다.

fetch Type를 LAZY로 설정하면 연관된 객체를 프록시로 가져오게 됩니다. 즉 Member 클래스 내부에 다른 객체의 레퍼런스가 있다해도 해당 레퍼런스를 필요할 때만 가져올 수 있는 것이죠.

class Member{
    @Id @GeneratedValue
    private Long id;

    private String username;
    
    @ManyToOne
    private Team team;
    
    @ManyToOne
    private Circle circle;
}

정리하면 fetch Type가 즉시 로딩이면 Team과 Circle 객체를 사용하든 말든 한 번에 전부 불러오는 것이고 지연 로딩이면 코드에서 레퍼런스를 통해 호출할 때만 프록시 초기화를 통해서 원본 객체를 가져온다고 생각하시면 됩니다.


즉시로딩(EAGER)의 경우에는 치명적인 단점을 안고 있습니다. 보통 JPA에서 복잡한 쿼리를 풀기 위해서 JPQL을 사용하는 일이 빈번합니다.

List<Member> memberList = em.createQuery("select m from Member m", Member.class).getResultList();

다음과 같은 코드를 실행했을 때 단순히 Member만 불러오는 것이 아니라 Member 내부의 Team과 Circle 전부를 불러옵니다.

EAGER 전략은 객체를 가져올 때 일단 무조건 값이 다 들어가 있어야합니다.

JPA 입장에서는 “select m from Member m” 으로 Member를 조회했더니 내부에 즉시 로딩으로 설정된 Circle과 Team이 있네? 하면서 전부 가져와 버리는 겁니다.

이렇게 되면 정작 나는 Member만 사용하면 되는데 필요없는 부분까지 강제로 가져오기 때문에 불러오는 쿼리문의 양도 많아지고 그에 따라 성능에도 영향을 미치게 됩니다.

내가 Member를 불러올 때 무조건 다 불러와야해!

이런 것이 아니라면 기본적으로 @XXXToOne의 fetch 전략을 LAZY로 수정해주는 것이 옳습니다.