프록시 패턴
IT 위키
더 많은 작업
- 상위 문서: 소프트웨어 디자인 패턴
Proxy Pattern; Surrogate Pattern
- 실제 객체 대신 같은 인터페이스를 가진 대리 객체(Proxy)를 두어, 실제 객체에 대한 접근을 제어하거나 부가 처리를 끼워 넣는 GoF 구조 패턴
클라이언트는 실제 객체를 쓰는 것과 똑같이 프록시를 호출한다. 프록시는 호출을 받아 필요한 일(생성 지연, 권한 확인, 원격 호출, 캐싱, 로깅)을 한 뒤 실제 객체에 넘긴다. 대리인에게 일을 맡기는 것에 비유된다.
| 역할 | 설명 |
|---|---|
| Subject | RealSubject 와 Proxy 가 함께 구현하는 공통 인터페이스. 그래서 클라이언트는 둘을 구분하지 않는다 |
| RealSubject | 실제 일을 하는 객체. 생성 비용이 크거나, 멀리 있거나, 보호가 필요한 대상이다 |
| Proxy | RealSubject 에 대한 참조를 가지고 Subject 인터페이스를 구현한다. 접근을 제어하고 실제 요청을 위임한다 |
| Client | Subject 인터페이스만 알고 호출한다 |
호출 흐름은 Client → Proxy → RealSubject 순이다.
| 종류 | 하는 일 | 예 |
|---|---|---|
| 가상 프록시(Virtual Proxy) | 생성 비용이 큰 객체를 실제로 필요할 때까지 만들지 않는다(지연 초기화) | 대용량 이미지 로딩, ORM 의 지연 로딩 |
| 원격 프록시(Remote Proxy) | 다른 주소 공간(다른 프로세스·서버)의 객체를 로컬 객체처럼 쓰게 한다. 통신을 감춘다 | Java RMI 스텁, gRPC 클라이언트 스텁 |
| 보호 프록시(Protection Proxy) | 호출자의 권한을 확인해 접근을 허용하거나 막는다 | 역할별 메서드 접근 통제 |
| 스마트 참조(Smart Reference) | 참조 횟수 관리, 잠금, 로깅 같은 부가 작업을 한다 | 스마트 포인터, 감사 로그 |
| 캐싱 프록시(Caching Proxy) | 결과를 저장해 두고 같은 요청에 다시 쓴다 | 조회 결과 캐시 |
- 장점
- 클라이언트와 실제 객체를 바꾸지 않고 접근 제어·지연 생성·캐싱을 덧붙일 수 있다(개방-폐쇄 원칙)
- 실제 객체의 생명주기를 프록시가 관리할 수 있다
- 원격·보안 같은 관심사를 실제 객체에서 떼어 낸다
- 단점
- 클래스가 늘어 구조가 복잡해진다
- 호출이 한 단계 더 거치므로 응답이 늦어질 수 있다
| 구분 | 프록시 | 데코레이터 | 어댑터 | 퍼사드 |
|---|---|---|---|---|
| 목적 | 접근 제어·대리 | 기능 추가(장식) | 인터페이스 변환 | 서브시스템 단순화 |
| 인터페이스 | 실제 객체와 같다 | 실제 객체와 같다 | 다른 인터페이스로 바꾼다 | 새 단순 인터페이스를 만든다 |
| 실제 객체 생성 | 프록시가 직접 만들거나 관리하기도 한다 | 클라이언트가 만들어 감싼다 | 기존 객체를 감싼다 | 여러 객체를 감싼다 |
| 중첩 | 보통 한 겹 | 여러 겹 쌓는 것이 일반적 | 한 겹 | 한 겹 |
데코레이터와 구조가 거의 같아 헷갈리기 쉽다. 차이는 의도다. 데코레이터는 기능을 덧붙이는 것이 목적이고, 프록시는 실제 객체에 이르는 길을 통제하는 것이 목적이다.
# Subject - 공통 인터페이스
class Image:
def display(self):
raise NotImplementedError
# RealSubject - 만들 때 디스크에서 읽어 오느라 비싸다
class RealImage(Image):
def __init__(self, path):
self.path = path
print("loading", path)
def display(self):
print("display", self.path)
# Proxy - 실제로 보여 줄 때까지 RealImage 를 만들지 않는다 (가상 프록시)
class ImageProxy(Image):
def __init__(self, path):
self.path = path
self._real = None
def display(self):
if self._real is None:
self._real = RealImage(self.path)
self._real.display()
img = ImageProxy("photo.png") # 아직 로딩하지 않는다
img.display() # 이때 처음 로딩한다
- ORM 의 지연 로딩(연관 객체를 조회 시점까지 미룬다)
- Spring AOP 같은 프레임워크의 트랜잭션·보안 처리용 동적 프록시
- 원격 호출 스텁(RMI, RPC)
- 네트워크의 프록시 서버도 같은 발상이지만, 이 문서의 패턴은 객체 설계 수준의 개념이다