티스토리 뷰

자바/개념

[디자인 패턴] Facade 패턴

다음김 2023. 12. 17. 21:26

 

이번 글에서는 GoF Design Patterns 중 하나인 Facade 패턴에 대해 알아보고자 한다. 개발을 하면서 누군가는 자신도 모르게 이 Facade 패턴을 적용해본 적이 있을 것이다. 굳이 어렵게 Facade 라는 이름을 붙였어야 했나 싶을 정도로 간단한 패턴이다. 그럼 자세히 알아보자.

 

 

Facade 패턴이란?

Facade is a structural design pattern that provides a simplified interface to a library, a framework, or any other complex set of classes.

 

Facade는 구조 디자인 패턴으로 복잡한 클래스들의 사용에 대한 간단한 인터페이스를 제공한다고 한다. 다시 말해 클라이언트가 A, B, C 클래스의 각 메소드를 직접 호출하던 기존 방식에 Facade 패턴을 적용하면 Facade 메소드 하나만을 호출함으로써 동일한 동작을 수행할 수 있다는 것이다. 따라서 클라이언트는 A, B, C 클래스의 복잡한 구조나 구현 상세를 모르더라도 Facade를 통해 비교적 간단하게 동일한 기능을 제공받을 수 있다.

*Structural Design Pattern: 프로젝트의 구조를 더 유연하고 효율적으로 유지하도록 객체와 클래스들을 조립하는 설계 방법

 

 

여기서 Facade가 하는 역할은 크게 2가지로 정리할 수 있다.

 

1. 복잡한 하위 시스템을 간단한 인터페이스를 통해 캡슐화

여기서 말하는 하위 시스템이란 위의 A, B, C 와 같은 사용자 정의 클래스이거나 외부 라이브러리일 수 있다. 클라이언트는 이러한 하위 시스템을 직접 호출하거나 의존하지 않고 Facade를 통해 접근한다. 즉 Facade는 하위 시스템들의 복잡성을 숨겨주어 클라이언트가 사용하기 쉽게 만들어준다.

 

2. 클라이언트와 하위 시스템 간의 Decoupling

클라이언트는 Facade라는 간단한 인터페이스를 통해서만 하위 시스템에 접근하기 때문에 클라이언트 구현과 하위 시스템 구현이 상호 의존적이지 않게 되는 Decoupling이 발생한다. 따라서 하위 시스템에 변경이 발생하더라도 클라이언트 코드에는 영향을 미치치 않는다.

 

Facade가 클라이언트와 하위 시스템 사이의 중개자로서 동작한다는 점에서 Mediator 패턴의 Mediator와 비슷한 역할을 한다고 볼 수 있다. 그러나 하위 시스템은 Facade를 인식하지 못하며, 하위 시스템 간에는 직접 접근할 수 있다는 점에서 Mediator 패턴과의 확연한 차이점을 보인다. (자세한 내용은 추후 Mediator 패턴에서 정리.)

 

 

Facade의 의미?

Facade 패턴의 구성요소에 대해 자세히 알아보기 전 Facade 라는 단어의 의미에 대해 한번 짚고 넘어가자. Facade 라는 단어의 의미는 the face of the building으로 정의된다. 즉 길거리의 행인들은 오직 건물의 표면만을 보고 건물 내부의 구조나 상세한 디자인 등은 알지 못하듯이, Facade를 사용하는 클라이언트 또한 Facade가 제공하는 인터페이스만을 알고 있지 실제 내부적으로는 어떤 클래스들을 의존하며, 어떤 순서로 각 클래스의 메소드를 호출하는지는 알지 못하는 것을 말한다. 

 

'클라이언트는 구현 상세를 알지 못한다' 라는 대목에서 그럼 Facade는 자바의 인터페이스와 비슷한 것이 아닌가? 라는 의문이 들 수 있다. 그러나 일단 Facade는 interface가 아닌 class로 정의되며, 자바의 인터페이스는 다형성을 구현하는 것에 조금더 초점을 맞춘 반면, Facade는 다형성 보다는 내부적으로 의존하고 있는 클래스들의 복잡성을 외부로부터 숨기는 것 즉 캡슐화에 중점을 둔다.

 

예를 들어 출발지 A에서 도착지 B로 가는 경로를 인터페이스로 정의한 경우, 실제 구현 객체에 따라 버스, 기차 또는 택시를 타고 갈 수도 있다. 즉 AtoBRoute 인터페이스를 구현한 실제 클래스들 중 어떤 객체를 사용하느냐에 따라 교통 수단이 달라질 수 있다.

public interface AtoBRoute {

    public go();
}

public class BusRoute implements AtoBRoute {

    public go() {
        System.out.println("Go by bus from A to B");
    }
}

public class TrainRoute implements AtoBRoute {

    public go() {
        System.out.println("Go by train from A to B");
    }
}

public class TaxiRoute implements AtoBRoute {

    public go() {
        System.out.println("Go by taxi from A to B");
    }
}

 

