프록시(Proxy)와 지연로딩(Lazy Loading)

bagt13·2022년 8월 15일
1

JPA

목록 보기
8/13
post-thumbnail




프록시(Proxy)란?

특정 객체를 호출하려고 할 때 해당 객체에 직접 요청하는 것이 아닌, 중간에 Proxy 객체(가짜)를 두어 Proxy 객체가 대신 요청을 받아 실제 객체를 호출해 주도록 하는 것이다.


프록시 패턴을 쓰는 일반적인 이유는 접근을 제어하고 싶거나, 부가 기능을 추가하고 싶을 때 사용한다.







Proxy의 특징

프록시는 실제 객체를 상속받아 만들어지며, 사용자는 프록시 객체인지 실제 객체인지 구분하지 않고 사용할 수 있다.

프록시 객체는 실제 객체의 참조(target)를 보관하며, 프록시 객체는 실제 객체의 메서드를 호출한다.



  • 프록시 객체는 처음 사용할 때 한 번만 초기화된다.
  • 프록시 객체는 원본 엔티티를 상속받기 때문에, 타입 체크시 == 대신 instance of를 사용해야 한다.
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때 프록시를 초기화하면
    문제 발생한다.

    (하이버네이트가 org.hibernate.LazyInitializationException 예외를 터트린다)





지연 로딩(Lazy)과 즉시 로딩(Eager)


위에서 프록시는 일반적으로 접근 제어 또는 부가 기능 추가를 위해 사용한다고 했는데, 프록시는 JPA의 지연로딩 과정에서도 사용된다.


지연 로딩은 특정 엔티티에 매핑된 다른 엔티티를 사용하는 시점에 DB에 쿼리를 날려 가져오는 것이며, 즉시 로딩매핑된 모든 엔티티를 한번에 가져오는 방식이다.




예를 들어, Member 엔티티에 Team 엔티티가 매핑되어 있다고 가정해보자.

이때, Member member = em.find(Member.class, 1L); 로 Member 객체를 불러온 상황이다.

즉시 로딩의 경우, 위에서 언급했듯이 연관된 Team 엔티티까지 한번에 가져온다.




반면, 지연 로딩의 경우에는 Member 객체를 얻기위한 쿼리만 DB로 날린다. 그리고 가짜 프록시 Team 객체를 생성해둔다.

이후, 실제로 Team 객체가 사용되는 시점에 DB에서 가져와, 이 프록시 객체를 초기화시킨다.



위 내용에 따르면, Member 엔티티를 사용하는 대부분의 상황에서 Team 엔티티까지 함께 사용한다면, EAGER(즉시로딩)로 설정하여 쿼리를 한 번씩 날리는 것이 효율적이라고 생각할 수 있다.



하지만, 아래와 같은 이유로 가급적이면 지연 로딩만 사용하는 것이 권장된다.

  • 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생한다.
  • 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
  • @ManyToOne, @OneToOne기본이 즉시 로딩이기 때문에, LAZY로 따로 설정해야 한다.
    (@OneToMany, @ManyToMany기본이 지연 로딩)



하지만 매핑된 엔티티들을 같이 불러와야 할 경우가 분명 있을텐데, 그때는 JPQL fetch join이나, 엔티티 그래프 기능을 사용하는 것이 좋다.







자료 출처 : 김영한님의 자바 ORM 표준 JPA 기본편


profile
주니어 백엔드 개발자입니다😄

0개의 댓글