Search

프록시 패턴 (디자인 패턴)

프록시 패턴(Proxy Pattern)

개요

대상 원본 객체를 대리하여 대신 처리하게 함으로써 로직의 흐름을 제어하는 행동 패턴
일반적으로 프록시란 누군가에게 어떤 일을 대신 시키는 것을 의미합니다 이를 객체 지향 프로그래밍에 접목해보면 클라이언트가 대상 객체를 직접 쓰는게 아니라 중간에 프록시(대리인)을 거쳐서 쓰는 코드 패턴입니다
클라이언트는 실제 객체 대신 프록시 객체를 통해 작업 수행하며
또한 프록시는 실제 객체와 클라이언트 사이에 존재하게 됩니다
그럼 굳이 위와 같이 프록시 객체를 이용해야하는 이유가 무엇일까요?
웹 개발을 해보았다면, forward, backward proxy가 웹 어플리케이션 통신 시 어떠한 역활을 수행하는 지 들어보았을 것입니다
보안이 필요하거나 원격에 있는 인스턴스를 접근할 때, 캐싱을 할 때에도 프록시를 많이 사용했을 것입니다
프록시 패턴도 마찬가지입니다
접근을 제어하기가 어려운 객체에 기능을 추가하고 싶은데, 기존의 특정 객체를 수정할 수 없는 상황일때
초기화 지연, 접근 제어, 로깅, 캐싱 등, 기존 객체 동작에 수정 없이 가미하고 싶을 때
사용이 됩니다.
아래와 같은 케이스에 많이 사용이 될 수 있습니다
Heavy Object Loading Cost
Access Control / Validation / Security
Caching, Logging
Remote objects

프록시의 종류

프록시 패턴은 특징 중 하나는 다양성에서 찾아볼 수 있습니다.
1. 가상 프록시 (Virtual Proxy)
지연 초기화
가끔 필요하지만 항상 메모리에 적재되어 있는 무거운 서비스 객체가 있는 경우
실제 객체의 생성에 많은 자원이 소모됨, 하지만 사용 빈도는 낮을 때 사용
서비스가 시작될 때 객체를 생성하는 대신, 객체 초기화가 실제로 필요한 시점에 초기화될 수 있도록 지연
from abc import ABC, abstractmethod class ISubject(ABC): @abstractmethod def action(self): pass class RealSubject(ISubject): def action(self): print("실제 객체 액션!!") class Proxy(ISubject): def __init__(self): self._subject = None def action(self): # 프록시 객체는 실제 요청(action 메서드 호출)이 들어왔을 때 실제 객체를 생성합니다. if self._subject is None: self._subject = RealSubject() self._subject.action() # 위임 # 추가 작업 수행 print("프록시 객체 액션!!") # 클라이언트 코드 if __name__ == "__main__": sub = Proxy() sub.action() # 해당 시점에 실제 객체 생성
Python
복사
2. 원격 프록시 (Remote Proxy)
https://blogshine.tistory.com/17
프록시 클래스는 로컬에 있고, 대상 객체는 원격 서버에 존재하는 경우
프록시 객체는 네트워크를 통해 클라이언트의 요청을 전달하여 네트워크와 관련된 불필요한 작업들을 처리하고 결과값만 반환
클라이언트 입장에선 프록시를 통해 객체를 이용하는 것이니 원격이든 로컬이든 신경 쓸 필요가 없으며, 프록시는 진짜 객체와 통신을 대리하게 됨
3.
로깅 프록시
대상 객체에 대한 로깅을 추가하려는 경우
프록시는 서비스 메서드를 실행하기 전달하기 전에 로깅을 하는 기능을 추가하여 재정의
from abc import ABC, abstractmethod class ISubject(ABC): @abstractmethod def action(self): pass class RealSubject(ISubject): def action(self): print("실제 객체 액션!!") class Proxy(ISubject): def __init__(self, subject: RealSubject): self._subject = subject # 대상 객체를 composition def action(self): print("로깅..................") self._subject.action() # 위임 # 추가 작업 수행 print("프록시 객체 액션!!") print("로깅..................") # 클라이언트 코드 if __name__ == "__main__": sub = Proxy(RealSubject()) sub.action()
Python
복사
4.
보호 프록시 (Protection Proxy)
프록시가 대상 객체에 대한 자원으로의 엑세스 제어(접근 권한)
특정 클라이언트만 서비스 객체를 사용할 수 있도록 하는 경우
프록시 객체를 통해 클라이언트의 자격 증명이 기준과 일치하는 경우에만 서비스 객체에 요청을 전달할 수 있게 한다.
아래 케이스 스터디에서 자세한 코드 구현
from abc import ABC, abstractmethod class ISubject(ABC): @abstractmethod def action(self): pass class RealSubject(ISubject): def action(self): print("실제 객체 액션!!") class Proxy(ISubject): def __init__(self, subject: RealSubject, access: bool): self._subject = subject # 대상 객체를 composition self._access = access # 접근 권한 def action(self): if self._access: self._subject.action() # 위임 # 추가 작업 수행 print("프록시 객체 액션!!") else: print("접근 권한이 없습니다.") # 클라이언트 코드 if __name__ == "__main__": sub = Proxy(RealSubject(), False) sub.action() # 결과 : 접근 권한이 없음
Python
복사
5.
캐싱 프록시
a.
데이터가 큰 경우 캐싱하여 재사용을 유도
b.
클라이언트 요청의 결과를 캐시하고 이 캐시의 수명 주기를 관리
6.
스마트 참조 (Smart Reference)
더 이상 사용되지 않는 무거운 객체를 해제해야 할 때 유용합니다.
활성 클라이언트를 추적하고 주기적으로 상태를 확인합니다.
객체를 사용하는 클라이언트가 없으면 프록시가 해당 객체를 해제하여 시스템 리소스를 확보합니다.
객체의 수정 사항을 모니터링하여 변경되지 않은 경우 재사용을 허용합니다.

