Facade 패턴: Adapter·Mediator와 구분하는 기준

반응형

Facade 패턴은 복잡한 subsystem 앞에 client가 자주 쓰는 고수준 interface를 둔다. 목표는 내부 class를 모두 숨기는 것이 아니라, 여러 단계를 알아야 하는 사용 경로를 하나의 명확한 진입점으로 만드는 것이다. Adapter와 Mediator도 중간 객체를 둔다는 모양은 비슷하지만 해결하려는 문제가 다르다.

Facade가 하는 일

예를 들어 backup을 만들려면 source를 읽고, 압축하고, 암호화하고, storage에 저장하는 순서를 알아야 할 수 있다. client마다 이 순서를 반복하면 subsystem 변경이 여러 곳으로 번진다.

client → BackupFacade → Reader
                      → Compressor
                      → Encryptor
                      → Storage

Facade는 자주 쓰는 use case를 고수준 method로 표현한다. subsystem class를 삭제하거나 client의 직접 접근을 반드시 금지하지는 않는다. 고급 기능이 필요한 client는 subsystem을 직접 쓸 수도 있고, 서로 다른 client군에 맞춘 facade를 여러 개 둘 수도 있다.

class Reader:
    def read(self, source):
        return f"data:{source}".encode()


class Compressor:
    def compress(self, data):
        return b"zip(" + data + b")"


class Encryptor:
    def encrypt(self, data, key_id):
        return f"enc[{key_id}](".encode() + data + b")"


class Storage:
    def put(self, name, data):
        return f"stored://{name}?bytes={len(data)}"


class BackupFacade:
    def __init__(self, reader, compressor, encryptor, storage):
        self.reader = reader
        self.compressor = compressor
        self.encryptor = encryptor
        self.storage = storage

    def create_backup(self, source, name, key_id):
        raw = self.reader.read(source)
        compressed = self.compressor.compress(raw)
        encrypted = self.encryptor.encrypt(compressed, key_id)
        return self.storage.put(name, encrypted)


backup = BackupFacade(Reader(), Compressor(), Encryptor(), Storage())
print(backup.create_backup("photos", "photos-2026", "backup-key"))

이 예제에서 create_backup은 client가 원하는 use case를 보여 주고 세부 순서를 조정한다. 실제 암호화나 storage를 구현한 코드는 아니다. production에서는 key material을 인자로 노출하지 않고 실패 복구, idempotency, transaction 경계와 관찰 가능성을 별도로 설계해야 한다.

Adapter와의 차이

Adapter의 핵심 의도는 기존 interface를 client가 기대하는 다른 interface로 변환하는 것이다. 예를 들어 third-party storage의 upload_blob()만 있는데 application이 put() interface를 요구한다면 adapter가 이름, argument와 반환 형식을 맞춘다.

Facade는 interface가 호환되지 않아서라기보다 subsystem 사용이 복잡해서 고수준 진입점을 만든다. 하나의 facade 안에서 adapter를 dependency로 사용할 수도 있다.

질문 Facade Adapter
주된 목적 복잡한 사용 경로 단순화 호환되지 않는 interface 변환
감싸는 범위 여러 subsystem일 때가 많음 한 class·service일 때가 많음
새 interface 고수준 use case 중심 client가 기대한 contract 중심
함께 사용 가능성 adapter를 내부 dependency로 사용 가능 facade 뒤에서 외부 구현을 맞출 수 있음

‘객체 하나를 감쌌다’는 코드 모양만으로 패턴을 정하지 말고 변경 이유를 본다.

Mediator와의 차이

Mediator는 여러 colleague가 서로 직접 얽히는 대신 상호작용을 중앙에서 조정한다. 참여 객체 간의 many-to-many communication을 줄이는 것이 중심이다. Facade는 subsystem을 사용하는 외부 client에게 단순한 입구를 제공하는 데 중심이 있고, 호출 방향도 주로 client에서 subsystem으로 향한다.

예를 들어 UI field 여러 개가 서로 상태를 바꾸는 규칙은 Mediator가 어울릴 수 있다. 반면 ‘사용자 등록’이 validation, account 생성, welcome message 준비를 어떤 순서로 호출할지 보여 주는 application entry point는 Facade 또는 Service Layer에 가까울 수 있다.

둘의 경계는 class 이름으로 자동 결정되지 않는다. facade가 내부 객체 간 interaction까지 과도하게 떠안으면 mediator나 domain service의 책임을 같이 가지면서 비대해질 수 있다.

API Gateway와 Service Layer는 자동으로 Facade가 아니다

API Gateway는 deployment, authentication, routing, protocol translation과 traffic policy를 담당할 수 있는 architecture component다. client용 단순 API를 제공한다는 점에서 facade 역할을 할 수 있지만, 제품 이름만으로 GoF Facade 패턴이라고 단정하지 않는다.

Service Layer도 application boundary에서 use case와 transaction을 조정한다. Facade와 모양이 겹칠 수 있지만 domain logic의 위치, transaction 책임과 remote boundary라는 더 큰 architecture 맥락을 가진다. 패턴 이름보다 책임과 dependency 방향을 문서화하는 것이 중요하다.

좋은 Facade를 위한 점검 기준

  • method 이름이 subsystem 기술이 아니라 client의 use case를 드러내는가
  • facade 안에 핵심 domain 규칙을 무작정 복사하지 않았는가
  • 모든 기능을 한 class에 모은 God object가 되지 않았는가
  • subsystem 변경이 client contract에 꼭 필요하지 않은 방식으로 새지 않는가
  • error, partial failure와 retry 책임이 명확한가
  • 고급 client가 필요한 세부 기능을 사용할 경로가 남아 있는가

Facade는 복잡성을 없애지 않는다. 복잡성이 필요한 곳을 subsystem 안에 두고, client가 알아야 할 표면을 줄인다. policy와 mechanism의 dependency 방향은 Clean Architecture의 정책과 수준, SRP·DIP 같은 설계 원칙은 SOLID 학습 메모에서 연결해 볼 수 있다.

참고 자료

반응형
KEEP READING
카테고리 전체 보기 →

댓글