목차
하나 하려고 여러 개를 순서대로 불러야 한다
주문 하나를 처리하려면 여러 서비스를 거쳐야 한다. 재고를 잡고, 결제를 하고, 배송을 걸고, 알림을 보낸다.
if (inventory.reserve(order)) { // 재고
payment.charge(order); // 결제
shipping.ship(order); // 배송
notification.notify(order); // 알림
}주문을 부르는 쪽이 이 네 서비스를 다 알아야 하고, 순서와 조건까지 챙겨야 한다. 재고를 결제보다 먼저 해야 한다는 것도, 재고 실패 시 멈춰야 한다는 것도 클라이언트의 몫이 된다. 주문하는 코드가 여러 군데면, 이 조율이 그만큼 복붙된다.
퍼사드 = 복잡한 안쪽에 단순한 창구 하나
퍼사드 패턴이 이걸 푼다.
여러 클래스로 이뤄진 복잡한 하위 시스템 앞에, 단순한 창구 하나를 세운다. 클라이언트는 그 창구만 상대한다.
“주문해줘” 한마디면 되게 만드는 것이다. 재고·결제·배송·알림을 누가 어떤 순서로 부르는지는 창구 뒤로 숨긴다.
클라이언트가 안쪽을 다 알아야 하는 게 문제
원래 코드의 문제는 클라이언트가 하위 시스템의 속을 다 안다는 것이다.
어느 서비스가 있는지, 무슨 순서로 부르는지, 언제 멈추는지 - 이 지식이 클라이언트에 박혀 있다. 그래서 하위 시스템이 조금만 바뀌어도(서비스 하나 추가, 순서 변경) 클라이언트를 다 찾아 고쳐야 한다. 쓰는 쪽과 안쪽이 너무 촘촘히 붙어 있다.
창구 하나를 세운다
조율을 통째로 맡는 클래스를 하나 만든다.
public class OrderFacade {
private final InventoryService inventory;
private final PaymentService payment;
private final ShippingService shipping;
private final NotificationService notification;
// 생성자 생략
public void placeOrder(Order order) {
if (!inventory.reserve(order)) {
throw new OutOfStockException();
}
payment.charge(order);
shipping.ship(order);
notification.notify(order);
}
}이제 부르는 쪽은 이 한 줄이다.
orderFacade.placeOrder(order);네 서비스와 순서·조건이 전부 OrderFacade 안으로 들어갔다. 클라이언트는 “주문해줘”만 안다.
안쪽은 그대로 두고 앞에만 세운다
퍼사드는 하위 시스템을 바꾸지도, 완전히 가리지도 않는다. 서비스들은 그대로 있고, 그 앞에 편한 진입점을 하나 더했을 뿐이다.
그래서 특별한 사정이 있으면 클라이언트가 서비스를 직접 부를 수도 있다. 퍼사드는 “쉬운 길”을 열어주는 것이지, 유일한 길로 강제하는 게 아니다. 대부분은 창구로 다니고, 예외적인 경우만 안쪽으로 직접 들어간다.
결합이 줄어든다
값어치는 결합에서 나온다. 클라이언트가 의존하는 건 이제 OrderFacade 하나다. 네 서비스가 아니다.
배송 방식이 바뀌거나 알림 서비스가 갈리거나 순서가 조정돼도, 그 변화는 OrderFacade 안에서 흡수된다. 클라이언트는 여전히 placeOrder(order)만 부른다. 안쪽이 아무리 출렁여도 창구의 모양이 그대로면 밖은 안전하다.
어댑터와 무엇이 다른가
둘 다 무언가를 감싸지만 목적이 다르다.
- 어댑터는 이미 정해진 인터페이스에 안 맞는 것을 맞춘다. 모양을 변환하는 게 일이고, 보통 대상은 하나다.
- 퍼사드는 맞춰야 할 인터페이스가 따로 없다. 복잡한 여럿을 위해 새 단순 인터페이스를 만든다.
어댑터는 “기존 모양에 끼워 맞추기”, 퍼사드는 “복잡한 것 앞에 쉬운 창구 새로 내기”다.
실무: 서비스 계층과 SDK 진입점
실무에서 퍼사드는 서비스 계층으로 흔히 나타난다. Spring의 @Service가 여러 리포지토리와 도메인 로직을 조율해 “회원가입”, “주문” 같은 한 동작으로 묶어주면, 컨트롤러 입장에선 그게 퍼사드다. 라이브러리·SDK가 복잡한 내부를 감추고 몇 개의 진입 클래스만 노출하는 것도 퍼사드다.
주의도 있다. 퍼사드가 너무 많은 걸 떠안으면 그 자체가 비대해진다. 창구 하나가 수십 가지 일을 다 하면, 이번엔 그 창구가 문제가 된다. 퍼사드도 “한 가지 흐름을 위한 창구” 단위로 나눠야 한다.
정리
| 퍼사드란 | 복잡한 하위 시스템 앞에 단순한 창구 하나를 세운다 |
| 문제 | 클라이언트가 여러 클래스와 순서·조건을 다 알아야 한다 |
| 고침 | 조율을 창구로 몰고, 클라이언트는 창구 메서드 하나만 부른다 |
| 성질 | 안쪽을 바꾸거나 막지 않는다 - 쉬운 길을 하나 더할 뿐 |
| 효과 | 결합이 줄어 안쪽이 바뀌어도 밖은 안전하다 |
| 어댑터와 | 어댑터=기존 모양에 맞춤, 퍼사드=복잡한 여럿에 새 단순 창구 |
복잡한 안쪽을 클라이언트가 다 짊어지지 않게, 앞에 쉬운 창구 하나를 세우는 것 - 그게 퍼사드다.