하리Hari

10. 퍼사드 패턴 (Facade)

복잡한 내부 시스템을 숨기고 단순화된 단일 창구를 제공하는 퍼사드 패턴을 알아봅니다.

한 줄 요약
여러 객체를 정해진 순서로 다뤄야 하는 복잡한 일을 창구 하나 뒤에 모아 두고, 쓰는 쪽은 그 창구에 한 번만 부탁하게 하는 패턴이에요. 퍼사드(Facade)는 건물의 “정면”이라는 뜻이에요.

문제: 쓰는 곳마다 복잡한 순서를 외워야 하는 코드

온라인 가게의 주문 프로그램을 만든다고 해 볼게요. 주문 하나를 받으려면 네 시스템을 순서대로 다뤄야 해요.

  1. 재고가 충분한지 확인하고, 있으면 빼요.
  2. 결제기로 돈을 받아요. (하나에 10000원)
  3. 배송을 접수해요.
  4. 손님에게 문자를 보내요.

주문은 앱에서도, 가게 앞 키오스크에서도 들어와요. 가장 먼저 떠오르는 방법은 주문을 받는 곳마다 네 시스템을 직접 다루는 거예요.

(참고: 주문 하나에 필요한 시스템들이에요. 저마다 할 줄 아는 일이 달라요)
[재고]를 설계하자:
    '남은수'를 [숫자]인 2로 정하자

    [논리]를 돌려주는 <있는지>를 만들자 ([숫자]인 '개수'):
        ('나'의 '남은수' >= '개수')를 돌려주자

    <빼기>를 만들자 ([숫자]인 '개수'):
        '나'의 '남은수'를 ('나'의 '남은수' - '개수')로 정하자

[결제기]를 설계하자:
    <결제하기>를 만들자 ([숫자]인 '원'):
        틀"[결제] {'원'}원"을 출력하자

[배송]을 설계하자:
    <접수하기>를 만들자 ([문자열]인 '주소'):
        틀"[배송] {'주소'}(으)로 접수"를 출력하자

[문자]를 설계하자:
    <보내기>를 만들자 ([문자열]인 '내용'):
        틀"[문자] {'내용'}"을 출력하자

(참고: 주문하는 곳마다 네 시스템을 직접, 순서대로 다뤄요)
<앱주문>을 만들자 ('재고', '결제기', '배송', '문자', [숫자]인 '개수', [문자열]인 '주소'):
    만약 ('재고'의 <있는지>('개수')가 거짓과 같다) 라면:
        "[앱] 재고가 모자라요."를 출력하자
        돌려주자
    '재고'의 <빼기>('개수')를 실행하자
    '결제기'의 <결제하기>('개수' * 10000)를 실행하자
    '배송'의 <접수하기>('주소')를 실행하자
    '문자'의 <보내기>("주문이 접수됐어요!")를 실행하자

<키오스크주문>을 만들자 ('재고', '결제기', '배송', '문자', [숫자]인 '개수', [문자열]인 '주소'):
    (참고: 재고 확인을 깜빡했어요)
    '재고'의 <빼기>('개수')를 실행하자
    '결제기'의 <결제하기>('개수' * 10000)를 실행하자
    '배송'의 <접수하기>('주소')를 실행하자
    '문자'의 <보내기>("주문이 접수됐어요!")를 실행하자

'가게재고'를 새로운 [재고]()로 정하자
'가게결제기'를 새로운 [결제기]()로 정하자
'가게배송'을 새로운 [배송]()으로 정하자
'가게문자'를 새로운 [문자]()로 정하자

<앱주문>('가게재고', '가게결제기', '가게배송', '가게문자', 2, "서울시 하리구")를 실행하자
<키오스크주문>('가게재고', '가게결제기', '가게배송', '가게문자', 1, "부산시 카나데구")를 실행하자
틀"남은 재고: {'가게재고'의 '남은수'}"를 출력하자

실행 결과
[결제] 20000원
[배송] 서울시 하리구(으)로 접수
[문자] 주문이 접수됐어요!
[결제] 10000원
[배송] 부산시 카나데구(으)로 접수
[문자] 주문이 접수됐어요!
남은 재고: -1

재고가 -1이 됐어요. 없는 물건을 팔고 돈까지 받았어요.

  • 같은 순서가 주문하는 곳마다 복사돼요. <앱주문>과 <키오스크주문>이 네 시스템을 똑같은 순서로 다뤄요. 전화 주문이 생기면 또 복사해야 해요.
  • 한 곳에서 빠뜨리면 버그가 돼요. 키오스크 쪽은 재고 확인을 깜빡했어요. 에러는 안 났지만 재고가 마이너스가 됐어요.
  • 주문하는 곳이 너무 많이 알아요. <키오스크주문>은 “주문을 받는다”만 알면 되는데, 시스템 네 개의 이름과 메서드, 순서, 가격 계산까지 다 알아야 해요. 인자만 여섯 개예요.
  • 순서가 바뀌면 모든 곳을 고쳐야 해요. “문자를 결제 전에 보내 주세요”라는 요청이 오면, 주문하는 곳을 하나하나 찾아 고쳐야 해요.

