SPRING

[SPRING] JPA Proxy

도원좀비 2025. 4. 21. 20:22

1️⃣ JPA 프록시(Proxy)란?

프록시란 Hibernate가 엔티티 대신 생성한 가짜 객체
실제 DB 조회는 지연시키고, 필요한 순간에만 쿼리를 실행하기 위해 사용

@ManyToOne(fetch = FetchType.LAZY)
private Member member;

 

  • 위와 같이 LAZY 전략을 사용하면, 실제 Member 객체가 아닌 프록시 객체가 주입
  • 프록시는 실제 객체의 서브 클래스로 만들어지며, 초기화 전까지는 데이터가 빔

2️⃣ 프록시는 언제 사용되는가?

상황 동작
@ManyToOne(fetch = LAZY) 프록시 객체로 감쌈
getName() 등 속성 접근 그 순간에 쿼리 실행 (초기화)
transaction 외부 접근 LazyInitializationException 발생

 

3️⃣ 실제 동작 예제

Order order = orderRepository.findById(1L).get();
Member member = order.getMember(); // 프록시 상태

System.out.println(member.getClass()); // 프록시 클래스 출력
System.out.println(member.getName()); // 이때 쿼리 실행

4️⃣ 프록시 객체 확인 및 초기화 여부 체크

Hibernate.isInitialized(member); // 초기화 여부 확인
em.getEntityManagerFactory()
  .getPersistenceUnitUtil()
  .isLoaded(member);

5️⃣ 프록시 주의사항

== 연산자 사용 금지

order.getMember() == member; // false일 수 있음

 

instanceof도 조심

member instanceof Member; // false일 수 있음

 

equals() 오버라이딩 시 주의

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
    ...
}

 

트랜잭션 밖에서 접근 금지

  • Lazy 속성은 트랜잭션 범위 내에서만 초기화 가능
  • 그렇지 않으면 LazyInitializationException 발생

6️⃣ 프록시 회피 방법

방법 1: fetch = EAGER (추천 ❌ – 성능 저하 가능성)

방법 2: JPQL JOIN FETCH 사용

@Query("SELECT o FROM Order o JOIN FETCH o.member")
List<Order> findAllWithMember();
  • 이 방법은 연관된 엔티티를 한 번에 불러와 프록시 사용 자체를 피할 수 있음

7️⃣ 팁 요약

  • 엔티티 간 연관관계는 LAZY로 설정하되, 필요한 경우 JOIN FETCH로 해결
  • 프록시 객체를 사용할 땐 Hibernate.getClass() 또는 PersistenceUnitUtil.isLoaded()로 안전하게 다룰 것
  • 서비스 로직은 항상 트랜잭션 안에서 실행되어야 안전함

'SPRING' 카테고리의 다른 글

[SPRING] 쿠키(Cookie)  (1) 2025.04.25
[SPRING] SSE(Sever-sent-event)  (1) 2025.04.25
[SPRING] 조인(JOINED) 전략  (2) 2025.04.19
[SPRING] 트랜잭션(Transaction)  (2) 2025.04.17
[SPRING] 인터셉터 AOP  (1) 2025.04.15