다음은 좀 더 복잡한 상황으로 버스 → 기차 → 택시 → 도보의 순서로 가야야지만 B에 도착할 수 있다고 해보자. A에서 B로 가는 방법이 오직 1가지인데 이 A에서 B로 가는 경로의 복잡성을 외부로부터 숨기기 위해 Facade를 사용할 수 있다. 즉 Facade는 단순히 여러 클래스들을 의존 및 조합하여 이러한 복잡성을 외부로부터 숨기는 것에 초점을 맞춘다. 다시 말해 Facade의 메소드는 현재 의존하고 있는 여러 객체들의 메소드를 특정 순서로 호출하여 좀더 절차 지향적으로 구성된다고 할 수 있다.

public class AtoBRoute {

    private BusRoute busRoute = new BusRoute();
    private TrainRoute trainRoute = new TrainRoute();
    private TaxiRoute taxiRoute = new TaxiRoute();
    private WalkRoute walkRoute = new WalkRoute();

    public void go() {
    	busRoute.go();
        trainRoute.go();
        taxiRoute.go();
        walkRoute.go();
    }
}

public class BusRoute {

    public void go() {
    	System.out.println("Go by bus");
    }
}

public class TrainRoute {

    public void go() {
    	System.out.println("Go by train");
    }
}

public class TaxiRoute {

    public void go() {
    	System.out.println("Go by taxi");
    }
}

public class WalkRoute {

    public void go() {
    	System.out.println("Go by walk");
    }
}

 

 

구성 요소

Facade 패턴은 크게 Client, Facade, Subsystems로 구성된다. 위에서 한 설명으로도 충분하지만 각 요소의 기능과 역할을 정리해보자.

Client

Facade를 통해 Subsystems에 접근한다. 물론 직접 Subsystem을 호출할 수도 있다.

 

Facade + Additional Facade

복잡하고 상호 의존적인 Subsystems에 대한 간단한 접근 방법, 즉 인터페이스를 클라이언트에게 제공한다. 또한 Subsystem의 일부 기능만을 클라이언트에게 제공하여 Subsystem에 대한 클라이언트의 무분별한 접근을 제한할 수도 있다. SRP 원칙에 따라 Facade 자체를 여러 개의 Facade로 나눌 수도 있으며, 이 나눠진 Additional Facade는 클라이언트 또는 다른 Facade에 의해 호출된다. 

 

Subsystems

다양한 기능을 하는 클래스들로 구성되며 Subsystems 간 의존성이 있을 수도 있다. 이 Subsystems는 Facade의 존재를 알지 못하며 따라서 Facade 없이 클라이언트가 직접 Subsystem에 접근할 수도 있다.

 

정리하자면 각 요소 간 의존성은 Client → Facade → Subsystems 방향으로만 발생한다. 

 

 

 

언제 사용? 왜 사용?

적용 상황 정의

그런 이 Facade 패턴은 어떤 상황에서 사용해야 효과적일까? 프로젝트에서 상호의존적인 클래스가 매우 많으며 특정 순서로 각 클래스의 메소드를 호출해야 할 때, 그리고 그 의존하고 있는 클래스의 일부 기능만을 필요로 하는 경우에 Facade 패턴을 적용할 수 있다. 예를 들어 아래와 같이 Main 클래스에서 A, B, C 클래스를 내부 필드로 의존하면서 process 메소드에서는 각 클래스의 메소드인 a, b, c1 메소드를 순서대로 호출하고 있다면 이에 Facade 패턴을 적용할 수 있다. 

public class Main {

    private A a = new A();
    private B b = new B();
    private C c = new C();
    
    public void process() {
        a.a();
        b.c();
        c.c1();
    }
}

public class A {

    public void a() {}
}

public class B {

    public void b() {}
}

public class C {

    public void c1() {}
    public void c2() {}
}

 

Facade 패턴을 적용한 결과는 다음과 같다. Main 클래스의 코드가 훨씬 깔끔해진 것을 알 수 있다. 만약 Main 클래스 외에 동일한 기능을 제공받고자 하는 클래스가 또 존재한다면 클래스 내부에서 Facade 객체를 생성하여 process 메소드를 호출해주기만 하면 된다. 즉 A, B, C 객체를 직접 생성하고 a, b, c1 메소드도 직접 호출할 필요가 없다.

public class Main {

    private Facade facade = new Facade();
    
    public void process() {
        facade.process();
    }
}

public class Facade {

    private A a;
    private B b;
    private C c;
    
    public void process() {
        a.a();
        b.c();
        c.c1();
    }
}

public class A {

    public void a() {}
}

public class B {

    public void b() {}
}

public class C {

    public void c1() {}
    public void c2() {}
}

 

이외에도 Facade 패턴을 통해 얻을 수 있는 장점과 단점은 다음과 같다. 

장점 및 단점

1. Subsystems의 복잡성을 Client로부터 숨길 수 있음
2. Client - Subsystems 간의 Decoupling

