책임 연쇄 chain of responsibility 패턴 - 타입스크립트 예시
SW설계
읽는데 5분 소요
처음 쓰여진 날: 2025-09-01
마지막 수정일: 2025-09-01
조회수: 26
요약
디자인 패턴 중 책임 연쇄 chain of responsibility 패턴을 타입스크립트 코드와 함께 알아봅니다.
책임 연쇄 (Chain of Responsibility) 패턴 요약
패턴 종류 | 핵심 키워드 |
---|---|
책임 연쇄 (Chain of Responsibility) | 요청을 처리할 수 있는 객체들을 체인(사슬)으로 연결하여, 요청이 들어오면 체인을 따라 순서대로 처리 기회를 넘기는 |

책임 연쇄 (Chain of Responsibility) 패턴
책임 연쇄 패턴은 요청을 처리할 수 있는 객체들을 체인(사슬)으로 연결하여, 요청이 들어오면 체인을 따라 순서대로 처리 기회를 넘기는 패턴입니다. 요청을 보내는 클라이언트는 체인의 시작점에만 요청을 전달할 뿐, 실제로 어떤 객체가 그 요청을 처리하는지는 알 필요가 없습니다.
이 패턴은 두 가지 대표적인 방식으로 활용될 수 있습니다.
- 독점적 처리: 체인을 따라가다 하나의 처리기가 요청을 처리하면 거기서 종료됩니다. (예: 승인/결재 시스템)
- 포괄적 처리: 하나의 요청을 여러 처리기가 모두 순차적으로 처리합니다. (예: 웹 프레임워크 미들웨어)
기본 구조
- Handler: 모든 처리기(ConcreteHandler)가 구현해야 할 공통 인터페이스입니다. 다음 처리기를 연결하기 위한
setNext()
메서드와 요청을 처리하는handle()
메서드를 정의합니다. - ConcreteHandler:
Handler
인터페이스를 구현하는 구체적인 처리기입니다.handle()
메서드 내에서 자신의 역할을 수행하고, 다음 처리기에게 요청을 넘길지 결정합니다. - Client: 요청을 생성하고, 체인의 첫 번째
Handler
에게 요청을 전달하는 역할을 합니다.
예시 1: 고객센터 문의 시스템 (독점적 처리)
이 예시는 체인을 따라가다 하나의 처리기가 요청을 처리하면 거기서 종료되는 방식입니다.
typescript
// Handler: 모든 처리기의 공통 인터페이스
abstract class SupportHandler {
protected nextHandler: SupportHandler | null = null;
setNext(handler: SupportHandler): SupportHandler {
this.nextHandler = handler;
return handler;
}
handle(query: { type: string; message: string }): void {
if (this.nextHandler) {
this.nextHandler.handle(query);
} else {
console.log("아무도 처리할 수 없는 문의입니다.");
}
}
}
// ConcreteHandler: 기술 지원팀
class TechSupportHandler extends SupportHandler {
handle(query: { type: string; message: string }): void {
if (query.type === "tech") {
console.log(`[기술 지원팀] "${query.message}" 문의를 처리합니다.`);
} else {
super.handle(query); // 처리 못하면 다음으로 넘김
}
}
}
// ... 다른 핸들러들 (Billing, General) ...
클라이언트 코드에서 처리기들을 체인으로 엮고 요청을 보내면, 요청은 자신을 처리할 수 있는 단 하나의 핸들러를 만날 때까지 체인을 따라 이동합니다.
예시 2: 웹 서버 미들웨어 (포괄적 처리)
하나의 요청을 여러 처리기가 모두 순차적으로 처리하는 방식입니다. 각 처리기는 자신의 역할을 수행한 뒤, 요청을 종료시키지 않고 무조건 다음 처리기에게 전달하여 작업이 누적되도록 합니다.
typescript
// 처리될 요청 객체
interface HttpRequest {
path: string;
user?: { id: number; name: string }; // 인증 미들웨어가 채워줄 속성
}
// Handler 추상 클래스
abstract class Middleware {
protected next: Middleware | null = null;
setNext(middleware: Middleware): Middleware {
this.next = middleware;
return middleware;
}
// 이제 handle은 항상 다음을 호출하는 구조가 됨
handle(request: HttpRequest): void {
if (this.next) {
this.next.handle(request);
}
}
}
// ConcreteHandler 1: 인증 미들웨어
class AuthMiddleware extends Middleware {
handle(request: HttpRequest): void {
console.log("1. [인증] 사용자 토큰 검사 완료. 요청에 사용자 정보 추가.");
request.user = { id: 123, name: "김재현" };
super.handle(request); // 다음으로 전달
}
}
// ConcreteHandler 2: 로깅 미들웨어
class LoggingMiddleware extends Middleware {
handle(request: HttpRequest): void {
console.log(`2. [로깅] Path: ${request.path}, User: ${request.user?.name}`);
super.handle(request); // 다음으로 전달
}
}
// 최종 처리기
class AppController extends Middleware {
handle(request: HttpRequest): void {
console.log("3. [컨트롤러] 최종 비즈니스 로직 실행.");
// 체인의 끝이므로 super.handle()을 호출하지 않음
}
}
// 클라이언트 코드
const auth = new AuthMiddleware();
const logging = new LoggingMiddleware();
const controller = new AppController();
// 체인 연결: 인증 -> 로깅 -> 컨트롤러
auth.setNext(logging).setNext(controller);
const request: HttpRequest = { path: "/profile" };
console.log("--- 클라이언트 요청 발생 ---");
auth.handle(request);
이처럼 단일 요청이 체인을 따라가며 인증, 로깅 등 여러 작업을 순차적으로 적용받는 구조를 만들 수 있습니다.
책임 연쇄 패턴 중요 키워드
- 요청자와 수신자를 분리하고, 수신자들을 사슬처럼 연결합니다.
- 결합도를 낮춥니다: 요청자는 어떤 수신자가 요청을 처리하는지 알 필요가 없습니다.
- 하나의 요청을 여러 객체가 처리할 수 있습니다. (미들웨어 방식)
- 새로운 처리기를 추가하거나 순서를 변경하는 것이 유연합니다.
문제 | 요청을 처리할 수 있는 여러 객체들을 연결하여, 요청이 들어오면 순차적으로 처리 기회를 넘기는 패턴으로 요청자와 수신자를 분리하고, 어떤 객체가 요청을 처리할지 미리 알 필요 없이 동적으로 결정할 수 있는 디자인 패턴은? |
보기 | |
답변 | |
정답 | 정답 보기 |