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 학습 메모에서 연결해 볼 수 있다.
참고 자료
'배움과 성장 > 소프트웨어 개발' 카테고리의 다른 글
| uv·uvx·npm·npx 차이: 프로젝트 실행과 일회성 도구 실행 구분 (0) | 2025.09.18 |
|---|---|
| Xcode 단축키 핵심 정리: 탐색·편집·빌드·디버깅 (0) | 2024.11.13 |
| SwiftUI View 구조: var body·struct·상태 객체의 역할 (0) | 2024.11.11 |
| Swift mutating: struct·enum에서 self를 바꾸는 규칙 (4) | 2024.11.11 |
| SwiftUI에서 struct와 class 고르기: @State·@Observable 기준 (1) | 2024.11.11 |
댓글