"로그인 체크도 해야 하고, 권한 검사도 해야 하고, 로그도 남겨야 하고, 데이터 압축도 해야 하는데..."
클라이언트의 요청 하나를 처리하기 위해 여러 개의 처리 과정(Handler)을 거쳐야 할 때, 이를 사슬(Chain)처럼 엮어서 순차적으로 실행하게 만드는 것이 책임 연쇄 패턴입니다. 하지만 이 사슬을 어떻게 구성하느냐에 따라 동작 방식이 완전히 다릅니다.

1. 방식 1: 순수 책임 연쇄 (Pure Chain) - "선착순 1명"
GoF 디자인 패턴에 나오는 고전적인 방식입니다. **"나 이거 처리 못 해? 그럼 다음 사람에게 넘겨"**라는 식입니다. 중요한 점은 누군가 처리를 완료하면 사슬은 거기서 멈춥니다.
상황 (지출 결재 시스템)
- 팀장: 10만 원 이하 전결
- 본부장: 100만 원 이하 전결
- 사장: 제한 없음
코드 예시
abstract class Approver {
protected Approver nextApprover; // 다음 결재자
public void setNext(Approver next) { this.nextApprover = next; }
public void processRequest(int amount) {
if (canApprove(amount)) {
approve(amount); // 내가 처리하고 끝! (체인 중단)
} else if (nextApprover != null) {
nextApprover.processRequest(amount); // 난 못하니 다음 사람에게 토스
} else {
System.out.println("처리할 사람이 없습니다.");
}
}
protected abstract boolean canApprove(int amount);
protected abstract void approve(int amount);
}
// 사용
teamLeader.setNext(director);
director.setNext(ceo);
teamLeader.processRequest(500000); // 팀장 Pass -> 본부장 처리(끝)
특징
- 배타적 처리: 핸들러 중 단 하나만 요청을 처리합니다.
- 예: GUI 이벤트(버튼이 클릭 안 되면 부모 패널로 클릭 이벤트 전파).
2. 방식 2: 필터 체인 (Filter Chain / Interceptor) - "릴레이 경주"
웹 개발(Spring)에서 주로 쓰이는 변형된 방식입니다. 모든 핸들러가 요청을 건드리고 지나갑니다. **전처리(Pre)와 후처리(Post)**가 모두 가능합니다.
상황 (웹 요청 처리 파이프라인)
- LoggingFilter: 요청 로그 남김
- AuthFilter: 인증 토큰 검사
- EncodingFilter: UTF-8 변환
코드 예시 (Servlet Filter 스타일)
interface Filter {
void doFilter(Request req, FilterChain chain);
}
class FilterChain {
private List<Filter> filters = new ArrayList<>();
private int index = 0;
public void addFilter(Filter filter) { filters.add(filter); }
public void doFilter(Request req) {
if (index < filters.size()) {
Filter filter = filters.get(index++);
filter.doFilter(req, this); // 다음 필터를 호출하라고 체인(this)을 넘김
}
}
}
class LoggingFilter implements Filter {
public void doFilter(Request req, FilterChain chain) {
System.out.println("1. [전처리] 로그 기록");
chain.doFilter(req); // 다음 사람에게 마이크 넘김 (여기서 멈추지 않음!)
System.out.println("4. [후처리] 로그 종료");
}
}
class AuthFilter implements Filter {
public void doFilter(Request req, FilterChain chain) {
System.out.println("2. [전처리] 인증 확인");
if (req.isAuth()) {
chain.doFilter(req); // 인증 성공 시에만 다음 진행
}
// 인증 실패하면 여기서 체인 끊김(반환)
}
}
특징
- 파이프라인 처리: 다수의 객체가 하나의 요청을 순서대로 가공합니다.
- 제어권: 각 필터가 chain.doFilter()를 호출할지 말지 결정하여, 흐름을 계속 이을지 중단할지 통제합니다.
3. 실무 예시: 스프링 시큐리티 (Spring Security)
스프링 시큐리티는 거대한 Filter Chain 덩어리입니다.
SecurityFilterChain 내부에는 CsrfFilter, UsernamePasswordAuthenticationFilter 등 수십 개의 필터가 연결되어 있으며, 요청이 들어오면 이 필터들을 싹 훑고 지나가며 보안 검사를 수행합니다.
요약: Pure Chain vs Filter Chain
| 구분 | Pure Chain (GoF) | Filter Chain (Web) |
| 처리 방식 | 책임 떠넘기기 (Pass the buck) | 파이프라인 (Pipeline) |
| 실행 핸들러 | 핸들러 중 하나만 실행됨 | 모든 핸들러가 순차적으로 실행됨 |
| 종료 시점 | 내가 처리 가능하면 즉시 종료 | 마지막 핸들러까지 갔다가 되돌아옴 |
| 대표 사례 | 결재 시스템, 예외 처리(try-catch) | 서블릿 필터, 스프링 시큐리티, 인터셉터 |
결론
"이 요청을 누가 처리할지 모르겠으니, 처리할 수 있는 놈이 나올 때까지 돌려보자"라면 Pure Chain입니다.
반면, "이 요청이 도착하기 전에 검문소를 5개 통과시켜야 한다"라면 Filter Chain입니다.
웹 백엔드 개발자라면 사실상 Pure Chain보다 Filter Chain 패턴을 훨씬 더 많이 사용하고 분석하게 될 것입니다.
'프로그래밍 > 디자인패턴' 카테고리의 다른 글
| 프로토타입 패턴(Prototype Pattern) 완벽 정리 (0) | 2025.12.07 |
|---|---|
| 커맨드 패턴(Command Pattern) 총정리 (0) | 2025.12.07 |
| 이터레이터 패턴(Iterator Pattern) 완벽 정리 (0) | 2025.12.07 |
| 상태 패턴(State Pattern) 완벽 정리 (0) | 2025.12.07 |
| 템플릿 메서드 패턴(Template Method Pattern) 총정리 (0) | 2025.12.07 |