케이스 스터디

Access Control / Validation

결제 요청 사례
클라이언트PaymentProxy를 통해 결제를 요청
프록시는 이 요청을 실제 Cash 객체에 전달
Cash 객체는 결제 처리를 시뮬레이션하고 결과를 출력
아래 코드
Payment 클래스: 결제 요청을 위한 추상 기본 클래스(인터페이스 역할).
Cash 클래스: 실제 현금 결제를 처리하는 클래스
PaymentProxy 클래스: 결제 요청을 중개하는 프록시 클래스
from abc import ABC, abstractmethod # Subject 인터페이스 (추상 기본 클래스 사용) class Payment(ABC): @abstractmethod def request(self, amount: float) -> None: pass # Real Subject (실제 객체) class Cash(Payment): def request(self, amount: float) -> None: print(f"결제요청 완료.. 금액: {amount}") # Proxy class PaymentProxy: def __init__(self, real_subject: Payment): self._real_subject = real_subject def __getattr__(self, name): if name == 'request': return self._real_subject.request raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") # 사용 예 target_object = Cash() payment_proxy = PaymentProxy(target_object) payment_proxy.request(100) # 존재하지 않는 메서드 호출 시 에러 발생 try: payment_proxy.non_existent_method() except AttributeError as e: print(f"Error: {e}")
Python
복사
프록시를 사용했을 때의 특징 및 목적
결제 처리에 대한 추가적인 제어 또는 로직을 삽입할 수 있습니다.
실제 결제 객체(Cash)에 대한 직접적인 접근을 제한합니다.
프록시를 통해 결제 요청만 허용하고, 다른 작업은 차단합니다.
가능한 확장
결제 전 권한 검사 추가
결제 로깅 기능 구현
여러 결제 방식(신용카드, 계좌이체 등)에 대한 프록시 적용
위에 명시된 확장 기능들이 적용된다면 아래와 같습니다.
refactoring.guru
from abc import ABC, abstractmethod import logging from typing import List # 로깅 설정 logging.basicConfig(level=logging.INFO) # Subject 인터페이스 (추상 기본 클래스 사용) class Payment(ABC): @abstractmethod def request(self, amount: float) -> None: pass # Real Subject (실제 객체들) class Cash(Payment): def request(self, amount: float) -> None: print(f"현금 결제 완료.. 금액: {amount}") class CreditCard(Payment): def request(self, amount: float) -> None: print(f"신용카드 결제 완료.. 금액: {amount}") class BankTransfer(Payment): def request(self, amount: float) -> None: print(f"계좌이체 결제 완료.. 금액: {amount}") # 권한 검사를 위한 클래스 class AuthorizationService: def is_authorized(self, user_id: str) -> bool: # 실제 환경에서는 데이터베이스 조회 등의 로직이 들어갈 수 있습니다 return user_id in ["authorized_user1", "authorized_user2"] # Proxy class PaymentProxy: def __init__(self, payment_methods: List[Payment]): self._payment_methods = {type(method).__name__: method for method in payment_methods} self._auth_service = AuthorizationService() # composition def request(self, amount: float, payment_type: str, user_id: str) -> None: # 권한 검사 if not self._auth_service.is_authorized(user_id): logging.warning(f"권한 없는 사용자의 결제 시도: {user_id}") raise PermissionError("결제 권한이 없습니다.") # 결제 방식 확인 if payment_type not in self._payment_methods: logging.error(f"지원하지 않는 결제 방식: {payment_type}") raise ValueError(f"지원하지 않는 결제 방식입니다: {payment_type}") # 로깅 logging.info(f"결제 요청: 금액={amount}, 방식={payment_type}, 사용자={user_id}") # 실제 결제 처리 self._payment_methods[payment_type].request(amount) def __getattr__(self, name): if name == 'request': return self.request raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") # 사용 예 if __name__ == "__main__": payment_methods = [Cash(), CreditCard(), BankTransfer()] payment_proxy = PaymentProxy(payment_methods) # 성공적인 결제 요청 try: payment_proxy.request(100, "Cash", "authorized_user1") except Exception as e: print(f"Error: {e}") # 권한 없는 사용자의 결제 시도 try: payment_proxy.request(200, "CreditCard", "unauthorized_user") except Exception as e: print(f"Error: {e}") # 지원하지 않는 결제 방식 시도 try: payment_proxy.request(300, "Bitcoin", "authorized_user2") except Exception as e: print(f"Error: {e}") # 존재하지 않는 메서드 호출 시도 try: payment_proxy.non_existent_method() except AttributeError as e: print(f"Error: {e}")
Python
복사

