Chain of Responsibility
Chain of Responsibility는 요청을 처리자들의 체인을 따라 전달할 수 있게 하는 행동 디자인 패턴입니다. 요청을 받은 각 처리자는 이를 처리하거나 다음 처리자에게 넘길 수 있습니다.

Intent
Chain of Responsibility 패턴은 요청을 핸들러 체인을 따라 전달합니다. 요청을 받으면 각 핸들러는 요청을 처리할지, 아니면 체인의 다음 핸들러로 전달할지를 결정합니다.
Problem
온라인 주문 시스템을 개발하고 있다고 가정합시다. 인증된 사용자만 주문을 생성할 수 있도록 접근을 제한하고, 관리자는 모든 주문에 대한 전체 접근을 가져야 합니다.

이러한 검사들은 순차적으로 수행되어야 합니다. 요청이 올바른 자격 증명을 포함하지 않으면 인증이 실패하고, 이후의 모든 검사를 진행할 필요가 없습니다.
시간이 지나면서 더 많은 순차 검사가 추가됩니다:
- 데이터 검증 및 살균
- IP 주소로부터의 반복된 실패한 요청 필터링
- 캐시된 결과 반환

결과적으로 코드는 복잡해지고, 유지보수가 어려워집니다. 하나의 검사를 수정하면 다른 검사들에도 영향을 줄 수 있습니다. 새로운 검사를 추가하려면 전체 코드를 다시 작성해야 할 수도 있습니다.
Solution
Chain of Responsibility 패턴은 각각의 검사를 독립적인 처리자(Handler) 객체로 추출합니다. 각 처리자는 다음 처리자에 대한 참조를 가집니다.

핵심 특징:
- 처리자들은 체인을 형성합니다
- 각 처리자는 요청을 처리하거나 다음으로 전달할 수 있습니다
- 처리자는 요청 전달을 중단할 수 있습니다

처리자는 요청을 받으면 먼저 자신이 처리할 수 있는지 확인합니다. 처리할 수 있다면 요청을 처리하고, 그렇지 않다면 다음 처리자에게 전달합니다.
Real-World Analogy

기술 지원 전화 시스템을 생각해보세요:
- 자동응답 시스템이 일반적인 솔루션을 제시합니다
- 해결되지 않으면 인간 운영자에게 연결됩니다
- 해결되지 않으면 엔지니어에게 연결됩니다
각 수준은 요청을 처리하거나 다음 수준으로 전달합니다.
Structure


| 구성 요소 | 역할 |
|---|---|
| Handler | 모든 처리자가 구현할 인터페이스. 요청 처리를 위한 단일 메서드 선언 |
| Base Handler | 공통 보일러플레이트 코드를 포함하는 선택적 추상 클래스 |
| Concrete Handlers | 실제 요청 처리 로직을 포함. 요청을 처리할지 다음으로 전달할지 결정 |
| Client | 체인을 구성하고 처리자에게 요청을 보냄 |
Pseudocode
GUI 요소의 상황별 도움말을 표시하는 예시:

사용자가 F1을 누르면, 애플리케이션은 마우스 포인터 아래에 있는 컴포넌트를 감지하고 도움말 요청을 보냅니다. 요청은 모든 컨테이너를 거쳐 도움말 정보를 표시할 수 있는 요소에 도달할 때까지 순회합니다.

