좋은 질문입니다.
사실 JPA가 프록시 객체를 사용하는 이유는 단 하나로 요약할 수 있습니다.
"당장 필요 없는 데이터를 나중에 가져오기 위해서"
입니다.
만약 프록시가 없다면?
예를 들어:
@Entity
public class Order {
@ManyToOne
private Member member;
}
주문을 조회합니다.
Order order = em.find(Order.class, 1L);
그러면 Order 객체 안에는 Member가 있어야 합니다.
프록시가 없는 세상에서는:
Order 조회
↓
Member도 조회
해야 합니다.
즉 SQL이:
SELECT *
FROM ORDERS
WHERE ID = 1
뿐만 아니라
SELECT *
FROM MEMBER
WHERE ID = ?
도 같이 실행됩니다.
그런데 생각해보면
Order order = em.find(Order.class, 1L);
한 뒤
order.getStatus();
만 사용할 수도 있습니다.
이 경우에는
Member 정보는 전혀 안 썼는데
Member 조회 SQL까지 실행됨
이 됩니다.
비효율적이죠.
프록시가 있으면?
Hibernate는 실제 Member 대신
Member proxy;
를 넣어둡니다.
즉:
Order order = em.find(Order.class, 1L);
시점에는
SELECT *
FROM ORDERS
WHERE ID = 1
만 실행
그리고
order.getMember().getName();
을 호출하는 순간
SELECT *
FROM MEMBER
WHERE ID = ?
실행
즉:
진짜 필요할 때 조회
가 가능해집니다.
그런데 왜 null을 넣지 않고 프록시를 넣는 거야?
여기서 핵심이 나옵니다.
예를 들어:
Member member = order.getMember();
만약 Member를 아직 조회 안 했다고 해서
null
을 반환하면
이후
member.getName();
실행 시
NullPointerException
발생
또는
if(member != null)
같은 체크를 개발자가 계속 해야 합니다.
그래서 Hibernate는
Member member = order.getMember();
를 해도
null
이 아니라
MemberProxy
를 반환합니다.
즉 사용자 입장에서는
Member member = order.getMember();
가
실제 객체인지
프록시인지
신경 쓸 필요가 없습니다.
프록시의 역할
프록시는 사실상
class MemberProxy extends Member
같은 객체입니다.
내부적으로:
public String getName() {
if (실제 Member 없음) {
DB 조회
}
return realMember.getName();
}
처럼 동작합니다.
즉:
겉모습은 Member
실제론 DB 조회 대행자
입니다.
JPA가 원하는 목표
JPA는 객체지향 개발을 추구합니다.
그래서 개발자가
Order order = ...
Member member = order.getMember();
를 사용할 때
DB에서 읽어왔는가?
메모리에 있는가?
프록시인가?
를 신경 쓰지 않게 만들고 싶습니다.
프록시가 없다면
개발자는
if(memberLoaded)
같은 코드를 계속 작성해야 할 것입니다.
비유
프록시는 호텔의 "객실 카드키"와 비슷합니다.
카드키 받음
↓
아직 방 안에 들어가지 않음
↓
문을 여는 순간 실제 방 사용
실제 방(Member)은 아직 안 열었지만
카드키(Proxy)는 이미 손에 들고 있는 상태입니다.
결론
JPA가 실제 객체 대신 프록시 객체를 반환하는 이유는 지연 로딩을 구현하여 필요할 때만 데이터를 조회하기 위해서입니다. 프록시는 실제 객체와 동일한 타입으로 동작하므로 개발자는 객체를 사용하는 방식 그대로 코드를 작성할 수 있고, JPA는 내부적으로 필요한 순간에만 DB 조회를 수행할 수 있습니다. 이는 성능 최적화와 객체지향적인 사용성을 동시에 얻기 위한 설계입니다.
질문:
JPA에서 프록시 객체가 필요한 주된 이유가 지연로딩이야?
답:
네. 가장 중요한 이유는 지연 로딩을 구현하기 위해서입니다. Hibernate는 실제 엔티티 대신 프록시 객체를 반환해 필요한 시점까지 DB 조회를 미룰 수 있습니다. 추가적으로 getReference()를 통한 조회 최적화나 연관관계 설정 시 불필요한 SELECT 방지에도 사용되지만, 이러한 기능들도 결국 "실제 엔티티 조회를 나중으로 미룬다"는 프록시의 본질에서 파생된 것입니다.
'Spring&IntelliJ' 카테고리의 다른 글
| cascade = CascadeType.ALL은 어떨때 주로 쓰이고 왜 쓰여? (0) | 2026.06.16 |
|---|---|
| h2 파일 생성 실패시, Database not found 애러 해결방법 (0) | 2026.06.05 |
| 1. 쓰기 지연과 지연 로딩에 대해서, 2.로우(ROW)락과 쓰기지연에 대해서 (0) | 2026.06.02 |
| JPA 2-1강 JPA 내부 동작 방식, JPA 2-2강 영속성 컨텍스트의 이해(1), JPA 2-3강 영속성 컨텍스트의 이해(2) (0) | 2026.05.31 |
| 1-1강 영속성의 이해, 1-2강 객체의 세상과 테이블의 세상 (0) | 2026.05.27 |