가상 프록시

import time class HighResolutionImage: def __init__(self, path): self.img = None self.load_image(path) def load_image(self, path): # 이미지를 디스크에서 불러와 메모리에 적재 (작업 자체가 무겁고 많은 자원을 필요로 함) try: time.sleep(1) # 1초 대기로 무거운 작업 시뮬레이션 self.img = path except KeyboardInterrupt: # Python에서는 InterruptedException 대신 KeyboardInterrupt를 사용 print("이미지 로딩이 중단되었습니다.") print(f"{path}에 있는 이미지 로딩 완료") def show_image(self): # 이미지를 화면에 렌더링 print(f"{self.img} 이미지 출력") # 사용 예 if __name__ == "__main__": image = HighResolutionImage("/path/to/image.jpg") image.show_image()
Python
복사
위의 코드는 고해상도 이미지를 인자로 받아 메모리로 올린 후, show_image 메서드를 통해 사용자에게 렌더링하는 흐름입니다
if __name__ == "__main__": image = HighResolutionImage("/path/to/image.jpg") image2 = HighResolutionImage("/path/to/image2.jpg") image3 = HighResolutionImage("/path/to/image3.jpg") image.show_image() # 정자
Python
복사
이미지들을 메모리에 올리는 과정으로 인해 정작 중요한 action이 늦어지게 됩니다
이런 경우, 프록시 패턴을 통해 지연 로딩 효과를 누릴 수 있습니다
show_image 메서드를 호출할 때, 프록시를 통해 접근 후 메모리에 올리는 과정을 통해 필요한 메모리만 적재를 할 수 있습니다.
import time from abc import ABC, abstractmethod class Image(ABC): @abstractmethod def show_image(self): pass class HighResolutionImage(Image): def __init__(self, path): self.path = path self.img = None def load_image(self): # 이미지를 디스크에서 불러와 메모리에 적재 (작업 자체가 무겁고 많은 자원을 필요로 함) try: time.sleep(1) # 1초 대기로 무거운 작업 시뮬레이션 self.img = self.path except KeyboardInterrupt: print("이미지 로딩이 중단되었습니다.") print(f"{self.path}에 있는 이미지 로딩 완료") def show_image(self): if self.img is None: self.load_image() print(f"{self.img} 이미지 출력") class ImageProxy(Image): def __init__(self, path): self.path = path self.image = None def show_image(self): if self.image is None: self.image = HighResolutionImage(self.path) self.image.show_image() # 사용 예 if __name__ == "__main__": image1 = ImageProxy("/path/to/image1.jpg") image2 = ImageProxy("/path/to/image2.jpg") print("이미지 객체 생성 완료 (아직 로딩되지 않음)") print("\n첫 번째 이미지 표시:") image1.show_image() print("\n두 번째 이미지 표시:") image2.show_image() print("\n첫 번째 이미지 다시 표시 (이미 로딩되어 있음):") image1.show_image()
Python
복사