3. 중복 코드 감소 및 코드 재사용성 증가

 

단점으로는 Facade가 God Object가 될 수 있다는 것이다. 즉 Facade가 너무 많은 클래스를 의존하여 SRP 원칙을 위반하게 된다. 이 경우에는 Additional Facade를 별도로 정의하여 여러 개의 Facade로 분리해줌으로써 문제를 해결할 수 있다.  

 

 

 

Facade 패턴 적용 예제

지금까지 Facade 패턴 자체에 대해서 알아보았다. 실제 문제 상황을 정의해보고 이를 코드를 구현해봄으로써 패턴에 대해 깊이 이해할 수 있을 것이다. 코드 구현에 앞서 문제 상황을 정의해보자.

 

 

요즘에는 키오스크가 없는 식당을 찾기 어려울 만큼 키오스크가 매우 보편화되었다. 누군가는 키오스크라는 시스템을 손쉽게 사용할 수 있지만 또 누군가에게는 키오스크가 두려운 존재일 수 있다. 따라서 어느 한 식당에서 이러한 키오스크 사용이 어려운 손님들을 위해 (합리적이지는 않지만) 키오스크 사용을 돕는 알바생을 고용했다고 가정하자. 키오스크 사용이 어려운 손님은 이 알바생에게 원하는 메뉴와 카드 또는 현금을 전달하면 알바생이 대신 키오스크를 사용해 주문을 생성해준다.

 

코드 구현

키오스크를 직접 사용하는 손님

다음은 패스트푸드점에 식사하러 들어온 손님이 수행하는 동작을 코드로 구현한 것이다. Kiosk 클래스 구현은 그다지 중요하지 않다고 생각하여 생략하였으며, 처음 Client 클래스의 makeOrder 메소드와 다음 Client 클래스의 makeOrder 메소드 간의 차이점에 주목하면 좋을 것 같다. 아래 Client는 키오스크의 사용법을 알고 있어 직접 Kiosk 객체에 의존하여 주문을 생성하고 있다.

public class Client {

    private Kiosk kiosk = new Kiosk();
    
    public void makeOrder() {
        kiosk.selectMenu(new Menu("빅맥"));
        kiosk.selectHereOrToGo();
        kiosk.selectPaymentMethod();
        kiosk.pay(getCard());
    }
}

 

알바생을 통해 주문하는 손님

반면 아래 ClientKiosk가 아닌 KioskPartTimer에 의존하여 Kiosk의 사용법을 알지 못해도 주문을 생성할 수 있다. 즉 여기서 KioskPartTimer는 Facade 로서 동작하는 것이다.

public class Cient {

    private KioskPartTimer kioskPartTimer;
    
    public void makeOrder() {
        kioskPartTimer.service(new Menu("빅맥"), getCard());
    }
}

public class KioskPartTimer() {

    private Kiosk kiosk;
    
    public void service(Menu menu, Card card) {
        kiosk.selectMenu(menu);
        kiosk.selectHereOrToGo();
        kiosk.selectPaymentMethod();
        kiosk.pay(card);
    }
}

 

KioskPartTimer에 의존하는 손님은 키오스크의 사용법을 알지 못해도 되며 만약 Kiosk의 사용법이 바뀐다 하더라도 Client 코드는 변경되지 않고 오직 KioskPartTimer 클래스만을 변경해주면 된다. 이것이 바로 Facade 패턴의 장점이다. 참고로 Kiosk 클래스 또한 하나의 Facade로서 동작한다고 볼 수도 있는데, 키오스크는 복잡한 주문 시스템, 결제 시스템, 할인 시스템 등에 의존하며 이를 외부로부터 숨기기 때문이다. 따라서 손님은 키오스크가 제공하는 단순한? 인터페이스를 통해 주문을 생성할 수 있는 것이다.

 

 

 

정리

Facade 패턴에서 기억할 중요 키워드는 Hide ComplexityDecoupling이다. 즉 긴밀히 연결되어 있던 두 계층 사이에 Facade를 끼워넣어 하위 계층의 복잡성을 숨겨주며 기존의 긴밀한 연결도 끊어주게 되는 것이다.

이 Facade 패턴은 Layered Architecture인 Contoller - Service - Repository 계층 간에도 적용할 수 있다. 다음 글에서는 Spring에서 Controller - Service 사이에 Facade 계층을 추가하여 리팩토링한 경험을 정리해보고자 한다.

 

 

 

끝.

 

 

 

참고

https://refactoring.guru/design-patterns/facade

https://www.geeksforgeeks.org/facade-design-pattern-introduction/

https://www.baeldung.com/java-facade-pattern

https://www.tutorialspoint.com/design_pattern/facade_pattern.htm

https://medium.com/@andreaspoyias/design-patterns-a-quick-guide-to-facade-pattern-16e3d2f1bfb6

https://medium.com/@juhaeradittya2000/facade-design-pattern-an-intuitive-overview-6be39e2b4413

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함