// 상황별 도움말을 제공하는 인터페이스interface ComponentWithContextualHelp { showHelp(): void;}
// 기본 컴포넌트 추상 클래스abstract class Component implements ComponentWithContextualHelp { tooltipText: string | null = null; protected container: Container | null = null;
showHelp(): void { if (this.tooltipText !== null) { // 툴팁 표시 console.log(`Tooltip: ${this.tooltipText}`); } else if (this.container !== null) { // 부모 컨테이너에게 위임 this.container.showHelp(); } }}
// 컨테이너 추상 클래스abstract class Container extends Component { protected children: Component[] = [];
add(child: Component): void { this.children.push(child); child.container = this; }}
// 버튼 컴포넌트class Button extends Component { // 기본 구현 사용}
// 패널 컴포넌트class Panel extends Container { modalHelpText: string | null = null;
showHelp(): void { if (this.modalHelpText !== null) { // 모달 창에 도움말 표시 console.log(`Modal Help: ${this.modalHelpText}`); } else { super.showHelp(); } }}
// 다이얼로그 컴포넌트class Dialog extends Container { wikiPageURL: string | null = null;
showHelp(): void { if (this.wikiPageURL !== null) { // Wiki 페이지 열기 console.log(`Opening Wiki: ${this.wikiPageURL}`); } else { super.showHelp(); } }}
// 애플리케이션class Application { private dialog: Dialog; private panel: Panel; private button: Button;
createUI(): void { this.dialog = new Dialog(); this.dialog.wikiPageURL = "https://wiki.example.com/budget-reports";
this.panel = new Panel(); this.panel.modalHelpText = "This panel shows budget reports.";
this.button = new Button(); this.button.tooltipText = "Click OK to confirm.";
this.panel.add(this.button); this.dialog.add(this.panel); }
onF1KeyPress(component: Component): void { component.showHelp(); }}Applicability
다음의 경우에 패턴을 사용합니다:
다양한 요청 처리가 필요할 때
프로그램이 다양한 종류의 요청을 여러 방식으로 처리해야 하지만, 정확한 요청 유형과 순서를 미리 알 수 없을 때 사용합니다. 패턴을 통해 여러 핸들러를 하나의 체인으로 연결하고, 요청이 도착하면 각 핸들러에게 처리할 수 있는지 확인할 기회를 제공합니다.
순서가 중요할 때
특정 순서로 여러 처리자를 실행해야 할 때 유용합니다. 체인의 핸들러를 원하는 순서로 연결할 수 있습니다.
동적 변경이 필요할 때
런타임에 처리자의 집합과 순서가 변경될 때 유용합니다. 체인 내에서 핸들러를 동적으로 삽입, 제거 또는 재정렬할 수 있습니다.
How to Implement
-
Handler 인터페이스 선언: 요청 처리 메서드의 서명을 정의합니다. 클라이언트가 어떻게 요청 데이터를 전달할지 결정합니다.
-
Base Handler 생성 (선택): 공통 코드를 포함할 추상 기본 처리자 클래스를 만듭니다.
-
다음 처리자 참조 필드 정의: 체인에서 다음 처리자를 가리키는 참조를 저장하는 필드를 정의합니다.
-
Concrete Handler 구현: 각 구체적인 처리자 서브클래스를 만듭니다. 각 처리자는 두 가지 결정을 내립니다:
- 요청을 처리할 것인가?
- 요청을 체인을 따라 전달할 것인가?
-
클라이언트에서 체인 구성: 체인을 직접 구성하거나, 사전에 구성된 체인을 받아 사용합니다.
-
유연한 트리거: 클라이언트는 체인의 첫 번째 처리자가 아닌 어느 처리자든 트리거할 수 있습니다.
Pros and Cons
장점
| 장점 | 설명 |
|---|---|
| 처리 순서 제어 | 요청이 처리되는 순서를 제어할 수 있습니다 |
| Single Responsibility Principle | 요청을 보내는 클래스와 처리하는 클래스를 분리합니다 |
| Open/Closed Principle | 기존 클라이언트 코드를 수정하지 않고 새 핸들러를 추가할 수 있습니다 |
단점
| 단점 | 설명 |
|---|---|
| 처리되지 않은 요청 | 일부 요청이 체인 끝까지 처리되지 않을 수 있습니다 |
Relations with Other Patterns
| 패턴 | 관계 |
|---|---|
| Command, Mediator, Observer | 요청의 송수신자를 연결하는 다양한 방식을 제공 |
| Composite | 리프 컴포넌트가 요청을 부모 체인을 통해 전달할 때 함께 사용 |
| Decorator | 구조적으로 유사하지만, CoR은 요청 흐름을 중단할 수 있음 |