캐싱 프록시

1.
지연 초기화
a.
실제 쿼리 실행을 필요한 시점까지 지연시킴.
b.
캐시에 결과가 없을 때만 실제 데이터베이스 쿼리를 수행.
2.
리소스 관리:
a.
불필요한 데이터베이스 연결과 쿼리 실행을 방지.
클래스 설명
classDiagram
    class DatabaseQuery {
        <<interface>>
        +execute_query(query: string) string
    }

    class RealDatabaseQuery {
        +execute_query(query: string) string
    }

    class CacheProxy {
        -_real_database_query: DatabaseQuery
        -_cache: dict
        -_cache_duration: int
        +__init__(real_database_query: DatabaseQuery, cache_duration_seconds: int)
        +execute_query(query: string) string
    }

    DatabaseQuery <|.. RealDatabaseQuery
    DatabaseQuery <|.. CacheProxy
    CacheProxy o-- RealDatabaseQuery : 포함

    class Client {
        +main()
    }

    Client --> CacheProxy : 사용
    Client --> RealDatabaseQuery : 생성
Mermaid
복사
DatabaseQuery 클래스
데이터베이스 쿼리 실행을 위한 인터페이스를 정의
RealDatabaseQuery 클래스
실제 데이터베이스 쿼리를 실행하는 클래스
쿼리 실행을 시뮬레이션
CacheProxy 클래스
데이터베이스 쿼리 결과를 캐싱하는 프록시 클래스
주요 기능
쿼리 결과를 캐시에 저장
캐시된 결과가 있고 유효 기간 내라면 캐시된 결과를 반환
캐시된 결과가 없거나 만료되었다면 실제 데이터베이스 쿼리를 실행하고 결과를 캐시
import time # Real Subject를 위한 인터페이스 정의 class DatabaseQuery: def execute_query(self, query): pass # Real Subject: 실제 데이터베이스를 나타냄 class RealDatabaseQuery(DatabaseQuery): def execute_query(self, query): print(f"쿼리 실행 중: {query}") # 데이터베이스 쿼리 실행을 시뮬레이션하고 결과 반환 return f"쿼리 결과: {query}" # Proxy: 데이터베이스 쿼리를 위한 캐싱 프록시 class CacheProxy(DatabaseQuery): def __init__(self, real_database_query, cache_duration_seconds): self._real_database_query = real_database_query self._cache = {} self._cache_duration = cache_duration_seconds def execute_query(self, query): if query in self._cache and time.time() - self._cache[query]["timestamp"] <= self._cache_duration: # 캐시된 결과가 아직 유효하면 반환 print(f"CacheProxy: 쿼리에 대한 캐시된 결과 반환: {query}") return self._cache[query]["result"] else: # 쿼리를 실행하고 결과를 캐시 result = self._real_database_query.execute_query(query) self._cache[query] = {"result": result, "timestamp": time.time()} return result # 클라이언트 코드 if __name__ == "__main__": # Real Subject 생성 real_database_query = RealDatabaseQuery() # 5초의 캐시 유효 기간을 가진 Cache Proxy 생성 cache_proxy = CacheProxy(real_database_query, cache_duration_seconds=5) # 데이터베이스 쿼리 수행, 일부는 캐시될 것임 print(cache_proxy.execute_query("SELECT * FROM table1")) print(cache_proxy.execute_query("SELECT * FROM table2")) time.sleep(3) # 3초 대기 # 캐시된 결과를 반환해야 함 print(cache_proxy.execute_query("SELECT * FROM table1")) print(cache_proxy.execute_query("SELECT * FROM table3"))
Python
복사

오픈소스 톺아보기

