1. 프록시 객체(Proxy Object)
정의
- 프록시 객체는 원래 객체를 대신하여 동작하는 대리 객체이며, 원래 객체에 대한 간접적인 접근을 제공하고, 원래 객체의 작업을 지연시키거나 추가적인 처리를 수행할 수 있음
- 객체의 실제 동작을 가리키는 참조(reference) 역할을 함
특징
- 프록시 객체는 실제 객체와 동일한 인터페이스(또는 구조)를 가짐
- 프록시 객체를 통해 실제 객체를 호출하거나, 호출 전에 추가 작업(로깅, 인증, 지연 초기화 등)을 수행할 수 있음
예시: JPA에서의 프록시 객체
JPA에서는 데이터베이스 엔티티를 실제로 가져오기 전에 프록시 객체를 생성하여 사용함
@Entity public class User { @Id @GeneratedValue private Long id; private String name; }
User user = entityManager.getReference(User.class, 1L);
- getReference() 메서드를 호출하면 User 엔티티의 실제 객체 대신 프록시 객체가 반환됨
- 프록시 객체는 실제 데이터베이스 쿼리를 실행하지 않고, 필요할 때(예: user.getName()호출 시) 실제 데이터를 가져옴
프록시 객체의 주요 역할
- 지연 로딩(Lazy Loading):
- 프록시 객체는 데이터베이스에서 엔티티를 즉시 가져오지 않고, 엔티티의 속성이 접근되는 순간에 데이터베이스 쿼리를 실행 -> 불필요한 데이터 로드를 방지하고 성능을 최적화
- 데이터베이스와의 연결 유지:
- 프록시 객체는 데이터베이스 세션(EntityManager)과 연결을 유지하여 필요한 시점에 데이터를 로드
- 경량 객체 생성:
- 프록시 객체는 실제 데이터를 포함하지 않고, 최소한의 정보를 유지 -> 메모리 사용을 줄이는 데 도움을 줌
프록시 객체의 단점
- LazyInitializationException:
- 프록시 객체를 사용할 때, 영속성 컨텍스트(EntityManager)가 닫혀 있으면 데이터 로드에 실패함
- 타입 문제:
- 프록시 객체는 실제 객체가 아닌 서브 클래스이므로, instanceof 또는 JSON 직렬화 시 문제가 발생할 수 있음
2. 프록시 패턴(Proxy Pattern)
정의
- 프록시 패턴은 디자인 패턴의 하나로, 실제 객체를 대체하거나 보호하기 위해 대리 객체를 제공하는 패턴임
- 원래 객체에 대한 접근을 제어하거나, 추가적인 로직(예: 캐싱, 로깅)을 삽입할 수 있음
구조
프록시 패턴은 3가지 주요 요소로 구성됨
- Subject (인터페이스 또는 추상 클래스):
- 실제 객체와 프록시 객체가 공통으로 구현하는 인터페이스임
- RealSubject (실제 객체):
- 실제 비즈니스 로직을 수행하는 클래스임
- Proxy (프록시 객체):
- RealSubject와 동일한 인터페이스를 구현함, RealSubject로의 접근을 제어하거나 추가 로직을 처리함
프록시 패턴의 종류
- Virtual Proxy:
- 실제 객체가 무거운 리소스를 사용할 경우, 객체의 생성을 지연함 (예: Lazy Loading)
- Protection Proxy:
- 실제 객체에 대한 접근을 제어함 (예: 권한 검증)
- Remote Proxy:
- 다른 네트워크 상의 객체를 로컬에서 호출할 수 있도록 프록시를 제공함 (예: RMI)
- Smart Proxy:
- 실제 객체 호출 전후에 추가 로직(예: 로깅, 캐싱)을 삽입함
예시: Virtual Proxy
// 공통 인터페이스
public interface Image {
void display();
}
// 실제 객체
public class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading " + filename);
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
// 프록시 객체
public class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename); // 실제 객체를 초기화
}
realImage.display();
}
}
// 클라이언트 코드
public class Main {
public static void main(String[] args) {
Image image = new ProxyImage("photo.jpg");
image.display(); // 실제 객체를 초기화하고 메서드 실행
image.display(); // 초기화된 객체를 재사용
}
}
출력 결과
Loading photo.jpg
Displaying photo.jpg
Displaying photo.jpg
JPA와의 관계
- JPA에서 프록시 객체는 Virtual Proxy 패턴에 해당함
- JPA는 Hibernate와 같은 구현체를 통해 프록시 객체를 생성하고, 데이터베이스에 실제로 접근하는 시점을 제어함
프록시 객체와 프록시 패턴의 관계
- 프록시 패턴은 디자인 원칙이며, 다양한 용도로 구현될 수 있음
- 프록시 객체는 프록시 패턴을 기반으로 동작하며, 실제 객체를 대신하여 특정 기능(예: 지연 로딩, 접근 제어 등)을 수행함
- JPA에서 사용하는 프록시 객체는 프록시 패턴의 한 구체적인 활용 사례임