해결책: 복잡한 일 앞에 창구 하나 세우기

퍼사드 패턴은 복잡한 시스템들을 고치지 않고, 그 앞에 쉬운 창구를 하나 세워요.

  1. 재고, 결제기, 배송, 문자는 그대로 둬요. 저마다 자기 일만 잘하면 돼요.
  2. 창구를 만들어요. 창구는 네 시스템을 품고 있어요.
  3. 창구에 <주문하기>를 하나 만들고, 그 안에서 올바른 순서를 한 번만 적어요. 재고 확인도 여기 있어요.
  4. 주문하는 곳은 이제 창구에 <주문하기> 한 번만 부탁해요. 네 시스템은 몰라도 돼요.

비유: 호텔 프런트

호텔에서 “내일 아침 7시에 깨워 주시고, 택시 한 대 불러 주세요”라고 프런트에 말하면 끝이에요. 프런트가 알람 담당, 택시 회사, 청소 담당에게 알아서 연락해요. 손님은 그 사람들의 전화번호도, 부탁하는 순서도 몰라도 돼요. 프런트가 바로 “퍼사드”예요. 그렇다고 직원들이 사라진 건 아니에요. 필요하면 손님이 청소 담당에게 직접 부탁할 수도 있어요.

누가 무엇을 하나요?

역할 이 예제에서 하는 일
복잡한 시스템들 [재고], [결제기], [배송], [문자] 저마다 자기 일을 해요. 퍼사드를 몰라요
퍼사드(창구) [주문창구] 시스템들을 품고, 올바른 순서로 다뤄요
쓰는 곳 <앱주문>, <키오스크주문> 창구에 한 번만 부탁해요

하리로 만들기

같은 가게를 퍼사드 패턴으로 다시 만들어 볼게요. 네 시스템의 코드는 한 글자도 바뀌지 않았어요.

(참고: 주문 하나에 필요한 시스템들이에요. 저마다 할 줄 아는 일이 달라요)
[재고]를 설계하자:
    '남은수'를 [숫자]인 2로 정하자

    [논리]를 돌려주는 <있는지>를 만들자 ([숫자]인 '개수'):
        ('나'의 '남은수' >= '개수')를 돌려주자

    <빼기>를 만들자 ([숫자]인 '개수'):
        '나'의 '남은수'를 ('나'의 '남은수' - '개수')로 정하자

[결제기]를 설계하자:
    <결제하기>를 만들자 ([숫자]인 '원'):
        틀"[결제] {'원'}원"을 출력하자

[배송]을 설계하자:
    <접수하기>를 만들자 ([문자열]인 '주소'):
        틀"[배송] {'주소'}(으)로 접수"를 출력하자

[문자]를 설계하자:
    <보내기>를 만들자 ([문자열]인 '내용'):
        틀"[문자] {'내용'}"을 출력하자

(참고: 네 시스템을 품고, 주문 한 번에 필요한 순서를 한곳에서 지키는 창구예요)
[주문창구]를 설계하자:
    '재고'를 [재고]인 새로운 [재고]()로 정하여 숨기자
    '결제기'를 [결제기]인 새로운 [결제기]()로 정하여 숨기자
    '배송'을 [배송]인 새로운 [배송]()으로 정하여 숨기자
    '문자'를 [문자]인 새로운 [문자]()로 정하여 숨기자

    [논리]를 돌려주는 <주문하기>를 만들자 ([숫자]인 '개수', [문자열]인 '주소'):
        만약 ('나'의 '재고'의 <있는지>('개수')가 거짓과 같다) 라면:
            "재고가 모자라요."를 출력하자
            거짓을 돌려주자
        '나'의 '재고'의 <빼기>('개수')를 실행하자
        '나'의 '결제기'의 <결제하기>('개수' * 10000)를 실행하자
        '나'의 '배송'의 <접수하기>('주소')를 실행하자
        '나'의 '문자'의 <보내기>("주문이 접수됐어요!")를 실행하자
        참을 돌려주자

    [숫자]를 돌려주는 <남은재고>를 만들자 ():
        '나'의 '재고'의 '남은수'를 돌려주자

(참고: 주문하는 곳은 창구 하나만 알아요)
<앱주문>을 만들자 ([주문창구]인 '창구', [숫자]인 '개수', [문자열]인 '주소'):
    '창구'의 <주문하기>('개수', '주소')를 실행하자

<키오스크주문>을 만들자 ([주문창구]인 '창구', [숫자]인 '개수', [문자열]인 '주소'):
    '창구'의 <주문하기>('개수', '주소')를 실행하자

'가게'를 새로운 [주문창구]()로 정하자
<앱주문>('가게', 2, "서울시 하리구")를 실행하자
<키오스크주문>('가게', 1, "부산시 카나데구")를 실행하자
틀"남은 재고: {'가게'의 <남은재고>()}"를 출력하자

실행 결과
[결제] 20000원
[배송] 서울시 하리구(으)로 접수
[문자] 주문이 접수됐어요!
재고가 모자라요.
남은 재고: 0