Sqlalchemy
파이썬의 orm 라이브러리인 sqlalchemy는 lazy loading을 통해 엔티티의 속성에 접근할 때까지 실제 데이터에 대한 로딩을 지연시키고 있습니다
완벽하게 프록시 패턴이 적용되었다라고 말할 수 없지만, 컨셉 위주로 보면 좋은 소스코드입니다
# SQLAlchemy의 지연 로딩 프록시 패턴 구현 SQLAlchemy는 주로 `InstrumentedAttribute` 클래스와 `QueryableAttribute` 클래스를 사용하여 지연 로딩을 구현합니다. 이들은 프록시 패턴의 변형을 사용합니다. ## 1. InstrumentedAttribute 이 클래스는 ORM 매핑된 속성의 프록시 역할을 합니다. ```python from sqlalchemy.orm.attributes import InstrumentedAttribute class InstrumentedAttribute: def __init__(self, class_, key, impl): self.class_ = class_ self.key = key self.impl = impl def __get__(self, instance, owner): if instance is None: return self return self.impl.get(instance, self) def __set__(self, instance, value): self.impl.set(instance, value, self) ``` ## 2. QueryableAttribute 이 클래스는 `InstrumentedAttribute`를 확장하여 쿼리 관련 기능을 추가합니다. ```python from sqlalchemy.orm.attributes import QueryableAttribute class QueryableAttribute(InstrumentedAttribute): def __init__(self, class_, key, impl, comparator): super().__init__(class_, key, impl) self.comparator = comparator def __getattr__(self, key): return getattr(self.comparator, key) ``` ## 3. 지연 로딩 구현 실제 지연 로딩은 `RelationshipProperty` 클래스에서 구현됩니다. ```python from sqlalchemy.orm.relationships import RelationshipProperty class RelationshipProperty: def __init__(self, argument, **kwargs): self.argument = argument self.lazy = kwargs.get('lazy', 'select') def init(self): # 실제 관계 초기화 로직 def _get_strategy(self): if self.lazy == 'select': return LazyLoader(self) # 다른 로딩 전략들... class LazyLoader: def __init__(self, parent_property): self.parent_property = parent_property def load_lazy(self, state, passive): # 실제 데이터베이스에서 관련 객체를 로드하는 로직 pass ``` ## 사용 예시 ```python from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String) addresses = relationship("Address", back_populates="user", lazy='select') class Address(Base): __tablename__ = 'addresses' id = Column(Integer, primary_key=True) email = Column(String) user_id = Column(Integer, ForeignKey('users.id')) user = relationship("User", back_populates="addresses") # 사용 user = session.query(User).first() # 이 시점에서는 addresses가 로드되지 않음 print(user.addresses) # 이 시점에 실제 쿼리 실행 ``` 이 구현에서 `user.addresses`는 `InstrumentedAttribute`의 인스턴스입니다. `addresses`에 접근할 때 `__get__` 메서드가 호출되어 실제 데이터를 로드합니다.
Python
복사
프록시 객체 사용
InstrumentedAttributeQueryableAttribute는 실제 데이터에 대한 프록시 역할을 합니다. 이들은 속성에 접근할 때까지 실제 데이터 로딩을 지연시킵니다.
디스크립터 프로토콜
Subject (인터페이스)
직접적인 인터페이스는 없지만, Python의 디스크립터 프로토콜(__get__, __set__)이 이 역할을 합니다.
Python의 디스크립터 프로토콜(__get__, __set__)을 사용하여 속성 접근을 가로챕니다.
RealSubject (실제 객체)
실제 데이터베이스의 데이터가 RealSubject에 해당합니다.
구체적인 클래스로는 매핑된 모델 클래스(예: User, Address)의 인스턴스가 이에 해당합니다.
로딩 전략
RelationshipProperty는 여러 로딩 전략(lazy, eager, joined 등)을 지원합니다.
'lazy' 옵션은 지연 로딩을 활성화
참고 주소

프록시 패턴의 실제 사용 사례

