PHpullh

C++ · 심층 가이드

C++ 패턴 완전 정리

전략과 옵저버, 데코레이터를 포함한 20개 설계 패턴 예제를 스마트 포인터 소유권과 정적 다형성이라는 C++ 고유의 관점에서 처음부터 다시 씁니다.

주제 20개 · 예제 코드 포함 · 최종 수정 2026-08-30 · 작성 pullh 편집팀

고전 디자인 패턴 책의 예제를 C++로 그대로 옮기면 대개 과하게 무거워집니다. 인터페이스와 가상 호출로만 풀던 문제 중 상당수를 이 언어는 템플릿과 std::function으로 더 얇게 해결하기 때문입니다. 전략을 클래스 계층으로 만들 것인지 호출 가능 객체 하나로 받을 것인지, 다형성을 런타임에 둘 것인지 컴파일 타임에 둘 것인지가 매번 선택지로 남습니다. 여기에 C++만의 축이 하나 더 붙는데, 바로 각 참여 객체를 누가 소유하느냐입니다.

전략 패턴커맨드는 결국 호출 가능 객체를 저장하는 같은 뼈대라 나란히 보면 차이가 선명하고, 데코레이터미들웨어 체인도 감싸는 방향만 다른 한 쌍입니다. 캐시 래퍼retry 유틸은 데코레이터의 실용적인 사례이므로 그 뒤에 이어 두는 편이 자연스럽습니다. 저장소 패턴의존성 주입 컨테이너, 파사드는 앞의 객체지향 편에서 만든 인터페이스 설계를 그대로 확장한 것이니 함께 읽으면 반복이 줄어듭니다.

옵저버는 C++에서 수명 관리로 무너지는 대표적인 패턴입니다. 구독자를 원시 포인터로 담아 둔 채 그 객체가 먼저 소멸하면, 다음 알림에서 이미 없는 대상을 호출하게 됩니다. 구독 해지를 잊지 않는 규율에 기대는 대신 std::weak_ptr로 보관하고 알림 시점에 lock()으로 살아 있는지 확인하는 편이 현실적입니다. 반대로 부모와 자식이 서로 std::shared_ptr을 쥐면 참조 횟수가 0이 되지 않아 조용히 새므로, 역방향 링크는 weak_ptr로 끊어 두어야 합니다.

01설정 객체

C++에서 설정 객체 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 설정 객체
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

02전략 패턴

C++에서 전략 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 전략 패턴
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(2)) cache[2] = "user";
    std::cout << cache[2] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

03옵저버

C++에서 옵저버 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 옵저버
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

04데코레이터

C++에서 데코레이터 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 데코레이터
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(4)) cache[4] = "user";
    std::cout << cache[4] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

05어댑터

C++에서 어댑터 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 어댑터
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

06커맨드

C++에서 커맨드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 커맨드
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(6)) cache[6] = "user";
    std::cout << cache[6] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

07상태 패턴

C++에서 상태 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 상태 패턴
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

08팩터리 메서드

C++에서 팩터리 메서드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 팩터리 메서드
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(8)) cache[8] = "user";
    std::cout << cache[8] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

09빌더 패턴

C++에서 빌더 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 빌더 패턴
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

10캐시 래퍼

C++에서 캐시 래퍼 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 캐시 래퍼
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(10)) cache[10] = "user";
    std::cout << cache[10] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

11retry 유틸

C++에서 retry 유틸 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// retry 유틸
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

12미들웨어 체인

C++에서 미들웨어 체인 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 미들웨어 체인
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(12)) cache[12] = "user";
    std::cout << cache[12] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

13검증기 조합

C++에서 검증기 조합 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 검증기 조합
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

14포매터 파이프

C++에서 포매터 파이프 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 포매터 파이프
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(14)) cache[14] = "user";
    std::cout << cache[14] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

15페이지네이션

C++에서 페이지네이션 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 페이지네이션
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

16데이터 매퍼

C++에서 데이터 매퍼 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 데이터 매퍼
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(16)) cache[16] = "user";
    std::cout << cache[16] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

17저장소 패턴

C++에서 저장소 패턴 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 저장소 패턴
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

18의존성 주입 컨테이너

C++에서 의존성 주입 컨테이너 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 의존성 주입 컨테이너
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(18)) cache[18] = "user";
    std::cout << cache[18] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

19템플릿 메서드

C++에서 템플릿 메서드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 템플릿 메서드
#include <iostream>

int percent(int value) { return static_cast<int>(value * 0.9); }
int flat(int value) { return value - 5; }
int main() {
    auto strategy = percent;
    std::cout << strategy(100) << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

20파사드

C++에서 파사드 개념을 코드로 빠르게 확인하는 패턴 예제입니다.

C++ code

// 파사드
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> cache;
    if (!cache.count(20)) cache[20] = "user";
    std::cout << cache[20] << "\n";
}
알아두면 좋은 점

패턴은 이름보다 책임 분리와 교체 가능성에 주목하면 더 실용적으로 이해됩니다.

자주 하는 실수

패턴 이름만 따라 하고 결합도를 줄이지 못하면 구조가 오히려 더 복잡해집니다.

정리하며

  • 옵저버의 구독자 목록은 weak_ptr로 담고 알림 직전 lock으로 생존을 확인합니다
  • shared_ptr 순환 참조는 누수로 이어지므로 역방향 링크는 weak_ptr로 끊습니다
  • 전략과 커맨드는 클래스 계층 없이 std::function 하나로 충분한 경우가 많습니다
  • 다형성을 런타임 가상 호출로 둘지 템플릿으로 컴파일 타임에 둘지 매번 판단합니다

더 깊이 들어가고 싶다면 C++ 학습 라이브러리에서 다른 주제 가이드를 이어서 보거나, 언어 비교에서 같은 개념이 다른 언어에서 어떻게 표현되는지 확인해 보세요.