내일배움캠프/TIL

2024 08 23 TIL : 프록시, 지연로딩, 즉시로딩

동성만능크리너 2024. 8. 23. 16:03

객체는 객체 그래프로 연관된 객체들을 탐색한다. 그런데 객체가 데이터베이스에 저장되어 있으므로 연관된 객체를 마음껏 탐색하기는 어렵다. JPA 구현체들은 이 문제를 해결하려고 프록시라는 기술을 사용한다. 

 

프록시를 사용하면 연관된 객체를 처음부터 데이터베이스에서 조회하는 것이 아니라, 실제 사용하는 시점에 데이터베이스에서 조회(지연 로딩)할 수 있다. 하지만 자주 함께 사용되는 객체들은 조인을 사용해서 함께 조회하는 것이 효과적(즉시 로딩)이다. 

JPA는 즉시 로딩과 지연 로딩이라는 방법으로 둘을 모두 지원한다. 

 

지연 로딩

어떤 엔티티를 데이터베이스에서 조회할 때 연관된 엔티티들을 함께 조회하지 않고, 만약 그 연관된 엔티티가 실제 사용되는 시점까지 조회를 지연하는 방법. 

 

프록시 객체 

지연 로딩을 사용하기 위해 실제 엔티티 객체 대신에 데이터베이스 조회를 지연할 수 있는 가짜 객체가 필요한데, 이것을 프록시 객체라고 한다. 프록시 클래스는 실제 클래스를 상속받아서 만들어지므로 실제 클래스와 겉 모양이 같다. 그래서 사용하는 입장에서는 이것이 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다.

상속

 

프록시 객체는 실제 객체에 대한 참조를 보관한다. 그리고 프록시 객체의 메소드를 호출하면 실제 객체의 메소드를 호출한다. 

위임

 

getname()처럼 실제 사용될 때 데이터베이스를 조회해서 실제 엔티티 객체를 생성하는데 이것을 프록시 객체의 초기화라고 한다.

 

프록시 객체의 초기화

 

 

프록시의 특징 

  • 프록시 객체는 처음 사용할 때 한 번만 초기화된다. 
  • 프록시 객체를 초기화한다고 프록시 객체가 실제 엔티티로 바뀌는 것은 아니다. 프록시 객체가 초기화되면 프록시 객체를 통해서 실제 엔티티에 접근할 수 있다. 
  • 프록시 객체는 원본 엔티티를 상속받은 객체이므로 타입 체크 시에 주의해서 사용해야 한다. 
  • 영속성 컨텍스트에 이미 찾는 엔티티가 있으면 데이터베이스를 조회할 필요가 없으므로 프록시가 아닌 실제 엔티티를 반환한다. 
  • 초기화는 영속성 컨텍스트의 도움을 받아야 가능하기 때문에, 하이버네이트에서 준영속 상태의 프록시를 초기화하면 예외를 뱉어낸다.

 

즉시 로딩과 지연 로딩 다시 정리.

즉시 로딩 : 엔티티를 조회할 때 연관된 엔티티도 함께 조회한다. 

설정 방법 : @ManyToOne(fetch = FetchType.EAGER)

 

지연 로딩: : 연관된 엔티티를 실제 사용할 때 조회한다. 

설정 방법 : @ManyToOne(fetch = FetchType.LAZY)

 

JPA는 Null값을 고려해서 기본적으로 외부 조인을 사용한다. 하지만 외부 조인보다 내부 조인이 성능과 최적화에서 더 유리하다

내부 조인을 사용하려면 외래 키에 NOT NULL 제약 조건을 설정하면 값이 있는 것을 보장한다. 따라서 이 때는 내부 조인만 사용해도 된다. 그리고 JPA에도 이런 사실을 알려줘야 한다. @JoinColumn에 nullable = false를 설정해서 이 외래 키는 null 값을 허용하지 않는다고 알려주면 JPA는 외부 조인 대신에 내부 조인을 사용한다. 

 

JPA 기본 페치 전략

fetch 속성의 기본 설정값은 다음과 같다. 

@ManyToOne, @OneToOne : 즉시 로딩(FetchType.EAGER)

@OneToMany, @ManyToMany : 즉시 로딩(FetchType.LAZY)

 

하지만 일단 개발할 때 모든 연관관계에 지연 로딩을 사용하고, 개발이 완료 단계쯤에 왔을 때 사용 빈도를 보고 필요한 곳에만 즉시 로딩을 사용하도록 최적화하는 게 좋다고 한다.