
Facade 패턴에서 Facade란 ”건물의 정면”을 의미하는 용어이며, 소프트웨어 공학 디자인 패턴 중 구조 패턴에 속한다.
출처 : https://ko.wikipedia.org/wiki/퍼사드_패턴
Facade 패턴은 복잡한 시스템을 단순화 하고, 이러한 시스템을 사용하는 클라이언트를 위해 간단한 인터페이스를 제공하는 디자인 패턴이다.
이 패턴을 통해서 클라이언트는 복잡한 내부 구성 요소를 알 필요 없이, 단순화된 인터페이스만을 통해 기능을 사용할 수 있다.
예를 들어 서브 시스템들이 아래와 같이 있다고 하자.
class CPU {
public void start() {
System.out.println("CPU started.");
}
public void shutdown() {
System.out.println("CPU shut down.");
}
}
class Memory {
public void load() {
System.out.println("Memory loaded.");
}
public void release() {
System.out.println("Memory released.");
}
}
class HardDrive {
public void readData() {
System.out.println("Hard drive reading data.");
}
public void writeData() {
System.out.println("Hard drive writing data.");
}
}
클라이언트가 만약 Facade 패턴 없이 서브 시스템을 일일이 호출 하여 하나의 로직을 만들어 나가고자 하면 복잡성은 매우 올라갈 것이다.
public class Client {
public static void main(String[] args) {
// 클라이언트가 모든 서브시스템에 직접 접근해야 함
CPU cpu = new CPU();
Memory memory = new Memory();
HardDrive hardDrive = new HardDrive();
// 컴퓨터를 시작할 때
cpu.start(); // CPU 시작
memory.load(); // 메모리 로드
hardDrive.readData(); // 하드 드라이브에서 데이터 읽기
System.out.println("Computer started.");
// 컴퓨터를 종료할 때
hardDrive.writeData(); // 하드 드라이브에 데이터 쓰기
memory.release(); // 메모리 해제
cpu.shutdown(); // CPU 종료
System.out.println("Computer shut down.");
}
}
이러한 서브 시스템을 일일이 호출하는 방법은 각 서브 시스템에 대해서 높은 이해도를 필수로 요구하며, 올바른 순서로 메서드를 호출 해야만 한다.
또한 서브 시스템 내부가 변경 될 경우에는 이와 상호작용하는 클라이언트의 코드 또한 수정 되어야 할 가능성이 높다.
Facade 패턴은 클라이언트에게 인터페이스를 제공하여 서브 시스템의 복잡한 로직을 알 필요 없게 한다.

위 diagram을 보면, Client는 Facade class만을 의존하고 있다.
Facade class는 각 서브 시스템(Interne1, Interne2, Interne3)을 의존하여 메서드를 호출해 복잡한 기능을 구현하고, 이를 Client에게 Interface로서 제공한다.
여기서 Interface란 Java의 Interface 개념이 아닌, Client가 시스템을 사용할 수 있게 하는 일종의 매개체를 뜻한다.
// Facade 클래스
class ComputerFacade {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public ComputerFacade() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}
public void startComputer() {
cpu.start();
memory.load();
hardDrive.readData();
System.out.println("Computer started.");
}
public void shutdownComputer() {
cpu.shutdown();
memory.release();
hardDrive.writeData();
System.out.println("Computer shut down.");
}
}
위 코드는 Facade 클래스이다.
Diagram에서 설명된 것 처럼 서브 시스템인 CPU, Memory, HardDrive 클래스들을 의존한다.
그리고 이러한 서브 시스템들을 이용해 복잡한 로직인 startComputer(), shutdownComputer() 메서드로 Client에게 제공한다.
이를 통해 Client는 서브 시스템에 대한 깊은 이해도 없이, Facade pattern을 통해서 제공된 interface 만으로도 아래와 같이 사용할 수 있다.
public class Client {
public static void main(String[] args) {
// 복잡한 서브시스템을 Facade 패턴으로 단순화
ComputerFacade computer = new ComputerFacade();
// 컴퓨터를 시작할 때
computer.startComputer();
// 컴퓨터를 종료할 때
computer.shutdownComputer();
}
}
Facade 패턴을 공부하게 된 이유로는 Service 계층간 호출로 인해 복잡성이 올라가고, 결합도가 매우 높아져 이를 통해 유지 보수 및 가독성이 심히 떨어졌기 때문이다.
위 Facade 예시와 같이 로직을 이해 하기 위해서는 반드시 각 Service들에 대한 깊은 이해를 요구 했고, 각 Service의 서비스 호출 순서도 매우 중요했다.
이러한 부수적인 영향들은 개발의 피로도를 높였고, Facade 패턴을 도입하고자 하는 결정적인 이유들이 되었다.
예를 들어 아래의 코드가 있다고 해보자.
@Service
public class UserService {
@Autowired
private NotificationService notificationService;
@Autowired
private PaymentService paymentService;
public void registerUser(User user) {
// 사용자 등록 로직
System.out.println("User registered: " + user.getName());
// 등록 후 알림 전송
notificationService.sendWelcomeEmail(user);
// 등록 후 결제 처리
paymentService.processPayment(user);
}
}
UserService에서는 NotificationService와 PaymentService를 의존하고 있으며, registerUser() 메서드에서 두 의존 중인 메서드를 호출하여 로직을 구성하고 있다.
이 때 UserService에서는 NotificationService와 PaymentService에 직접적으로 의존 하고 있어 서비스간 결합이 높아졌다.
이러한 문제는 NotificationService나 PaymentService, 의존하고 있는 Service 메서드의 시그니쳐나 내부 로직이 변경 될 경우 UserService의 registerUser() 메서드 또한 변경 될 가능성이 매우 크다는 것이다.
고작 2개의 Service만 의존하고 있어 감이 잘 안온다면, 의존하고 있는 Service의 갯수를 10개 이상이라 생각 해보자.
끔찍 하지 않은가.
추가적으로 중요한 점은 UserService의 registerUser()는 유저를 등록 시키는 책임을 가지고 있다.
그러나 추가적으로 등록 후 알림 전송과 등록 후 결제 처리라는 책임 또한 가지고 있어 SRP를 어기게 되었다.
이러한 문제들을 Facade 패턴을 사용하여 해결해보자.
@Service
public class UserServiceFacade {
@Autowired
private UserService userService;
@Autowired
private NotificationService notificationService;
@Autowired
private PaymentService paymentService;
public void registerUser(User user) {
// 사용자 등록
userService.registerUser(user);
// 등록 후 다른 서비스 처리
notificationService.sendWelcomeEmail(user);
paymentService.processPayment(user);
}
}
UserServiceFacade 클래스에서는 서브 시스템인 UserService, NotificationService, PaymentService를 의존하여 Client에게 registerUser()라는 인터페이스를 제공하고 있다.
이를 통해 userService.registerUser()의 SRP 위반 문제와 각 메서드의 변경으로 인한 부수적인 영향도 줄이는 효과를 얻게 되었다.
파이팅입니다~!!