영화를 보러 집에서 준비한다고 생각해보자. 조명을 끄고, TV를 켜고, 스피커 볼륨을 조절하고, 스트리밍 앱을 실행한다. 여러 장치를 각각 조작해야 한다. 리모컨 하나로 "영화 모드" 버튼을 누르면 이걸 한 번에 처리해준다면 얼마나 편할까.
Facade가 바로 그 리모컨이다.
복잡한 서브시스템들을 하나의 단순한 인터페이스로 감싸는 패턴이다. 사용하는 쪽은 내부 구조를 몰라도 되고, Facade만 알면 된다.

온라인 주문 시스템을 예로 들어보자. 주문 하나를 처리하려면 재고 확인, 결제, 배송 세 시스템이 각각 동작해야 한다.
// 서브시스템들 — 각자 독립적으로 복잡한 로직을 가짐
public class InventoryService {
public boolean checkStock(String productId) {
System.out.println("재고 확인: " + productId);
return true;
}
}
public class PaymentService {
public boolean processPayment(String cardNumber, int amount) {
System.out.println("결제 처리: " + amount + "원");
return true;
}
}
public class ShippingService {
public void ship(String productId, String address) {
System.out.println("배송 시작: " + productId + " → " + address);
}
}
// Facade — 복잡한 순서를 내부에서 처리
public class OrderFacade {
private InventoryService inventory = new InventoryService();
private PaymentService payment = new PaymentService();
private ShippingService shipping = new ShippingService();
public void placeOrder(String productId, String cardNumber, int amount, String address) {
if (!inventory.checkStock(productId)) {
System.out.println("재고 없음");
return;
}
if (!payment.processPayment(cardNumber, amount)) {
System.out.println("결제 실패");
return;
}
shipping.ship(productId, address);
System.out.println("주문 완료");
}
}
// 사용하는 쪽 — Facade 하나만 알면 됨
OrderFacade order = new OrderFacade();
order.placeOrder("ITEM-001", "1234-5678", 29000, "서울시 강남구");
InventoryService, PaymentService, ShippingService가 각각 어떤 메서드를 갖고 있는지, 어떤 순서로 호출해야 하는지 사용하는 쪽은 알 필요가 없다.
Facade는 서브시스템을 숨기지 않는다. 여전히 필요하면 서브시스템에 직접 접근할 수 있다. Facade는 편의를 위한 진입점이지, 강제적인 제한이 아니다.
// 직접 접근도 여전히 가능
PaymentService payment = new PaymentService();
payment.processPayment("1234-5678", 5000);
| 구분 | Adapter | Facade |
|---|---|---|
| 목적 | 인터페이스 변환 | 복잡성 단순화 |
| 대상 | 하나의 클래스 | 여러 서브시스템 |
| 기존 코드 | 호환되지 않는 것을 연결 | 그대로 두고 진입점만 추가 |
복잡한 시스템을 다룰 때 "어디서부터 시작해야 하지?"라는 막막함이 든다면, Facade를 만들 타이밍이다. 복잡함은 안으로 숨기고 바깥에는 단순한 창구 하나만 내어두는 것, 그게 Facade가 하는 일이다.