본문 바로가기
JPA

[JPA] 프록시와 지연로딩

by 당코 2023. 3. 5.

프록시

프록시는 실제 객체를 참조하는 가짜 객체이다.
실제 클래스를 상속받아서 만들어져 실제 클래스와 겉모양이 같다.
프록시 객체는 실제 객체의 참조(target)를 보관하기 때문에 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.

 
jpa에서 프록시 엔티티 객체를 호출하는 메서드는 em.getReference()이다.
em.find()는 데이터베이스를 통해서 실제 엔티티 객체를 조회하지만
em.getReference()는 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체를 조회한다.

 
 

프록시 객체의 초기화

  1. 프록시 객체에 getName() 같은 메서드를 사용하게 되면
  2. 프록시 객체는 영속성 컨텍스트에 초기화 요청을 한다.
  3. 영속성 컨텍스트에서 실제 DB를 조회하여
  4. 실제 Entity를 생성한다.
  5. 프록시 객체는 생성된 실제 Entity의 getName()을 호출하여 결과를 알려준다. 

 

프록시의 특징

  • 프록시 객체는 처음 사용할 때 한 번만 초기화한다.
  • 프록시 객체를 초기화할 때, 프록시 객체가 실제 엔티티로 바뀌는 것은 아니다. 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근 가능하다.
  • 프록시 객체는 원본 엔티티를 상속받기 때문에 타입 체크 시 주의해야 한다. (== 비 교 실패, 대신 instance of 사용)
  • 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 em.getReference()를 호출해 도 실제 엔티티 반환한다.
  • 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생한다. (하이버네이트는 org.hibernate.LazyInitializationException 예외를 터트림)

 

지연로딩

Member와 Team이 다대일로 연관관계가 있는 경우 Member를 조회할 때 Team을 같이 조회해야 할까?
단순히 Member의 정보만 출력해야 하는 경우 Team의 정보는 필요가 없을 것이다.
이때 사용하는 방법이 지연로딩이다.
실제 Team 객체를 DB에서 가져오는 대신 프록시를 통해 필요할 때 조회하도록 하는 방법이다.

@Entity
public class Member {
	@Id
	@GeneratedValue
	private Long id;
    
	@Column(name = "USERNAME")
	private String name;
    
	@ManyToOne(fetch = FetchType.LAZY) //**
	@JoinColumn(name = "TEAM_ID")
	private Team team;
	..
 }

@ManyToOne에 FetchType을 LAZTY로 설정해 주면 지연로딩으로 동작한다.
 
코드를 통해 동작 방식을 살펴보겠다.
em.find를 통해 Member 엔티티를 찾아올 경우 Team은 지연로딩이 적용되어 프록시가 생성되게 된다.

 
get.Team()을 통해 실제 Team을 사용하게 되는 시점에 프록시가 초기화되어 DB를 조회하게 된다.

 
 

즉시 로딩

즉시 로딩은 엔티티를 조회할 때 내부의 연관관계가 있는 모든 것들을 즉시 조회한다.
Member를 조회할 경우 Team도 같이 조회하는데 이때 프록시를 사용하지 않고 실제 객체를 DB에서 가져오게 된다.

@Entity
public class Member {
	@Id
	@GeneratedValue
	private Long id;
    
	@Column(name = "USERNAME")
	private String name;
    
	@ManyToOne(fetch = FetchType.EAGER) //**
	@JoinColumn(name = "TEAM_ID")
	private Team team;
	..
 }

@ManyToOne에 FetchType을 Eager로 설정하면 즉시로딩으로 동작한다.
 
즉시로딩에는 여러 가지 취약점이 있다.
즉시 로딩을 적용하면 예상하지 못한 SQL이 발생할 수 있다.
또한 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
 

결론

가급적 지연로딩을 사용하자.
@ManyToOne, @OneToOne은 기본이 즉시로딩으로 설정돼 있어 지연로딩으로 바꿔줘야 한다.
@OneToMany, @ManyToMany는 기본이 지연로딩으로 설정되어 있다.
 
 
출처 : https://www.inflearn.com/course/ORM-JPA-Basic/dashboard