[2026] C++ Facade 패턴 완벽 가이드 | 복잡한 서브시스템을 하나의 간단한 인터페이스로
이 글의 핵심
C++ Facade 패턴 : 복잡한 서브시스템을 하나의 간단한 인터페이스로. Facade 패턴이란?. 왜 필요한가·기본 구조.
Facade 패턴이란? 왜 필요한가
여러 서브시스템을 한 얼굴로 묶는 흐름은 구조 패턴 시리즈와 종합 가이드에서 다른 패턴과 대비해 볼 수 있습니다.
문제 시나리오: 복잡한 서브시스템
문제: 비디오 재생을 위해 여러 클래스를 직접 조작해야 합니다. 아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 나쁜 설계: 클라이언트가 모든 세부사항을 알아야 함
int main() {
VideoFile video("movie.mp4");
CodecFactory factory;
Codec* codec = factory.extract(video);
AudioMixer mixer;
mixer.fix(video);
VideoConverter converter;
converter.convert(video, codec);
// 5개 클래스를 직접 조작...
}
해결: Facade 패턴은 복잡한 서브시스템을 하나의 간단한 인터페이스로 감쌉니다. 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// 좋은 설계: Facade
// 타입 정의
class VideoPlayer {
VideoFile video_;
CodecFactory factory_;
AudioMixer mixer_;
VideoConverter converter_;
public:
void play(const std::string& filename) {
video_.load(filename);
auto* codec = factory_.extract(video_);
mixer_.fix(video_);
converter_.convert(video_, codec);
// 내부 복잡성 숨김
}
};
int main() {
VideoPlayer player;
player.play("movie.mp4"); // 간단!
}
아래 코드는 mermaid를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
flowchart LR
client[Client]
facade[Facadebr/(VideoPlayer)]
subsystem1[Subsystem1br/(VideoFile)]
subsystem2[Subsystem2br/(Codec)]
subsystem3[Subsystem3br/(AudioMixer)]
client --> facade
facade --> subsystem1
facade --> subsystem2
facade --> subsystem3
목차
1. 기본 구조
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
#include <string>
// 서브시스템 클래스들 (복잡한 내부)
class Parser {
public:
void parse(const std::string& path) {
std::cout << "Parsing " << path << "...\n";
}
};
class Validator {
public:
bool validate() {
std::cout << "Validating...\n";
return true;
}
};
class Compiler {
public:
void compile() {
std::cout << "Compiling...\n";
}
};
class Linker {
public:
void link() {
std::cout << "Linking...\n";
}
};
// Facade: 하나의 진입점
class BuildPipeline {
Parser parser_;
Validator validator_;
Compiler compiler_;
Linker linker_;
public:
bool build(const std::string& path) {
std::cout << "=== Build Started ===\n";
parser_.parse(path);
if (!validator_.validate()) {
std::cout << "Validation failed!\n";
return false;
}
compiler_.compile();
linker_.link();
std::cout << "=== Build Complete ===\n";
return true;
}
};
int main() {
BuildPipeline pipeline;
if (pipeline.build("src/main.cpp"))
std::cout << "✓ Build OK\n";
return 0;
}
2. 멀티미디어 시스템 예제
비디오 플레이어 Facade
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
#include <string>
#include <memory>
// 서브시스템: 복잡한 비디오 처리
class VideoFile {
std::string filename_;
public:
explicit VideoFile(std::string filename) : filename_(std::move(filename)) {
std::cout << "Loading video: " << filename_ << '\n';
}
std::string getCodecType() const { return "MPEG4"; }
};
class Codec {
public:
virtual ~Codec() = default;
virtual void decode() = 0;
};
class MPEG4Codec : public Codec {
public:
void decode() override { std::cout << "Decoding MPEG4...\n"; }
};
class H264Codec : public Codec {
public:
void decode() override { std::cout << "Decoding H264...\n"; }
};
class CodecFactory {
public:
static std::unique_ptr<Codec> extract(const VideoFile& file) {
if (file.getCodecType() == "MPEG4")
return std::make_unique<MPEG4Codec>();
return std::make_unique<H264Codec>();
}
};
class AudioMixer {
public:
void fix(const VideoFile& file) {
std::cout << "Fixing audio sync...\n";
}
};
class VideoRenderer {
public:
void render() {
std::cout << "Rendering video...\n";
}
};
// Facade: 간단한 인터페이스
class VideoPlayer {
public:
void play(const std::string& filename) {
std::cout << "=== Playing Video ===\n";
VideoFile video(filename);
auto codec = CodecFactory::extract(video);
codec->decode();
AudioMixer mixer;
mixer.fix(video);
VideoRenderer renderer;
renderer.render();
std::cout << "=== Playback Started ===\n";
}
};
int main() {
VideoPlayer player;
player.play("movie.mp4");
return 0;
}
핵심: 클라이언트는 play() 하나만 호출하면 됩니다.
3. 데이터베이스 래퍼
복잡한 DB 연산을 단순화
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
#include <string>
#include <vector>
// 서브시스템: 저수준 DB 연산
class Connection {
public:
void connect(const std::string& host) {
std::cout << "Connecting to " << host << "...\n";
}
void disconnect() {
std::cout << "Disconnecting...\n";
}
};
class Query {
public:
void prepare(const std::string& sql) {
std::cout << "Preparing query: " << sql << '\n';
}
void execute() {
std::cout << "Executing query...\n";
}
};
class ResultSet {
public:
std::vector<std::string> fetch() {
return {"row1", "row2", "row3"};
}
};
class Transaction {
public:
void begin() { std::cout << "BEGIN TRANSACTION\n"; }
void commit() { std::cout << "COMMIT\n"; }
void rollback() { std::cout << "ROLLBACK\n"; }
};
// Facade: 간단한 DB 인터페이스
class Database {
Connection conn_;
Transaction trans_;
public:
void connect(const std::string& host) {
conn_.connect(host);
}
std::vector<std::string> query(const std::string& sql) {
trans_.begin();
try {
Query q;
q.prepare(sql);
q.execute();
ResultSet rs;
auto results = rs.fetch();
trans_.commit();
return results;
} catch (...) {
trans_.rollback();
throw;
}
}
void disconnect() {
conn_.disconnect();
}
};
int main() {
Database db;
db.connect("localhost:5432");
auto results = db.query("SELECT * FROM users");
for (const auto& row : results)
std::cout << "Row: " << row << '\n';
db.disconnect();
return 0;
}
핵심: 트랜잭션, 연결, 쿼리 준비 등 복잡한 과정을 query() 하나로 처리합니다.
4. 자주 발생하는 문제와 해결법
문제 1: Facade가 너무 커짐
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ❌ 나쁜 예: God Object
class SystemFacade {
public:
void doEverything() { /* 100줄 */ }
void doMore() { /* 100줄 */ }
// 20개 메서드...
};
해결: 여러 Facade로 분리하세요. 다음은 간단한 cpp 코드 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ✅ 좋은 예: 책임 분리
class VideoFacade { /* 비디오 관련만 */ };
class AudioFacade { /* 오디오 관련만 */ };
class NetworkFacade { /* 네트워크 관련만 */ };
문제 2: 서브시스템 직접 접근
// ❌ 나쁜 예: Facade 우회
VideoFile video("movie.mp4");
Codec* codec = new MPEG4Codec(); // 직접 접근
해결: Facade만 사용하도록 서브시스템을 private/internal로 만드세요. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
// ✅ 좋은 예: 서브시스템 숨김
namespace internal {
class VideoFile { /* ....*/ };
}
class VideoPlayer { // public API
internal::VideoFile video_;
};
문제 3: 모든 기능 노출 불가
// ❌ 문제: Facade가 고급 기능을 제공하지 않음
player.play("movie.mp4"); // OK
player.setSubtitle("en"); // 없음!
해결: 필요한 고급 기능은 Facade에 추가하거나, 서브시스템 접근자를 제공하세요. 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ✅ 해결 1: 메서드 추가
class VideoPlayer {
public:
void play(const std::string& filename);
void setSubtitle(const std::string& lang); // 추가
};
// ✅ 해결 2: 서브시스템 접근자
class VideoPlayer {
public:
VideoFile& getVideoFile() { return video_; } // 고급 사용자용
};
5. 프로덕션 패턴
패턴 1: Singleton Facade
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class Logger {
Logger() = default;
public:
static Logger& instance() {
static Logger inst;
return inst;
}
void log(const std::string& msg) {
// 복잡한 로깅 시스템 감춤
std::cout << "[LOG] " << msg << '\n';
}
};
// 사용
Logger::instance().log("Application started");
패턴 2: Builder와 조합
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
class VideoPlayerBuilder {
std::string codec_;
bool subtitles_ = false;
public:
VideoPlayerBuilder& setCodec(const std::string& c) { codec_ = c; return *this; }
VideoPlayerBuilder& enableSubtitles() { subtitles_ = true; return *this; }
VideoPlayer build() { return VideoPlayer(codec_, subtitles_); }
};
// 사용
auto player = VideoPlayerBuilder()
.setCodec("H264")
.enableSubtitles()
.build();
6. 완전한 예제: 게임 엔진 초기화
다음은 cpp를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
#include <iostream>
#include <string>
// 서브시스템: 복잡한 게임 엔진 컴포넌트
class GraphicsEngine {
public:
void init() { std::cout << "Graphics: Initializing OpenGL...\n"; }
void setResolution(int w, int h) {
std::cout << "Graphics: Set resolution " << w << "x" << h << '\n';
}
void enableVSync() { std::cout << "Graphics: VSync enabled\n"; }
};
class AudioEngine {
public:
void init() { std::cout << "Audio: Initializing OpenAL...\n"; }
void setVolume(float v) {
std::cout << "Audio: Volume set to " << v << '\n';
}
};
class PhysicsEngine {
public:
void init() { std::cout << "Physics: Initializing Bullet...\n"; }
void setGravity(float g) {
std::cout << "Physics: Gravity set to " << g << '\n';
}
};
class InputManager {
public:
void init() { std::cout << "Input: Initializing SDL...\n"; }
void bindKey(const std::string& key, const std::string& action) {
std::cout << "Input: Bind " << key << " -> " << action << '\n';
}
};
class NetworkManager {
public:
void init() { std::cout << "Network: Initializing sockets...\n"; }
void connect(const std::string& server) {
std::cout << "Network: Connecting to " << server << "...\n";
}
};
// Facade: 게임 엔진 초기화를 하나의 인터페이스로
class GameEngine {
GraphicsEngine graphics_;
AudioEngine audio_;
PhysicsEngine physics_;
InputManager input_;
NetworkManager network_;
public:
void initialize(int width, int height) {
std::cout << "=== Game Engine Initialization ===\n";
graphics_.init();
graphics_.setResolution(width, height);
graphics_.enableVSync();
audio_.init();
audio_.setVolume(0.8f);
physics_.init();
physics_.setGravity(9.8f);
input_.init();
input_.bindKey("W", "MoveForward");
input_.bindKey("S", "MoveBackward");
network_.init();
std::cout << "=== Initialization Complete ===\n";
}
void connectToServer(const std::string& server) {
network_.connect(server);
}
void shutdown() {
std::cout << "=== Shutting Down ===\n";
}
};
int main() {
GameEngine engine;
engine.initialize(1920, 1080);
engine.connectToServer("game.server.com");
std::cout << "\n[Game Running...]\n\n";
engine.shutdown();
return 0;
}
출력: 다음은 code를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
=== Game Engine Initialization ===
Graphics: Initializing OpenGL...
Graphics: Set resolution 1920x1080
Graphics: VSync enabled
Audio: Initializing OpenAL...
Audio: Volume set to 0.8
Physics: Initializing Bullet...
Physics: Gravity set to 9.8
Input: Initializing SDL...
Input: Bind W -> MoveForward
Input: Bind S -> MoveBackward
Network: Initializing sockets...
=== Initialization Complete ===
Network: Connecting to game.server.com...
[Game Running...]
=== Shutting Down ===
정리
| 항목 | 설명 |
|---|---|
| 목적 | 서브시스템에 대한 단순한 진입점 제공 |
| 장점 | 사용처 단순화, 서브시스템 교체·수정 시 영향 범위 축소, 복잡성 숨김 |
| 단점 | 모든 기능을 노출하지는 못함, Facade가 비대해질 수 있음 |
| 사용 시기 | 복잡한 라이브러리 래핑, 멀티 스텝 초기화, 레거시 코드 감싸기 |
| 관련 글: Adapter 패턴, Decorator 패턴, Proxy 패턴, Bridge 패턴. | |
| 한 줄 요약: Facade 패턴으로 복잡한 라이브러리·파이프라인·게임 엔진을 하나의 간단한 인터페이스로 쓸 수 있습니다. |
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ Decorator Pattern 완벽 가이드 | 기능 동적 추가와 조합
- C++ Proxy Pattern 완벽 가이드 | 접근 제어와 지연 로딩
- C++ Bridge 패턴 완벽 가이드 | 구현과 추상화 분리로 확장성 높이기
이 글에서 다루는 키워드 (관련 검색어)
C++, Facade, design pattern, structural, API, simplification, wrapper 등으로 검색하시면 이 글이 도움이 됩니다.
자주 묻는 질문 (FAQ)
Q. 이 내용을 실무에서 언제 쓰나요?
A. C++ Facade 패턴 완벽 가이드. 라이브러리·레거시·여러 클래스를 하나의 진입점으로 감싸 사용을 단순화하는 구조 패턴, 실전 예제, 멀티미디어 시스템, 데이터베이스 래퍼까지. 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.
Q. 선행으로 읽으면 좋은 글은?
A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.