프록시패턴 (Proxy Pattern)
-
- 객체에 대한 접근을 제어하거나 추가적인 기능을 제공하기 위해 객체의 대리인 역할을 수행하는 클래스를 만드는 구조적 디자인 패턴
- 프록시 객체는 실제 객체(Real Subject)에 대한 참조를 유지하며, 클라이언트가 실제 객체에 직접 접근하지 않고 프록시를 통해 간접적으로 접근하도록 만든다.
프록시 패턴의 핵심
- 접근 제어
- 권한, 인증, 요청 제한 등을 통해 객체에 대한 접근을 관리한다.
- 예: 특정 사용자만 데이터베이스 수정 권한을 갖는 경우.
- 리소스 관리
- 리소스가 무겁거나 생성 비용이 높은 객체의 생성을 지연하거나 최적화 한다.
- 예: 대용량 데이터를 지연 로딩(Lazy Loading)으로 처리.
- 추가 기능
- 로깅, 캐싱, 요청 검증 등 실제 객체와 독립적으로 부가적인 동작을 수행한다.
- 예: 객체에 대한 호출 시 로깅 정보를 기록.
구성요소
- Subject (인터페이스 또는 추상 클래스)
- 실제 객체(Real Subject)와 프록시(Proxy)가 구현해야 할 공통 인터페이스 또는 추상 클래스
- Real Subject (실제 객체)
- 실제 작업을 수행하는 객체
- Proxy (프록시 객체)
- Real Subject와 동일한 인터페이스를 구현하며, 클라이언트가 요청을 프록시 객체를 통해 처리하도록 한다
- 필요에 따라 요청을 가로채거나, 접근을 제어하거나, 추가 로직을 수행한 후 Real Subject로 요청을 전달
프록시 패턴의 유형
- 가상 프록시 (Virtual Proxy)
- 리소스가 무겁거나 비용이 많이 드는 객체를 실제로 생성하기 전에 대체 역할을 수행
- 예: 대용량 이미지 로딩을 위한 지연 초기화(lazy initialization).
- 원격 프록시 (Remote Proxy)
- 네트워크를 통해 다른 주소 공간에 있는 객체를 대리한다.
- 예: 원격 서버와의 통신을 단순화하는 Stub.
- 보호 프록시 (Protection Proxy)
- 객체에 대한 접근 제어를 제공합니다. 사용자 권한에 따라 객체 접근을 제한
- 예: 관리자만 데이터 수정이 가능한 시스템.
- 캐싱 프록시 (Caching Proxy)
- 결과를 캐싱하여 동일한 요청에 대해 성능을 향상
- 예: 데이터베이스 쿼리 캐싱.
- 스마트 프록시 (Smart Proxy)
- 추가적인 작업(예: 참조 카운팅, 로깅 등)을 수행
- 예: 객체의 메모리 사용 추적.
예제
from abc import ABC, abstractmethod
# Subject (인터페이스)
class Image(ABC):
@abstractmethod
def display(self):
pass
# Real Subject (실제 객체)
class RealImage(Image):
def __init__(self, filename):
self.filename = filename
self.load_from_disk()
def load_from_disk(self):
print(f"Loading {self.filename} from disk...")
def display(self):
print(f"Displaying {self.filename}")
# Proxy (프록시 객체)
class ProxyImage(Image):
def __init__(self, filename):
self.filename = filename
self.real_image = None
def display(self):
if not self.real_image: # RealImage 객체가 없으면 생성
self.real_image = RealImage(self.filename) # 지연 초기화
self.real_image.display() # RealImage의 display 메서드 호출
# 클라이언트 코드
if __name__ == "__main__":
image = ProxyImage("example.jpg")
# 첫 번째 호출에서 실제 객체 생성 및 표시
image.display()
# 두 번째 호출에서는 이미 생성된 객체를 재사용
image.display()
작동 방식
-
- 클라이언트가 ProxyImage 객체를 생성
- ProxyImage 클래스의 __init__ 메서드가 호출
- filename을 저장하고 real_image를 None으로 초기화.
- 실제 이미지(RealImage)는 아직 생성되지 않음
- 목적: 리소스 낭비를 줄이기 위해 실제 객체 생성 시점을 지연.
- display() 메서드 첫 번째 호출
- 프록시 객체(ProxyImage)의 display 메서드가 호출
- real_image가 None인지 확인
- 조건이 참이므로 RealImage 객체를 생성
- RealImage 생성자(__init__)가 호출되어 디스크에서 이미지를 로드
- print : Loading example.jpg from disk...
- 이후, 생성된 RealImage 객체의 display 메서드를 호출
- print : Displaying example.jpg
- display() 메서드 두 번째 호출
- 프록시 객체의 display 메서드가 호출
- real_image가 이미 생성되어 있으므로 조건이 거짓
- 이미 생성된 RealImage 객체의 display 메서드만 호출
- print : Displaying example.jpg
- 클라이언트가 ProxyImage 객체를 생성
핵심:
- 지연 초기화(Lazy Initialization)를 통해 불필요한 리소스 낭비를 방지.
- 클라이언트는 ProxyImage와 RealImage의 차이를 전혀 알지 못하고 동일한 방식으로 사용.
프록시 패턴의 장점
- 객체 생성 지연, 접근 제어, 추가 기능(로깅, 캐싱 등)을 쉽게 구현할 수 있음.
- 실제 객체와 클라이언트 간의 결합도를 낮춰 유연성을 높임.
- 네트워크 통신 또는 보안 요구 사항과 같은 복잡한 작업을 추상화.
프록시 패턴의 단점
- 프록시 객체를 추가로 생성하므로 코드가 복잡해질 수 있음.
- 요청을 실제 객체로 위임하므로 약간의 성능 저하가 있을 수 있음.
- 잘못 설계하면 유지보수가 어려울 수 있음.
전략 패턴이 적합한 상황
- 알고리즘이 다양하게 변경될 수 있는 경우:
- 실행 시점에서 서로 다른 알고리즘을 적용해야 하는 경우.
- 예: 결제 방식, 경로 탐색 알고리즘, 데이터 압축 방식 등.
- 동작을 동적으로 변경할 필요가 있는 경우:
- 실행 중에 객체의 동작을 쉽게 변경할 수 있어야 하는 경우.
- 코드의 중복을 줄이고 유지보수성을 높이고 싶은 경우:
- 다양한 동작을 캡슐화하여 코드 변경을 최소화하고 확장을 용이하게 하고 싶을 때.
'디자인패턴' 카테고리의 다른 글
노출 모듈 패턴 (Revealing Module Pattern) (0) | 2024.12.17 |
---|---|
디자인패턴 - 이터레이터 패턴 (0) | 2024.12.17 |
옵저버 패턴 (Observer Pattern) (4) | 2024.12.14 |
디자인패턴 - 전략패턴 (2) | 2024.12.14 |
디자인패턴 - 팩토리 패턴 (0) | 2024.12.12 |