한 줄 정의
디자인 패턴 의사결정 트리는 단순 암기식 패턴 도입 대신 해결하려는 구조적 병목(Friction)을 3대 질문 경로(생성, 구조, 행동)로 명확히 진단하여 오버엔지니어링을 차단하고 최적의 디자인 패턴을 선택하게 돕는 의사결정 가이드다raw/Stop Memorizing Design Patterns. Use This Decision Tree Instead.md#L27.
핵심 요지
- 패턴 남용과 오용 방지: 디자인 패턴은 그 자체가 틀려서 실패하기보다, 적용하지 말아야 할 때 남용하거나 근본 문제를 정의하지 않는 회피 수단으로 쓰일 때 실패한다.
- 비용 통제 수단: 패턴은 유연성의 비용을 무한정 지불하는 것이 아니라, 통제 가능한 특정 지점으로 비용을 모아서 지불하는 아키텍처적 거래(Trade-off)다.
- 3대 핵심 진단 경로: ‘고통이 어디서 비롯되는가?‘라는 질문에 따라 세 경로로 좁힌다raw/Stop Memorizing Design Patterns. Use This Decision Tree Instead.md#L27.
- 객체 생성의 복잡성 ➡️ 생성 패턴 (Creational)
- 객체 결합 및 경계 조율의 모호함 ➡️ 구조 패턴 (Structural)
- 상황과 정책 변화에 따른 동작 분기 ➡️ 행동 패턴 (Behavioral)
상세
1. 객체 생성 경로 (Creational Patterns)
생성자 파라미터 폭증, 동적 인스턴스화, 프리셋 관리 등의 문제가 있을 때 진입한다.
- Singleton: 정말 딱 하나의 인스턴스만 필요하며 상태가 거의 없는(Stateless) 공유 객체에만 제한적으로 사용한다. 단순히 ‘어디서나 접근하기 쉽다’는 이유로 싱글톤을 남용하면 테스트 격리를 무너뜨린다.
- Builder: 생성자에 선택 파라미터가 누적되거나 오용 우려가 클 때 사용한다. 객체 생성 조건을 규정하고 검증을 사전에 처리한다.
- Factory Method: 상위 추상 클래스가 규격(Contract)만 정의하고, 실질적 인스턴스화는 서브클래스에 위임할 때 사용한다.
- Abstract Factory: 서로 연관되어 함께 동작해야 하는 객체군을 세트로 생성할 때 적합하다.
- Prototype: 초기화 리소스가 큰 객체에 대해 기존에 세팅이 끝난 복제본(Cloning)을 만드는 것이 유리할 때 쓴다.
2. 객체 구조화 경로 (Structural Patterns)
컴포넌트의 경계가 모호하여 결합도가 높거나 인터페이스가 얽혀있을 때 진입한다.
- Adapter: 호환되지 않는 인터페이스를 연결하여 비즈니스 도메인이 외부 규격에 오염되지 않도록 변환(Translation)만을 전담시킨다. 어댑터 내부에 비즈니스 규칙이 섞여서는 안 된다.
- Facade: 저수준 하위 시스템의 여러 기능을 순차 실행해야 할 때 단일 진입점만 노출하여 오용을 차단한다.
- Decorator: 상속 구조의 팽창 없이 로깅/암호화/캐싱 같은 선택적 기능을 조합할 때 사용한다. 각 래퍼는 독립적으로 동작해야 하며, 서로의 상태에 의존하면 추론 지옥이 된다.
- Proxy: 지연 로딩, 권한 제어, 캐싱, 원격 통신 등을 위해 대리자 객체를 둘 때 활용한다.
- Composite: 파일 시스템이나 UI 트리처럼 계층 구조의 노드와 컨테이너를 일관되게 처리할 때 적합하다.
- Flyweight: 동일 데이터를 가진 수많은 객체의 메모리 낭비를 줄이기 위해 공유 상태를 참조하게 한다.
- Bridge: 구조의 수정 방향이 두 축으로 뻗어 나갈 때 상속의 행렬 폭발을 막도록 추상화와 구현을 독립 분리한다.
3. 행동 및 흐름 제어 경로 (Behavioral Patterns)
비즈니스 로직에 조건 분기(if-else)가 중복 누적되거나 워크플로우 정책이 동적으로 바뀔 때 선택한다.
- Chain of Responsibility: 요청이 미들웨어 성격의 단계들을 통과하며 처리를 중단하거나 다음으로 넘기는 흐름에 적합하다.
- Command: 액션 자체를 객체화하여 큐 관리, 로깅, 재시도, 실행 취소(Undo) 인프라를 구축한다.
- Strategy: 동일 역할의 알고리즘 구현체를 동적으로 전환할 때 쓴다. 호출부는 수정하지 않고 벤더/알고리즘을 교체한다.
조건이 X이면 A를 수행하라는 식의 분기 로직이 코드 및 테스트 셋업에 반복될 때 도입하는 실무 시그널이 있다. - State: 명확한 라이프사이클이나 단계 변화가 존재하여, 상태별 동작을 별도 객체로 정의할 때 사용한다.
- Observer: 구독 방식의 일대다 전파 구조를 설계할 때 활용하되, 남용 시 흐름 파악이 어려워지므로 부수 효과에 유의해야 한다.
- Memento: 캡슐화를 훼손하지 않고 상태 스냅샷을 저장해 롤백이나 실행 취소를 지원한다.
- Mediator: 복잡한 UI 상호작용 등 객체 간 직접 의존을 완화할 때 쓴다. 중재자의 책임 범위를 좁게 한정해야 비대한 거대 클래스(God Class)화를 막을 수 있다.
- Visitor: 데이터 구조는 안정적이나, 해당 구조를 순회하며 실행할 분석/연산이 계속 추가될 때 쓴다.
예시
- 알림 서비스 설계: 이메일, SMS 등 채널이 지속 늘어날 때
NotificationChannel전략을 분리하고, 사용자 요금제나 국가 피처 플래그 설정에 따라 동적으로 Strategy를 선택하도록 구성한다. - 인증 파이프라인:
속도 제한 ➡️ 인증 ➡️ 정합성 검증과 같이 순차 필터링이 필요할 때 Chain of Responsibility를 활용해 가볍게 쪼갠 후 테스트한다.
충돌
- 전용 헬퍼 스크립트 vs 과도한 추상화: 간단한 분기 로직이나 일회성 생성자 초기화에 무조건 디자인 패턴의 추상 인터페이스 레이어를 도입하는 것은 Vibe coding 단계의 빠른 변경을 방해하는 기술 부채가 된다. pain point(가려운 곳)가 지속적인 중복 테스트 코드 셋업이나 수십 개의 파일 연쇄 수정 등의 구체적인 비용 손실로 입증될 때만 도입해야 한다.