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 |