이번에는 키오스크 주문이 “재고가 모자라요”로 막혔고, 재고는 0에서 멈췄어요. 키오스크 쪽에서 재고 확인을 빠뜨릴 수가 없어요. 확인하는 코드가 창구 안에 한 번만 있기 때문이에요.

하나씩 다시 보기

  • 시스템들은 그대로. [재고], [결제기], [배송], [문자]는 바뀌지 않았어요. 퍼사드가 있다는 것도 몰라요.
  • 순서는 창구 한 곳에. 재고 확인 → 재고 빼기 → 결제 → 배송 → 문자 순서가 <주문하기> 안에만 있어요. “문자를 결제 전에” 같은 요청이 오면 여기 두 줄만 바꾸면 돼요.
  • 주문하는 곳은 한 줄. <앱주문>과 <키오스크주문>은 창구에 <주문하기>만 부탁해요. 인자도 여섯 개에서 세 개로 줄었어요.
  • 결과도 알려 줘요. <주문하기>는 주문이 됐는지 참/거짓으로 돌려줘요. 주문하는 곳은 필요하면 이 값만 보고 화면에 알리면 돼요.

창구가 시스템을 완전히 가리나요?
꼭 그렇지는 않아요. 이 예제에서는 시스템들을 숨기자로 넣어서 창구를 통해서만 쓰게 했지만, 퍼사드의 핵심은 “자주 하는 복잡한 일을 쉽게” 만드는 거예요. 드물게 필요한 세세한 일(재고만 따로 채우기 같은)은 시스템을 직접 쓰게 열어 두는 경우도 많아요. 이 예제의 <남은재고>처럼 필요한 것만 창구에 하나씩 더해도 되고요.

새로 주문받는 곳을 더해 보면

“전화 주문”이 생겼다고 해 볼게요.

  • 앞의 방식: 네 시스템을 다루는 순서를 또 한 벌 복사해야 해요. 이번에도 무언가를 빠뜨릴 수 있어요.
  • 퍼사드 패턴: <전화주문>을 만들고 그 안에서 '창구'의 <주문하기>(...)를 한 번 부르면 끝이에요. 순서를 빠뜨릴 방법이 없어요.

직접 해 보기

  1. 위 코드에 <전화주문>을 더해서, 재고가 0인 지금 상태에서 한 개를 주문해 보세요. “재고가 모자라요.“가 나와야 해요.
  2. [주문창구]에 <입고하기>([숫자]인 '개수')를 더해서 재고를 채울 수 있게 만들어 보세요. [재고]에는 이미 <빼기>가 있으니, 비슷하게 <채우기>를 만들면 돼요. 채운 뒤에 다시 주문해 보세요.

언제 쓰면 좋을까요?

  • 한 가지 일을 하려면 여러 객체를 정해진 순서로 다뤄야 할 때
  • 그 순서가 여러 곳에 복사되기 시작할 때
  • 쓰는 쪽이 복잡한 내부를 몰라도 되게 하고 싶을 때 (처음 쓰는 사람, 다른 팀, 패키지를 쓰는 사람)
  • 복잡한 시스템을 바꾸거나 갈아 끼울 때, 바뀌는 곳을 창구 한 곳으로 모으고 싶을 때

좋은 점과 아쉬운 점

좋은 점 아쉬운 점
복잡한 순서가 한곳에만 있어서 빠뜨릴 수 없어요 창구가 이것저것 다 맡다 보면 너무 커질 수 있어요
쓰는 쪽 코드가 짧고 쉬워져요 창구에 없는 세세한 일은 결국 시스템을 직접 써야 해요
시스템이 바뀌어도 창구만 고치면 돼요 창구 하나에 모든 길이 모이면, 창구를 고칠 때 신경 쓸 곳이 많아요
시스템들은 하나도 고치지 않아요

너무 이른 패턴은 독이에요.
주문하는 곳이 한 군데뿐이고 단계도 두세 개라면, 그 자리에서 직접 부르는 게 더 읽기 쉬워요. 같은 순서를 두 번째로 복사하려는 순간이, 또는 “어디선가 한 단계를 빠뜨렸다”는 버그가 처음 나온 순간이 창구를 세울 때예요.

다른 패턴과의 관계

  • 어댑터 패턴과 비교해 보세요. 어댑터는 객체 하나의 모양을 우리 약속에 맞춰 바꾸고, 퍼사드는 여러 객체를 묶어서 더 쉬운 창구를 새로 만들어요.
  • 싱글톤 패턴으로 만들기도 해요. 가게의 주문창구는 보통 하나면 충분해서, 싱글톤으로 두고 앱, 키오스크, 전화가 모두 같은 창구를 쓰게 할 수 있어요.
  • 빌더 패턴과 함께 쓸 수 있어요. 창구 안에서 복잡한 주문서를 빌더로 조립하면, 쓰는 쪽은 여전히 <주문하기> 한 줄만 알면 돼요.
놀이터
결과
코드를 작성하고 '실행' 버튼을 눌러보세요.