위에서 케이스를 통해 살펴 보았지만, 프록시 디자인 패턴은 다양한 실제 시나리오에서 실용적으로 적용될 수 있는 케이스들은 어떤 것들이 있을지 사례들을 리스트업해봅시다.
1.
이미지 로딩을 위한 가상 프록시
사진 편집 앱에서 고해상도 이미지를 대신 표현합니다.
필요할 때만 이미지를 로드하여 메모리를 최적화하고 시작 시간을 향상시킵니다.
2.
웹 서비스를 위한 원격 프록시
원격 웹 서비스나 API와 통신할 때 사용합니다.
인증, 암호화, 네트워크 연결 세부사항을 처리하여 통신을 단순화합니다.
3.
데이터베이스 쿼리를 위한 캐싱 프록시
자주 접근하는 쿼리 결과를 저장합니다.
서버 부하를 줄이고 데이터 검색 속도를 높입니다.
4.
비용이 큰 객체의 지연 로딩
대규모 데이터셋이나 복잡한 객체에 사용됩니다.
필요할 때까지 객체 생성을 지연시킵니다.
CAD 소프트웨어(2D 및 3D 컴퓨터 지원 설계)에서 흔히 사용됩니다.
5.
보안 프록시를 통한 접근 제어
권한 및 인증 확인을 강제하여 안전한 데이터 접근을 보장합니다.
6.
디버깅을 위한 로깅 프록시
메소드 호출과 매개변수를 기록합니다.
코드 수정 없이 문제 진단을 돕습니다.
7.
리소스 관리를 위한 카운팅 프록시
동시성 프로그래밍과 같은 리소스 중심 시스템에서 사용됩니다.
객체 참조를 추적하고 더 이상 필요하지 않을 때 해제합니다.
8.
성능 지표를 위한 모니터링 프록시
성능 데이터와 사용 통계를 수집합니다.
주요 기능에 영향을 주지 않으면서 인사이트를 향상시킵니다.
9.
프록시 서버를 통한 부하 분산
웹 애플리케이션에서 네트워크 요청을 백엔드 서버들에 분산시킵니다.
가용성과 부하 분산을 보장합니다.
10.
데이터베이스 작업을 위한 트랜잭션 프록시
트랜잭션을 처리합니다.
롤백과 커밋 메커니즘을 통해 데이터 무결성을 보장합니다.
완전 일대일 대응으로 연결되어 있지는 않지만 아래와 같이 생각해볼 수는 있습니다.
UOW가 프록시 패턴의 특성을 가지는 측면
1.
간접적 접근
UOW를 통해 엔티티에 접근함으로써, 직접적인 엔티티 조작을 방지
이는 프록시 패턴의 '대리 접근' 특성과 유사
2.
추가 기능 제공
UOW는 엔티티 접근 시 변경 추적, 트랜잭션 관리 등의 추가 기능을 제공
이는 프록시 패턴이 실제 객체에 부가 기능을 추가하는 것과 유사합니다.
3.
객체 생성 및 관리 제어
UOW는 엔티티의 생성, 로딩, 저장을 관리
이는 프록시 패턴이 실제 객체의 생성과 초기화를 제어하는 것과 유사

디자인 패턴 간 비교

프록시 vs. 데코레이터 패턴
프록시와 데코레이터 패턴 모두 객체를 감싸지만, 그 의도와 사용 방식에 차이가 존재
데코레이터 패턴
객체의 행동을 동적으로 확장합니다.
클라이언트가 이를 인식하고 의도적으로 사용하는 경우가 많습니다.
기존 객체에 새로운 기능을 추가하거나 수정하는 데 중점을 둡니다.
프록시 패턴
객체에 대한 접근을 제어하거나 기능을 향상시키는 데 초점을 맞춥니다.
주로 클라이언트에게 투명하게 작동합니다. 즉, 클라이언트는 프록시를 통해 작업하고 있다는 사실을 모를 수 있습니다.
주로 보안, 지연 로딩, 캐싱 등의 목적으로 사용됩니다
프록시 vs. 파사드 패턴
파사드와 프록시 패턴 모두 복잡한 시스템과의 상호작용을 단순화하지만, 그 범위와 의도에 차이가 존재
파사드 패턴
복잡한 시스템이나 서브시스템에 대해 통합된, 단순화된 인터페이스를 제공합니다.
전체 시스템의 복잡성을 감추고 사용하기 쉬운 인터페이스를 제공하는 데 중점을 둡니다.
여러 구성 요소를 포함하는 큰 시스템에 대한 진입점 역할을 합니다.
프록시 패턴
주로 개별 객체에 대한 접근을 관리합니다.
시스템의 구조와 인터페이스를 변경하지 않으면서 제어나 기능을 추가합니다.
특정 객체에 대한 접근을 제어하거나 추가 기능을 제공하는 데 중점을 둡니다.
요약하자면, 데코레이터는 객체의 기능을 확장하고, 파사드는 복잡한 시스템을 단순화하며, 프록시는 객체에 대한 접근을 제어하거나 관리합니다 각 패턴은 서로 다른 문제를 해결하기 위해 설계되었지만, 때로는 이들의 특성이 중첩되거나 결합되어 사용될 수 있습니다.

참고