JAVA로 구현하는 알고리즘과 디자인 패턴 공부: 퍼사드 패턴 (Facade)
1. 퍼사드 패턴이란? 🏛️
개념: 복잡한 서브시스템(Subsystem)을 더 쉽게 사용할 수 있도록, 단순하고 통합된 하나의 인터페이스(창구)를 제공하는 패턴입니다. 'Facade'는 프랑스어로 건물의 '정면'을 의미하며, 건물의 복잡한 내부 구조를 감추고 깔끔한 정면만 보여주는 것처럼 동작합니다.
가장 좋은 예는 **홈시어터의 '통합 리모컨'**입니다. 영화를 보기 위해 TV 켜기, 앰프 켜고 모드 바꾸기, DVD 플레이어 켜기, 조명 낮추기 등 여러 기기를 복잡하게 조작해야 합니다. 퍼사드 패턴은 이 모든 과정을 '영화 보기' 버튼 하나로 묶어주는 통합 리모컨과 같습니다. 사용자는 버튼 하나만 누르면 되고, 그 뒤에서 일어나는 복잡한 일들은 알 필요가 없습니다.
2. 왜 사용할까요?
- 단순함: 복잡한 내부 구현을 몰라도, 퍼사드(Facade)가 제공하는 간단한 메서드만 호출하면 되므로 서브시스템을 매우 쉽게 사용할 수 있습니다.
- 결합도 감소: 클라이언트는 퍼사드 객체에만 의존하고, 그 뒤에 있는 수많은 서브시스템 클래스들과는 직접적인 의존 관계가 없어집니다. 덕분에 내부 서브시스템이 변경되더라도 클라이언트 코드는 영향을 받지 않습니다.
- 계층화된 구조: 시스템을 여러 계층으로 나눌 때, 각 계층의 진입점으로 퍼사드를 사용하면 깔끔한 구조를 만들 수 있습니다.
3. Java로 구현하는 방법
시나리오: 위에서 설명한 홈시어터 시스템을 직접 만들어 보겠습니다.
1단계: 복잡한 서브시스템 클래스들 준비
우리가 제어해야 할 복잡하고 다양한 기기들입니다.
// 서브시스템 1: 앰프
class Amplifier {
public void on() { System.out.println("앰프가 켜졌습니다."); }
public void setDvd(DvdPlayer dvd) { System.out.println("앰프를 DVD 플레이어와 연결합니다."); }
public void setSurroundSound() { System.out.println("앰프를 서라운드 사운드로 설정합니다."); }
public void setVolume(int level) { System.out.println("볼륨을 " + level + "로 설정합니다."); }
public void off() { System.out.println("앰프가 꺼졌습니다."); }
}
// 서브시스템 2: DVD 플레이어
class DvdPlayer {
public void on() { System.out.println("DVD 플레이어가 켜졌습니다."); }
public void play(String movie) { System.out.println("'" + movie + "'를 재생합니다."); }
public void stop() { System.out.println("DVD를 멈춥니다."); }
public void off() { System.out.println("DVD 플레이어가 꺼졌습니다."); }
}
// 서브시스템 3: 프로젝터
class Projector {
public void on() { System.out.println("프로젝터가 켜졌습니다."); }
public void wideScreenMode() { System.out.println("프로젝터를 와이드 스크린 모드로 설정합니다."); }
public void off() { System.out.println("프로젝터가 꺼졌습니다."); }
}
2단계: 퍼사드(Facade) 클래스 구현
이 패턴의 핵심입니다. 퍼사드 클래스는 내부에 모든 서브시스템 객체들을 멤버 변수로 가지고, '영화 보기', '영화 끄기' 같은 단순화된 통합 메서드를 제공합니다.
public class HomeTheaterFacade {
// 퍼사드는 제어해야 할 모든 서브시스템을 알고 있다.
private Amplifier amp;
private DvdPlayer dvd;
private Projector projector;
public HomeTheaterFacade(Amplifier amp, DvdPlayer dvd, Projector projector) {
this.amp = amp;
this.dvd = dvd;
this.projector = projector;
}
// '영화 보기' 버튼에 해당하는 통합 메서드
// 복잡한 과정들을 순서대로 처리해준다.
public void watchMovie(String movie) {
System.out.println("=== 영화 볼 준비를 시작합니다... ===");
projector.on();
projector.wideScreenMode();
amp.on();
amp.setDvd(dvd);
amp.setSurroundSound();
amp.setVolume(5);
dvd.on();
dvd.play(movie);
}
// '영화 끄기' 버튼에 해당하는 통합 메서드
public void endMovie() {
System.out.println("\n=== 홈시어터를 종료합니다... ===");
dvd.stop();
dvd.off();
amp.off();
projector.off();
}
}
3단계: 클라이언트 코드에서 퍼사드 사용하기
클라이언트는 이제 각 기기를 어떻게 켜고 설정하는지 알 필요 없이, 퍼사드가 제공하는 간단한 메서드만 호출하면 됩니다.
public class Client {
public static void main(String[] args) {
// 1. 제어할 서브시스템 객체들을 생성
Amplifier amp = new Amplifier();
DvdPlayer dvd = new DvdPlayer();
Projector projector = new Projector();
// 2. 서브시스템들을 통합 관리하는 퍼사드 객체 생성
HomeTheaterFacade homeTheater = new HomeTheaterFacade(amp, dvd, projector);
// 3. 클라이언트는 '영화 보기' 메서드 하나만 호출하면 된다.
homeTheater.watchMovie("인셉션");
// 4. '영화 끄기' 메서드 하나만 호출하면 된다.
homeTheater.endMovie();
}
}
4. 퍼사드 패턴과 실제 사례
- SLF4J (Simple Logging Facade for Java): 이름부터 'Facade'가 들어간 대표적인 예입니다. SLF4J는 Logback, Log4j, java.util.logging 등 다양한 로깅 구현체들의 복잡한 설정과 API를 Logger라는 단일한 인터페이스 뒤로 감춰줍니다. 개발자는 SLF4J의 간단한 API만 사용하면 되고, 실제 로깅 구현체는 자유롭게 교체할 수 있습니다.
- 스프링 JdbcTemplate: 자바의 표준 데이터베이스 연결 API인 JDBC는 Connection, Statement, ResultSet 관리, 예외 처리 등 매우 복잡하고 반복적인 코드가 필요합니다. 스프링의 JdbcTemplate은 이 모든 복잡한 과정을 내부적으로 처리해주고, 개발자에게는 query(), update() 같은 단순한 메서드만 제공하는 퍼사드 역할을 합니다.
5. 문제 제시 및 답변 💡
문제: 컴퓨터를 켜는 과정을 생각해보세요. 전원 버튼을 누르면 내부적으로는 CPU가 활성화되고, 메모리가 자체 테스트를 수행하며, 하드 드라이브가 부팅 섹터를 읽는 등 여러 복잡한 과정이 순차적으로 일어납니다.
이 '컴퓨터 부팅 과정'을 퍼사드 패턴을 적용하여 설계해 보세요. 어떤 클래스들이 서브시스템이 되고, 퍼사드 클래스는 어떤 모습일까요?
답변:
퍼사드 패턴을 적용하기에 아주 좋은 예입니다.
- 서브시스템:
- CPU 클래스: freeze(), jump(), execute() 같은 메서드를 가짐.
- Memory 클래스: load(bootAddress, data) 같은 메서드를 가짐.
- HardDrive 클래스: read(bootSector) 같은 메서드를 가짐.
- 퍼사드 클래스: ComputerFacade
- 이 클래스는 내부에 CPU, Memory, HardDrive 객체를 가지고 있습니다.
- start()라는 공개 메서드 하나를 제공합니다.
- start() 메서드 내부에서 hardDrive.read(), memory.load(), cpu.jump(), cpu.execute() 같은 복잡한 서브시스템 메서드들을 정해진 순서대로 호출하여 부팅 과정을 처리합니다.
**사용자(클라이언트)**는 ComputerFacade 객체의 start() 메서드만 호출하면 되고, CPU, 메모리, 하드 드라이브의 복잡한 상호작용에 대해서는 전혀 신경 쓸 필요가 없습니다.
'프로그래밍 > 알고리즘&자료구조&패턴' 카테고리의 다른 글
| 디자인패턴 - 컴포지트 패턴 (2) | 2025.08.18 |
|---|---|
| 디자인패턴 - 프록시 패턴 (1) | 2025.08.18 |
| 디자인패턴 - 데코레이터 패턴 (1) | 2025.08.18 |
| 디자인 패턴 - 어댑터 패턴 (1) | 2025.08.18 |
| 디자인패턴 - 빌더패턴 (1) | 2025.08.18 |