Behavioral
Behavioral Design Patterns 는 객체 간 상호작용 방식과 책임 배분 전략을 설계하는 11 가지의 패턴 집합이다. 대표적으로 Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor, Interpreter가 있으며, 각 패턴은 응집도 높은 메시징, 실행 위임, 상태 기반 제어, 알고리즘 분리 등 동작 중심 설계 문제를 해결한다. 실무에서는 이벤트 처리, 명령 큐, 상태 머신, GUI 로직 등에서 널리 활용된다.
배경
GoF(“Gang of Four”) 에서 정의한 23 개 패턴 중 하나로, 객체 간의 협력과 메시지 전달을 정형화하기 위해 등장했다.
문제 상황:
- 객체 간 복잡한 상호작용, 행위의 동적 변경, 알고리즘 분리 필요성 등에서 기존 구조로는 유연성과 확장성이 부족하다.
해결 방안:
- 행위 패턴을 통해 객체 간 상호작용을 구조화하고, 행위를 캡슐화하여 동적으로 관리할 수 있게 한다.
목적 및 필요성
주요 목적:
- 느슨한 결합 (Loose Coupling): 객체 간 직접적인 의존성을 최소화
- 책임 분산: 복잡한 작업을 여러 객체에 적절히 분배
- 유연한 협력: 런타임에 객체 간 상호작용 방식 변경 가능
- 코드 재사용성: 검증된 상호작용 패턴의 재활용
필요성:
- 객체지향 시스템에서 객체 간 복잡한 상호작용 관리
- 시스템 확장성과 유지보수성 향상
- 비즈니스 로직의 변화에 대한 유연한 대응
핵심 개념
행위 패턴 (Behavioral Pattern) 은 객체 간 상호작용, 알고리즘 분리, 행위의 캡슐화 등을 통해 시스템의 유연성과 확장성을 높이는 GoF 디자인 패턴의 한 분류이다.
기본 개념
객체 간 커뮤니케이션 (Object Communication)
- 객체들이 어떻게 메시지를 주고받고 협력하는지를 정의
- 직접적인 참조를 통한 강한 결합을 피하고 느슨한 결합 구현
책임의 분산 (Responsibility Distribution)
- 단일 객체가 모든 작업을 처리하지 않고 여러 객체에 책임을 분산
- 각 객체는 자신의 전문 영역에 집중
알고리즘 캡슐화 (Algorithm Encapsulation)
- 알고리즘을 독립적인 객체로 캡슐화하여 런타임에 동적 선택 가능
- 변경과 확장에 대해 유연성 제공
상태 및 행위 관리 (State and Behavior Management)
- 객체의 내부 상태 변화에 따른 행위 변화를 체계적으로 관리
- 상태 전이와 행위 변화의 명확한 분리
핵심 개념의 실무 구현 연관성
이벤트 기반 아키텍처 (Event-Driven Architecture)
- Observer 패턴을 통한 발행 - 구독 모델 구현
- 마이크로서비스 간 비동기 통신에 활용
상태 기계 구현 (State Machine Implementation)
- State 패턴을 통한 복잡한 비즈니스 로직의 상태 관리
- 워크플로우 엔진과 결제 처리 시스템에서 핵심 역할
커맨드 패턴 기반 CQRS (Command Query Responsibility Segregation)
- Command 패턴을 통한 명령과 조회의 분리
- 대규모 시스템의 확장성과 성능 최적화
패턴이 해결하고자 하는 설계 문제
설계 문제 | 문제 설명 | 해결 방식 (패턴 적용) |
---|---|---|
객체 간 강한 결합 문제 | 객체들이 직접 서로를 참조함으로써 변경에 취약하고 재사용성이 떨어짐 | 인터페이스, 중재자 (Mediator), **관찰자 (Observer)**를 통해 간접 통신 구조 설계 |
변경에 대한 시스템 취약성 | 한 객체 또는 모듈의 변경이 전체 시스템에 연쇄적으로 영향을 미침 | 개방 - 폐쇄 원칙 (OCP) 기반 설계: Command, Strategy, Observer 등 적용 |
복잡한 조건부 로직 | 다중 if-else 또는 switch 문이 반복되어 가독성과 유지보수성 저하 | 전략 (Strategy), 상태 (State) 패턴으로 로직을 객체로 분리하여 캡슐화 |
책임의 분산 부족 | 하나의 객체가 너무 많은 책임을 가짐 (단일 책임 원칙 위반) | 책임 연쇄 (Chain of Responsibility), Command, Visitor로 책임 분리 |
요청 처리의 유연성 부족 | 요청 처리 방식이 고정되어 있고 유연하게 확장하거나 조합하기 어려움 | Command, Interpreter, Template Method로 요청을 캡슐화 및 구조화 |
객체 상태 관리 문제 | 상태에 따른 동작 변경이 조건문 기반으로 구현되어 유지보수 어려움 | State 패턴을 통해 객체의 상태와 동작을 분리 |
패턴 적용의 결과와 트레이드오프
긍정적 결과:
- 시스템의 유연성과 확장성 향상
- 코드 재사용성과 유지보수성 개선
- 테스트 용이성 증대
트레이드오프:
- 초기 개발 복잡도 증가 vs 장기적 유지보수 비용 절감
- 런타임 성능 오버헤드 vs 개발 생산성 향상
- 학습 곡선 존재 vs 팀 전체의 설계 품질 향상
기능 및 역할
기능 | 역할 |
---|---|
메시지 전달 | Chain of Responsibility, Mediator 등을 통해 요청 흐름 제어 |
행동 캡슐화 | Strategy, Command 로 알고리즘/동작 구조화 |
상태 기반 행동 제어 | State 패턴으로 객체 동작 상태 전환 구조화 |
순회 및 부가 기능 | Iterator, Visitor 로 구조 순회 및 외부 연산 수행 |
이벤트 처리 | Observer 로 상태 변화 알림 및 반응 동기화 |
특징
객체 컴포지션 중심
- 상속보다는 객체 컴포지션을 통한 유연성 확보
- 런타임에 객체 간 관계 변경 가능
인터페이스 기반 설계
- 구체적인 구현보다는 인터페이스에 의존
- 다형성을 통한 유연한 구현 교체
단일 책임 원칙 준수
- 각 객체는 하나의 명확한 책임만 담당
- 코드의 이해도와 유지보수성 향상
핵심 원칙
graph TD A[Behavioral Pattern 핵심 원칙] --> B[느슨한 결합<br/>Loose Coupling] A --> C[책임 분산<br/>Responsibility Distribution] A --> D[인터페이스 분리<br/>Interface Segregation] A --> E[개방-폐쇄 원칙<br/>Open-Closed Principle] B --> B1[객체 간 직접 참조 최소화] B --> B2[중재자를 통한 간접 통신] C --> C1[단일 책임 원칙 준수] C --> C2[역할과 책임의 명확한 분리] D --> D1[클라이언트 특화 인터페이스] D --> D2[의존성 역전] E --> E1[확장에는 열려있고] E --> E2[수정에는 닫혀있음]
느슨한 결합 (Loose Coupling)
- 객체 간 직접적인 참조를 최소화하여 의존성 감소
- 한 객체의 변경이 다른 객체에 미치는 영향 최소화
책임 분산 (Responsibility Distribution)
- 복잡한 작업을 여러 객체에 적절히 분배
- 각 객체는 자신의 전문 영역에만 집중
인터페이스 분리 (Interface Segregation)
- 클라이언트가 사용하지 않는 인터페이스에 의존하지 않도록 분리
- 변경의 영향 범위 최소화
주요 원리
위임 원리 (Delegation Principle)
- 작업을 다른 객체에 위임하여 책임 분산
- 상속 대신 컴포지션을 통한 유연성 확보
중재 원리 (Mediation Principle)
- 객체 간 직접 통신 대신 중재자를 통한 간접 통신
- 시스템의 복잡성 감소
관찰 원리 (Observation Principle)
- 상태 변화를 관찰하고 그에 따른 적절한 반응 수행
- 이벤트 기반 시스템의 기초
행동 패턴의 종류 및 비교
graph TB subgraph "Behavioral Patterns Architecture" subgraph "Communication Patterns" A1[Chain of Responsibility] A2[Command] A3[Mediator] A4[Observer] end subgraph "Algorithm Patterns" B1[Strategy] B2[Template Method] B3[Visitor] end subgraph "State Management" C1[State] C2[Memento] end subgraph "Iteration & Interpretation" D1[Iterator] D2[Interpreter] end subgraph "Utility Patterns" E1[Null Object] end end A1 --> F[Request Processing] A2 --> F A3 --> G[Communication Coordination] A4 --> G B1 --> H[Algorithm Selection] B2 --> H B3 --> H C1 --> I[State Management] C2 --> I D1 --> J[Data Access] D2 --> J E1 --> K[Default Behavior]
패턴명 | 핵심 목적 | 사용 시점 | 구조적 특징 | 장점 | 단점 |
---|---|---|---|---|---|
Chain of Responsibility | 요청을 처리할 수 있는 객체를 연결하여 처리 책임을 넘김 | 요청을 처리할 객체가 여러 개일 수 있고, 순차적으로 위임할 때 | 핸들러 체인 구성 | 결합도 감소, 처리자 유연성 | 디버깅 어려움, 체인 순서 의존 |
Command | 요청을 객체로 캡슐화하여 요청자와 수행자를 분리 | 명령 실행을 큐잉하거나, 취소/재실행 기능이 필요할 때 | 명령 객체 + 리시버 구성 | 요청 재사용, Undo/Redo 구현 가능 | 클래스 수 증가, 복잡성 증가 |
Interpreter | 문법 표현을 클래스로 정의하고 해석 | 도메인에 특화된 언어나 스크립트를 해석할 필요가 있을 때 | 추상 표현 + 구체 표현 클래스 구조 | 문법 유연성, DSL 구성 가능 | 복잡한 문법에는 부적합, 성능 저하 가능 |
Iterator | 내부 표현을 노출하지 않고 요소에 순차 접근 | 컨테이너나 컬렉션의 내부 구조와 무관하게 순회가 필요할 때 | 반복자 객체가 컬렉션을 순회 | 구현 간결화, 컬렉션 타입 일관된 접근 | 병렬 처리 어렵고, 동시 수정 시 문제 발생 |
Mediator | 객체 간의 상호작용을 중재자 (Mediator) 에 위임 | 여러 객체 간의 복잡한 상호작용이 존재할 때 | 중앙 중재 객체가 통신 조정 | 객체 간 결합도 최소화, 통제 구조 명확 | Mediator 자체 복잡도 증가 |
Memento | 객체 상태를 외부에 저장하고 복원 가능하게 함 | Undo 기능 구현이나 상태 복원이 필요할 때 | Memento(스냅샷) + Caretaker 구조 | 상태 캡슐화, 내부 정보 보호 | 메모리 부담, 직렬화 구현 필요 |
Observer | 한 객체 상태 변경 시 관련 객체들에 자동 통지 | 상태 변화에 따라 자동으로 동기화해야 할 때 (이벤트 시스템) | Subject + Observer 구조 | 느슨한 결합, 이벤트 중심 설계 용이 | 알림 폭주 가능성, 순서 제어 어려움 |
State | 객체 상태에 따라 행동을 동적으로 변경 | 상태 기반으로 분기 처리가 많고, 상태별 로직 분리 필요할 때 | 상태 객체들이 Context 내에서 교체됨 | 상태 전이 명확, SRP 준수 | 클래스 수 증가, 상태 전이 복잡도 증가 |
Strategy | 알고리즘을 런타임에 교체 가능하게 캡슐화 | 알고리즘이 여러 개 존재하고, 런타임에 유동적으로 교체할 필요가 있을 때 | 전략 인터페이스 + 구현 클래스 구성 | 알고리즘 독립성 확보, 클라이언트 코드 단순화 | 객체 간 연결 필요, 전략 관리 필요 |
Template Method | 알고리즘의 뼈대를 정의하고, 일부 단계를 서브클래스에서 구현 | 알고리즘은 같지만 세부 처리만 달라야 할 때 | 상위 클래스에서 알고리즘 흐름 고정 | 코드 재사용성 향상, 공통 로직 집중화 | 상속 기반이라 유연성 제한, 변경 어려움 |
Visitor | 구조 변경 없이 객체에 새로운 기능을 추가 | 복잡한 객체 구조에 다양한 연산을 적용하고자 할 때 | Visitor 인터페이스 + ConcreteVisitor | 연산 분리, 새로운 기능 쉽게 추가 | 객체 구조가 변경되면 모든 Visitor 수정 필요, 복잡성 증가 |
장점
구분 | 항목 | 설명 |
---|---|---|
구조적 장점 | 느슨한 결합 (Low Coupling) | 객체 간 직접 참조 없이 인터페이스를 통해 협력 → 변경에 유연하고 테스트 용이 |
구조적 장점 | 책임 분리 (Responsibility Separation) | 객체가 하나의 명확한 역할만 수행 → 유지보수성 및 가독성 향상 |
구조적 장점 | 단일 책임 원칙 (SRP 준수) | 행위 캡슐화를 통해 한 객체가 하나의 책임만 가지게 됨 |
확장성 | 개방 - 폐쇄 원칙 (OCP 준수) | 새로운 알고리즘, 상태, 동작을 기존 코드 수정 없이 확장 가능 |
확장성 | 전략/행위 교체 용이성 | 실행 시점에 객체의 행위나 전략을 교체할 수 있어 동적 로직 변경이 가능 |
재사용성 | 알고리즘/행위 재사용 | 캡슐화된 행위 객체는 다양한 컨텍스트에서 독립적으로 재사용 가능 |
테스트성 | 테스트 용이성 | 인터페이스 기반 설계 덕분에 Mocking/Stub 을 활용한 단위 테스트 작성이 쉬움 |
유연성 | 런타임 유연성 | 조건문 없이도 실행 중 전략, 상태, 행위 등을 동적으로 전환 가능 |
실무 적용성 | 이벤트 기반 구조 적합 | Observer, Command, Chain 등의 패턴은 이벤트 중심 시스템에 적합 |
실무 적용성 | 핸들러 조합 및 흐름 제어 유리 | Chain of Responsibility 나 Template Method 는 동작 순서나 책임을 유연하게 조절 가능 |
단점과 문제점 그리고 해결방안
단점
항목 | 설명 | 해결방안 |
---|---|---|
복잡성 증가 | 객체 간 협력이 많아지면서 시스템 구조가 복잡해지고 이해하기 어려움 | UML 다이어그램 활용, 문서화 강화, 컴포넌트 기반 설계 적용 |
클래스 수 증가 | 상태, 전략, 명령 등 객체 분리로 인한 클래스 수 급증 | 공통 책임 통합, 유틸리티 패턴 적용, DI(의존성 주입) 로 관리 |
성능 오버헤드 | 중개자/위임자 호출이 늘어나면서 성능 저하 발생 가능 | 직접 호출 허용, Lazy Initialization, 캐싱 전략 활용 |
디버깅 어려움 | 호출 경로가 다양하고 간접적이라 스택 트레이스 분석이 어려움 | 로깅 시스템 강화, 상관 ID 사용, 트레이싱 도구 적용 (예: OpenTelemetry) |
러닝 커브 | 패턴 자체의 개념과 관계 구조 학습이 필요함 | 팀 내 교육, 코드 리뷰 통한 공유, 패턴 캡슐화된 템플릿 제공 |
테스트 복잡성 | 각 객체의 독립성과 상호작용이 높아 테스트 커버리지가 어려움 | Mocking 도구 활용 (예: unittest.mock, Sinon.js), 통합 테스트 우선 접근 |
구조 난해화 | 설계 초기에 과도한 추상화가 오히려 구조를 더 어렵게 만듦 | 단순성 우선 설계, 필요할 때만 패턴 도입 (YAGNI 원칙 적용) |
문제점
항목 | 원인 | 영향 | 탐지 및 진단 | 예방 방법 | 해결 방법 및 기법 |
---|---|---|---|---|---|
과도한 패턴 적용 | 무분별하게 모든 문제를 패턴으로 해결하려는 접근 방식 | 코드 복잡성 증가, 유지보수성 저하 | 코드 리뷰, 설계 리뷰 | 요구사항 기반 선별 적용, 단순한 구조 선호 | 리팩토링, 필요 없는 계층 제거 |
순환 참조 | Observer, Mediator 간 의존성 설계 미흡 | 스택 오버플로우, 무한 루프 가능성 | 정적 분석 도구, 코드 리뷰 | 약한 참조 (WeakRef), 이벤트 해제 구조 명확화 | 참조 제거 또는 중간 매핑 객체 도입 |
메모리 누수 | 구독 해제 누락 (특히 Observer 패턴) | 메모리 증가, 시스템 리소스 소모 | 메모리 프로파일링, GC 로그 분석 | 자동 구독 해제 구조, Context Manager, Weak Reference | 스마트 포인터 활용, WeakMap/Set 구조로 관리 |
무한 재귀/호출 | Self-reference 또는 재귀 흐름 차단 미비 | 시스템 다운, 무한 호출 | 스택 추적, 호출 로그 분석 | 호출 깊이 제한, 순환 의존성 제거 | 호출 트리 리팩토링 또는 이벤트 큐 도입 |
상태 불일치 | State/Memento 사용 시 멀티스레드 환경에서 동기화 문제 | 데이터 불일치, 비정상 상태 전이 | 스레드 분석, 경쟁 조건 모니터링 도구 사용 | 불변 객체, Lock/Atomic 사용, 이벤트 기반 아 |
도전 과제
카테고리 | 도전 과제 | 원인 | 영향 | 해결 방안 |
---|---|---|---|---|
패턴 선택/설계 | 부적절한 패턴 선택 | 요구사항과 무관한 패턴 적용 | 코드 복잡도 증가, 유지보수성 저하 | 패턴 선택 전 요구사항 정제 및 설계 목적 재검토 |
구조 복잡성 | 클래스/객체 수 증가 | 전략/행위 객체 분리 과도 | 관리 복잡성, 진입 장벽 상승 | 중복 전략 통합, 팩토리 패턴 도입, 문서화/시각화 |
런타임 안전성 | 전략/상태 불일치 | Context 내부 상태와 전략 객체 간 계약 미비 | 예외 발생, 비정상 동작 | 상태 전이 제어, 전략 변경 전/후 검증, 트랜잭션 관리 강화 |
동시성 이슈 | 전략 객체의 공유 상태 문제 | 멀티스레드 환경에서 상태를 갖는 전략 객체 공유 | 데이터 레이스, 잘못된 동작 | Stateless 객체 사용, Thread-local 전략 분리, Actor 모델 적용 |
성능 최적화 | 객체 호출 및 이벤트 과다 | 다수 객체 간 호출 및 옵저버 알림 반복 | 메모리/CPU 오버헤드, 응답 지연 | 이벤트 배치 처리, 캐싱 도입, 불필요한 참조 제거 |
타입 안정성 | 런타임 구성 시 오류 가능 | 동적 전략/핸들러 변경 → 정적 타입 체크 불가 | 예외 처리 누락, 런타임 실패 | 명확한 인터페이스 계약, 전략 매핑 유효성 검사 |
테스트/유지보수성 | 복잡한 흐름 및 의존성 | 핸들러 체인, 전략 내부 흐름이 깊고 분산됨 | 테스트 작성 어려움, 신규 투입 시 학습 곡선 증가 | Mock 설계 강화, 행위 단위 테스트 도입, 코드 컨벤션 및 문서화 철저 |
비동기 흐름 통합 | 이벤트 기반 시스템에서의 제어 복잡 | 옵저버/커맨드/체인 등 비동기 로직의 흐름 추적 어려움 | 디버깅 어려움, 흐름 분기 누락 | 이벤트 추적 시스템 도입 (예: OpenTelemetry), 명시적 이벤트 로그 활용 |
기술 적용/환경 적응 | 클라우드·MSA·서버리스 환경의 특수성 | 동기적 객체 협력 방식이 분산/비동기 환경과 충돌 | 네트워크 지연, 부분 실패, 확장성 저하 | 이벤트 소싱, CQRS, 회로 차단기, 벌크헤드, 리액티브 패턴 조합 적용 |
실무 사용 예시
사용 목적 | 함께 사용되는 기술 | 효과 |
---|---|---|
웹 애플리케이션 이벤트 처리 | Observer + JavaScript Event System | 사용자 인터랙션에 대한 실시간 반응 시스템 구현 |
마이크로서비스 간 통신 | Command + Message Queue (RabbitMQ, Kafka) | 서비스 간 비동기 통신 및 장애 격리 |
게임 AI 시스템 | State + Finite State Machine | 복잡한 NPC 행동 패턴 및 게임 상태 관리 |
API 인증/인가 시스템 | Chain of Responsibility + Spring Security | 다단계 보안 검증 프로세스 구현 |
데이터 처리 파이프라인 | Strategy + Apache Spark | 데이터 소스별 맞춤형 처리 알고리즘 적용 |
GUI 프레임워크 | Observer + MVC Pattern | 모델 변경에 따른 뷰 자동 업데이트 |
실무 적용 예시
활용 사례
사례 1: 결제 모듈에서 Strategy 패턴 적용
전자상거래 플랫폼에서 다양한 결제 수단 (신용카드, 페이팔, 포인트 등) 을 Strategy 패턴으로 캡슐화하여 사용자가 결제 방식에 따라 적절한 PaymentStrategy
를 선택하도록 설계.
시스템 구성 및 흐름:
sequenceDiagram participant User participant Context as CheckoutService participant Strategy as PaymentStrategy participant Concrete1 as CreditCardStrategy participant Concrete2 as PayPalStrategy User->>Context: selectPaymentMethod(method) Context->>Strategy: setStrategy(concrete) User->>Context: pay(amount) Context->>Strategy: strategy.pay(amount)
CheckoutService
(Context) 가 런타임에PaymentStrategy
구현체 (CreditCardStrategy
,PayPalStrategy
) 를 주입받음.pay()
호출 시 선택된 알고리즘이 실행.
유무에 따른 차이
조건 | Strategy 미사용 | Strategy 사용 |
---|---|---|
코드 복잡도 | pay() 내부 if/else 분기 다수 | 결제방식별 클래스 분리로 단순화 |
확장성 | 새로운 결제 추가 시 pay() 수정 | 전략 클래스 추가 + Context 에서 등록 |
테스트 | 결제 방식 테스트 함께 복잡 | 각 전략 단위 테스트 용이 |
사례 2: 채팅 애플리케이션
시나리오: 채팅 애플리케이션에서 사용자 간의 메시지 전달을 관리하는 시스템을 설계할 때, Mediator 패턴을 활용하여 사용자 객체 간의 직접적인 상호작용을 피하고 중앙 집중화된 메시지 중개자를 통해 메시지를 전달한다.
시스템 구성:
- 사용자 객체: 메시지를 보내고 받을 수 있는 객체
- 메시지 중개자: 사용자 객체 간의 메시지 전달을 관리하는 중앙 객체
Workflow:
- 사용자 A 가 메시지를 보낸다.
- 메시지 중개자가 메시지를 받아 사용자 B 에게 전달한다.
- 사용자 B 가 메시지를 수신한다.
역할:
- 사용자 객체: 메시지의 송수신을 담당한다.
- 메시지 중개자: 메시지의 전달을 중앙에서 관리한다.
사례 3: 이벤트 알림 시스템
Observer 패턴 적용 사례: 이벤트 알림 시스템
시스템 구성:
- Subject(주체) → Observer(관찰자)
- Subject 는 상태 변화 시 Observer 에게 알림
Workflow:
- Subject 상태 변경
- Subject 가 Observer 에게 알림
- Observer 가 상태 반영
역할:
- 상태 변화를 실시간으로 반영하여, 객체 간 결합도를 낮추고 유연성을 높입니다.
차이점:
- 패턴 미적용 시 직접 상태 동기화 필요
- 패턴 적용 시 자동 동기화 및 유연성 향상
사례 4: 실시간 알림 시스템에 Observer 패턴 활용
시나리오: SNS 앱에서 새로운 댓글이 등록될 때마다 구독자들에게 실시간 알림 발송 필요
시스템 구성:
- Subject: 게시글, Observer: 사용자 알림 서비스
- Subject 는 Observer 등록/제거 기능 제공, 상태 변동시 일괄 통지
워크플로우:
- 사용자가 게시글을 구독 (Observer 등록)
- 댓글 등록 시 게시글 상태 변경
- 게시글이 Observer(알림 서비스) 에 이벤트 통지
- 알림 서비스가 구독 사용자에게 실시간 알림 전송
역할:
- 게시글: 상태/이벤트 발생에 대한 중심 관리
- 알림 서비스: 사용자별 알림 분배
사례 5: 전자상거래 주문 처리 시스템
시스템 구성:
graph TB subgraph "Order Processing System" A[Order Controller] --> B[Command Processor] B --> C[Validation Chain] B --> D[Payment Strategy] B --> E[Inventory Observer] B --> F[Notification Observer] C --> C1[Stock Validator] C --> C2[Payment Validator] C --> C3[Address Validator] D --> D1[Credit Card Strategy] D --> D2[PayPal Strategy] D --> D3[Bank Transfer Strategy] E --> E1[Inventory Service] F --> F1[Email Service] F --> F2[SMS Service] end
Workflow:
- 주문 접수: Command 패턴으로 주문 요청을 객체화
- 유효성 검증: Chain of Responsibility 로 다단계 검증
- 결제 처리: Strategy 패턴으로 결제 방식 선택
- 재고 업데이트: Observer 패턴으로 재고 시스템에 통지
- 알림 발송: Observer 패턴으로 고객 알림 서비스 작동
Behavioral Pattern 의 역할:
- Command: 주문 요청의 캡슐화 및 실행 취소 지원
- Chain of Responsibility: 유연한 검증 단계 추가/제거
- Strategy: 다양한 결제 방식의 동적 선택
- Observer: 주문 상태 변화에 따른 자동 업데이트
패턴 유무에 따른 차이점:
- 패턴 적용 시: 각 구성요소가 독립적으로 변경 가능, 새로운 결제 방식이나 검증 단계 쉽게 추가
- 패턴 미적용 시: 모든 로직이 하나의 클래스에 집중되어 변경 시 전체 시스템 영향, 테스트 어려움
실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점
카테고리 | 항목 | 설명 | 권장사항 |
---|---|---|---|
패턴 선택 | 패턴 적합성 | 실제 문제 유형에 적합한 패턴인지 여부 판단 | 문제 분석 후 적절한 패턴 선택, 오·과용 방지 |
남용/오용 경계 | 지나치게 많은 패턴 적용 시 복잡도 증가 가능 | 적용 목적과 장단점 명확히 인지 및 기록 | |
설계 단계 | 요구사항 변화 대응성 | 요구사항 변경 가능성에 유연하게 대응할 수 있는 구조 설계 | 단순한 구조에서 시작해 점진적 적용, 인터페이스 기반 설계 |
인터페이스 명확성 | 각 역할 객체의 책임과 API 명확화 | 인터페이스 분리 원칙 (SRP), 설계 시점에 역할 명확히 정의 | |
결합도 최소화 | 객체 간 의존도와 결합도는 낮출수록 유리 | 의존성 주입 (DI), 인터페이스 중심 설계 적용 | |
순환 참조 방지 | 다중 객체 간 상호참조로 인한 런타임 오류 방지 | 설계 리뷰, 의존성 시각화 및 순환참조 검증 툴 활용 | |
과도한 추상화 | 불필요한 추상화는 유지보수성과 성능 저하 유발 | 핵심 기능 위주 구성, 사용성 중심 설계 | |
구현 단계 | 흐름 가시성 | 로직 흐름과 책임 분리가 불명확할 경우 유지보수 어려움 | 로깅, 시각화 도구, 메시지 흐름 주석 삽입 |
경로 제어 복잡도 | 복잡한 조건 분기, 예외 흐름이 많은 경우 로직 불명확 | 명확한 종료 조건 정의, 상태 기반 분기 도입 | |
클래스 수 증가 | 단계마다 새로운 클래스가 필요해지며 복잡도 상승 가능 | 유틸리티 기반 처리 병행, 래퍼 또는 필터 방식 도입 | |
직접 구현 vs 추상화 | 성능이 중요한 부분은 추상 계층보다 직접 구현이 유리 | 크리티컬 영역은 최소 간접 호출 설계, 병행 사용 고려 | |
테스트 단계 | 단위 테스트 강화 | 객체 간 역할 분리로 단위 테스트가 가능해야 함 | Mock, Stub 객체 활용, 자동화 테스트 구축 |
통합 테스트 복잡성 | 여러 패턴 간 협력 시 통합 테스트 난이도 증가 가능 | 테스트 레벨 분리, 각 패턴 조합별 시나리오 정의 및 문서화 | |
변경 영향 범위 파악 | 구조 변경 시 영향 받는 모듈을 명확히 파악해야 함 | 커버리지 도구 및 통합 테스트 주기적 실행 | |
유지보수 단계 | 문서화 및 가독성 유지 | 설계 의도와 구현 구조를 후속 개발자가 이해 가능해야 함 | 주석, UML, 플로우 차트 등 병행 문서화 |
인터페이스 일관성 유지 | 역할 또는 구성 요소가 변경되어도 외부 의존성 최소화 | 버전 관리, 변경 이력 관리, 하위 호환성 고려 | |
클래스 의도 주석화 | 패턴을 적용한 목적이 코드 상에 드러나야 함 | 핵심 클래스에 설계 의도 주석 삽입 (@Pattern , @Role 등) | |
테스트 전략 | 테스트 가능성 확보 | 유닛별 테스트가 가능하도록 의존성과 흐름을 분리해야 함 | 전략 패턴, 의존성 주입, Facade 결합 등 테스트 중심 설계 적용 |
문서화/관리 | 설계 문서화 | 구조와 흐름, 의도를 명확히 기록하여 개발자 간 공유 | 설계 시점에 다이어그램 (UML, 시퀀스, 컴포넌트 등) 작성 |
코드 - 문서 일치 | 문서화 내용과 실제 구현 간 불일치 방지 | 정기적 리뷰 및 문서 자동 생성 도구 활용 (Swagger, TypeDoc 등) |
테스트 전략
단위 테스트 전략
- 각 패턴 구성요소의 독립적 테스트
- Mock 객체를 활용한 의존성 격리
- 경계값 및 예외 상황 테스트
통합 테스트 전략
- 패턴 구성요소 간 상호작용 검증
- 엔드투엔드 시나리오 테스트
- 성능 및 동시성 테스트
리팩토링 전략
점진적 리팩토링
- 기존 코드를 단계적으로 패턴 구조로 변환
- 각 단계별 테스트 수행으로 안정성 확보
패턴 추출
- 반복되는 코드 패턴을 추상화
- 공통 인터페이스 도출 및 구현체 분리
활용 시 흔한 실수
불필요한 패턴 적용
- 단순한 문제에 복잡한 패턴 사용
- 해결책: 요구사항 복잡도에 맞는 적절한 패턴 선택
인터페이스 설계 오류
- 너무 크거나 작은 인터페이스 정의
- 해결책: 인터페이스 분리 원칙 준수
생명주기 관리 소홀
- Observer 등록 해제 누락
- 해결책: RAII 패턴이나 자동 관리 메커니즘 활용
비동기/멀티스레드 환경에서의 Behavioral Pattern 구현 고려사항
공통 고려사항
항목 | 고려사항 | 설명 | 해결 전략 |
---|---|---|---|
상태 공유 | 공유 객체 접근 충돌 | Observer 목록, 상태 객체 등 | Lock, Copy-on-Write, Concurrent Queue |
순서 보장 | 메시지 순서, 상태 전이 순서 | notify 순서, 상태 변경 타이밍 | 메시지 큐 (RabbitMQ 등), Ordered Dispatcher |
실행 컨텍스트 | 스레드 간 전략 객체 공유 | 하나의 전략이 여러 Context 에서 사용될 수 있음 | Thread-Local Storage or Stateless 설계 |
중단 처리 | 비동기 Task 취소 및 오류 | Strategy 실행 중 예외 발생 | Future, Timeout 설정, 에러 전파 체계 |
동기화 비용 | 락 과다 사용으로 성능 저하 | 과도한 동기화로 성능 저하 | 락 최소화, CAS (Compare-and-Swap) 알고리즘 활용 |
성능 최적화 고려사항
카테고리 | 항목 | 설명 | 권장사항 |
---|---|---|---|
객체 관리 최적화 | 객체 수 증가 | 패턴 적용으로 클래스/객체 수 증가 가능 | 객체 풀링, 불필요 객체 생성을 지양, Flyweight 패턴 병용 |
동일 객체 중복 생성 | 동일 행위/전략 객체를 매번 새로 생성하는 경우 메모리 낭비 | 행위 객체 캐싱, 싱글턴 또는 정적 객체 활용 | |
객체 생명주기 관리 | 생성 - 삭제 반복으로 인한 GC 부하 | 지연 초기화, 참조 해제 명확히, 약한 참조 (Weak Reference) 사용 | |
행위 처리 최적화 | 핸들러 실행 비용 | Chain of Responsibility 등에서 체인 길이에 따라 실행 지연 가능 | 체인 분리, 병렬 처리 고려, 전략적 분기 설정 |
이벤트 처리 부하 | Observer/Event Dispatcher 등에서 이벤트 폭주 발생 가능 | 큐 기반 비동기 처리, 이벤트 버퍼링 및 배치 처리 | |
런타임 위임 비용 | Strategy, Command 패턴에서 위임 호출에 따른 성능 저하 | 빈번한 호출 시 인라인 처리 또는 최적화된 인터페이스 사용 | |
상태 관리 최적화 | 상태 메모리 사용량 | State, Memento 등에서 불필요한 상태 저장으로 메모리 증가 | 최소 상태 캡처, 조건 기반 상태 저장, 상태 제거 시점 명확히 설정 |
공유 상태 동시 접근 | 상태 객체 공유 시 Race Condition 발생 가능 | 무상태 (stateless) 설계, 락 최소화, 원자성 확보 | |
동시성 및 병렬성 | 다수 옵저버/핸들러 동시 실행 | 이벤트 리스너가 다수일 때 동시성 문제 발생 가능 | 스레드풀, 이벤트 루프, Actor 모델 도입 |
락 경합/데드락 위험 | 공유 리소스 접근 시 락 경합 및 교착 상태 발생 위험 | Lock-free 자료구조 사용, 명확한 락 범위 설정, 비동기 처리 | |
메모리 및 네트워크 | 메모리 누수 | Observer 등에서 참조가 끊기지 않으면 GC 불가 | 약한 참조 (WeakMap, WeakReference), 수명 주기와 해제 타이밍 명시 |
원격 호출 비용 | 프록시/Facade 패턴 등에서 네트워크 요청 증가 가능 | 호출 최소화, 로컬 캐시 적용, 지연 로딩 도입 | |
디버깅/유지보수성 | 상호작용 디버깅 어려움 | 패턴 간 위임 구조로 흐름 파악이 어려워 디버깅 복잡도 증가 | AOP 기반 로깅, 트레이싱 도구 활용 (예: OpenTelemetry) |
클래스 폭발/복잡성 증가 | 패턴 조합 시 클래스 수가 많아져 가독성과 관리가 어려워짐 | 패턴 최소화 원칙 적용, 공통 추상화 구조 사용, 리팩토링 주기적 수행 | |
패턴 적용 판단 | 과도한 패턴 사용 | 실제 필요보다 많은 패턴 적용으로 오히려 복잡성 및 오버헤드 증가 | “YAGNI(You Aren’t Gonna Need It)” 원칙 적용, 설계 시점에서 최소한의 도입 고려 |
주제와 관련하여 주목할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
패턴 구조 및 역할 | 객체 협력 | 느슨한 결합 | 객체 간 직접 참조를 줄여 유연성과 재사용성을 확보함 |
책임 분리 | 역할 명확화 | 책임 단위의 분리로 유지보수성과 확장성 향상 | |
패턴 종류 및 특성 | 대표 Behavioral 패턴 | Observer, Strategy, Command 등 | 다양한 객체 간 협력 및 행위 분산을 실현하는 구조 |
이벤트 기반 패턴 | Publish-Subscribe, Event Dispatcher | 비동기/다대다 구조의 이벤트 흐름 구현 | |
실무 적용 | 이벤트 시스템 | Observer | 실시간 알림, UI 반응성, 상태 변화 전파 구조 설계 |
명령/워크플로우 처리 | Command, Chain of Responsibility | 명령 캡슐화, 책임 체인 구조로 유연한 요청 처리 | |
테스트 전략 | Mockable 구조 | 인터페이스 분리로 테스트 시 독립적 유닛 테스트 가능 | |
설계 원칙 대응 | 커플링 감소 | 책임 위임, 인터페이스 활용 | 객체 간 직접 호출 대신 메시지 기반 협력 도입 |
런타임 유연성 | Strategy, Command | 런타임에 행동 (알고리즘, 명령) 을 자유롭게 교체 가능 | |
SOLID 원칙 적용 | DIP, OCP, SRP | 행동 캡슐화를 통한 의존 역전, 개방/폐쇄 원칙 구현 | |
현대 기술 트렌드 | 함수형 프로그래밍 | 고차 함수 + Strategy | 전략을 함수 객체 (First-Class Function) 로 구현 가능 |
리액티브 프로그래밍 | RxJS, ReactiveX, Reactor 등 | Observer 패턴 기반 비동기 데이터 흐름 모델 구현 가능 | |
클라우드 네이티브 아키텍처 | Event Sourcing, Saga Pattern | Command/Event 기반 구조로 마이크로서비스 간 분산 처리 구현 | |
AI/ML 파이프라인 | Chain of Responsibility | 모델 파이프라인 각 처리 단계를 체인 구조로 구성 가능 | |
AI/ML 전략 | A/B 테스트 + Strategy | 모델 전략 교체를 유연하게 적용 가능한 구조 | |
교육 및 품질 관점 | 팀 내 패턴 교육 | 학습 및 온보딩 | 신입 개발자에게 디자인 패턴 구조 및 협력 원리 교육에 적합 |
유지보수/확장성 | 패턴 적용 효과 | 구조화된 코드 설계로 변경에 강하고 확장에 용이한 코드 구현 가능 | |
코드 품질 향상 | 객체 간 역할 분리 | 단일 책임 부여 및 재사용 가능한 구조 유도 | |
복합 적용 전략 | 하이브리드 패턴 구조 | Composite + Observer 등 | 구조/행위 패턴을 조합하여 유연성과 기능성을 동시에 확보 |
Reactive + EDA | 이벤트 흐름 중심 복합 아키텍처 | Observer/Event Dispatcher 패턴을 활용한 모듈 간 decoupling 구성 |
반드시 학습해야할 내용
카테고리 | 주제 | 항목 | 설명 |
---|---|---|---|
GoF 패턴 개요 | Behavioral Pattern | 종류 및 구조 | 총 11 가지 행동 패턴의 목적, 구조, 적용 방식 포함 |
핵심 패턴 | Observer | 상태 변화 알림 | 주체 (Subject) 의 상태 변경 시 관찰자 (Observer) 에게 자동으로 알림 전파 |
Strategy | 알고리즘 교체 | 런타임에 알고리즘을 자유롭게 교체 가능하도록 캡슐화 | |
Command | 요청 캡슐화 | 명령을 객체로 추상화하여 요청 실행을 유연하게 제어 | |
Chain of Responsibility | 책임 분산 구조 | 요청 처리 객체를 체인으로 연결하여 처리 책임을 동적으로 전가 | |
Interpreter | 언어 해석 | 도메인 언어의 문법을 표현하고 해석기 (Context) 를 통해 의미 분석 | |
Iterator | 순회 구조 | 컬렉션의 내부 구조를 노출하지 않고 순차적으로 접근할 수 있도록 구현 | |
Template Method | 알고리즘 템플릿 | 알고리즘의 구조는 상위 클래스가 정의하고, 세부 구현은 하위 클래스가 담당 | |
Visitor | 기능 확장 구조 | 객체 구조는 고정한 채, 새로운 연산을 추가할 수 있게 하는 구조 | |
객체지향 설계 원칙 | SOLID 원칙 | OCP, SRP, DIP 등 | Behavioral Pattern 은 개방/폐쇄 원칙 (OCP), 의존성 역전 원칙 (DIP) 등 설계 원칙을 구현하는 데 핵심 역할 |
Loose Coupling | 결합도 최소화 | 패턴을 통해 객체 간 직접 의존을 줄이고 변경에 유연하게 대응 | |
캡슐화/행위 위임 | 인터페이스 추상화 | 행위의 분리 및 재사용성을 높이기 위한 위임, 인터페이스 기반 설계 구현 | |
구현 기술 | 델리게이션 (Delegation) | 함수형 콜백, 델리게이터 구조 | 동적 동작 위임을 통한 유연한 런타임 행동 제어 |
Event Dispatcher | 비동기 이벤트 분배 구조 | 이벤트 기반 시스템에서 비차단 (Non-blocking) 구조 구현 | |
멀티스레딩/동시성 | 스레드 안전 구현 | 스레드 안전한 Behavioral Pattern (예: Command + Queue) 구성 | |
메모리 관리 | 스마트 포인터/GC 대응 구조 | 명령 및 상태 객체의 수명 관리를 위한 메모리 최적화 전략 | |
실무 적용 | 이벤트/명령 시스템 | 메시지 기반 워크플로우, 이벤트 버스 | Command, Observer 등의 패턴을 이벤트 시스템에 적용 |
테스트 자동화 | Mocking/Stub 구성 | 이벤트 기반 행동을 테스트하기 위한 목 객체 설계 전략 | |
프레임워크 적용 | Spring,.NET, Express | 주요 프레임워크 내 Behavioral 패턴의 실제 구현 구조 분석 | |
아키텍처 적용 | MVC, MVP, MVVM | 각 계층 간 책임 분리를 위한 Behavioral 패턴 적용 사례 | |
마이크로서비스 | 메시지 브로커, API 게이트웨이 | 서비스 간 decoupling 을 위한 패턴 활용 (예: Command + Queue) | |
심화 학습 영역 | 패턴별 실전 코드 | 구현 예제 + 테스트 코드 | 각 행동 패턴을 독립 또는 조합하여 실전 예제와 테스트 작성 |
패턴 조합 및 응용 | 하이브리드 패턴 | Strategy + Command, Observer + Mediator 등 복합 구조 적용 분석 | |
리팩토링 전략 | 패턴 도입 전후 비교 | 코드 리팩토링을 통해 Behavioral 패턴 도입 전후의 구조 변화 분석 | |
연관 이론/개념 | 이벤트 기반 아키텍처 | EDA, Event Sourcing, CQRS | 행동 패턴이 자연스럽게 통합되는 아키텍처 구조 학습 |
책임 분산 | 역할 기반 객체 협력 구조 | 객체 간 책임 분리를 통한 협력 구조 설계 분석 | |
함수형 프로그래밍 + 패턴 | FP 에서의 행동 패턴 응용 | 고차 함수, 콜백 기반의 Strategy, Command 구현 예 | |
패턴 확장성 | Composite/Hybrid Pattern | 구조 + 행위 패턴 결합을 통한 설계 확장 전략 |
용어 정리
카테고리 | 용어 | 설명 |
---|---|---|
디자인 패턴 | GoF (Gang of Four) | 『Design Patterns』의 저자 4 인, 디자인 패턴의 창시자들 |
Behavioral Pattern | 객체 간 상호작용과 책임 분배를 다루는 패턴군 | |
Strategy | 알고리즘을 캡슐화하여 런타임에 교체 가능한 행동 패턴 | |
Observer | 상태 변화 발생 시 등록된 객체들에 알림을 전달하는 행동 패턴 | |
Command | 요청을 객체화하여 실행 로직을 캡슐화하는 행동 패턴 | |
Chain of Responsibility | 요청을 순차적으로 처리할 수 있는 핸들러 체인을 구성하는 행동 패턴 | |
Mediator | 객체 간 직접 통신 대신 중재자 객체를 통해 간접 통신하게 하는 행동 패턴 | |
설계 원칙 | OCP (Open-Closed Principle) | 확장에는 열려 있고, 변경에는 닫혀 있어야 한다는 원칙 |
SRP (Single Responsibility Principle) | 클래스는 하나의 책임만 가져야 한다는 원칙 | |
Loose Coupling | 객체 간 결합도를 낮추는 설계 원칙, 변경에 유연하게 대응 가능 | |
Decoupling | 시스템 구성 요소 간의 의존성을 낮추어 독립성 향상을 지향하는 설계 원칙 | |
패턴 구성 요소 | Context | 전략 또는 상태 객체를 포함하여 실행 흐름을 제어하는 클래스 |
Strategy (전략 객체) | 구체적인 알고리즘 또는 행위를 담고 있는 객체 | |
Subject | 상태 변화를 관리하며 Observer 들에게 알림을 전파하는 객체 | |
Observer (관찰자) | Subject 의 상태 변화를 감시하고 반응하는 객체 | |
Handler | Chain of Responsibility 에서 요청을 처리하거나 전달하는 객체 | |
구현 기법 | Delegation (위임) | 작업을 다른 객체에 위임하여 유연성과 재사용성을 높이는 구조 |
Mediation (중재) | 직접 연결된 객체들 대신 중재자를 통해 간접적으로 메시지를 주고받는 구조 | |
Publish-Subscribe (발행 - 구독) | 이벤트 발행자와 구독자가 느슨하게 결합되어 있는 비동기 이벤트 처리 구조 | |
추상 개념 | 캡슐화 (Encapsulation) | 데이터와 관련 기능을 하나의 단위로 묶고 외부로부터 은닉하는 객체지향 원칙 |
책임 분산 (Responsibility Distribution) | 하나의 객체에 과도한 책임이 몰리지 않도록 여러 객체에 기능을 분산하는 방식 | |
기타 개념 | 객체 간 상호작용 | 객체들이 메시지를 주고받으며 협력하여 기능을 수행하는 구조 |
캡슐화된 요청 (Command 객체) | 요청 정보를 객체로 추상화하여 다양한 실행 전략을 유연하게 처리 | |
리스너 (Listener) | 이벤트를 감지하고 응답하는 구조적 역할 객체 | |
실무 개념 | CQRS (Command Query Responsibility Segregation) | 커맨드와 조회 작업을 분리하여 확장성과 성능을 개선하는 설계 원칙 |
Ubiquity (만연성) | 특정 개념 또는 패턴이 다양한 분야에서 널리 사용되는 상태 |
참고 및 출처
- Facade design pattern. What is it and why is it useful? - Dev.to
- Design Patterns - Wikipedia
- Design Patterns: Elements of Reusable Object-Oriented Software (GoF)
- The Catalog of Design Patterns - Refactoring.Guru
- Structural Design Patterns - Refactoring.Guru
- Facade Pattern - Refactoring.Guru
- Gang of Four (GoF) Design Patterns - GeeksforGeeks
- Structural Design Patterns in Java - GeeksforGeeks
- Facade Design Pattern in Java - GeeksforGeeks
- Understanding Gang of Four (GoF) Design Patterns - Medium
- GOF Design Patterns Overview - DigitalOcean
- All the GoF Patterns - CMU School of Computer Science (PDF)
- Design Patterns and Refactoring - Sourcemaking
- How to Implement the Facade Pattern in Python - Real Python
- Design Patterns Explained – Codecademy Blog
- Facade Pattern in Software Design - Baeldung
- The Facade Pattern in TypeScript - Medium