자바 디자인 패턴 용어 사전
행위 패턴책임 연쇄 · CoR

Chain of Responsibility

요청을 처리기 사슬에 흘려보내 누군가 처리하게 하는 패턴. 송신자와 수신자를 분리한다.

요청을 처리기 사슬에 흘려보내 처리할 수 있는 객체가 받게 하는 패턴.

abstract class Handler {
    private Handler next;
    Handler setNext(Handler n) { this.next = n; return n; }

    void handle(Request req) {
        if (canHandle(req)) process(req);
        else if (next != null) next.handle(req);   // 못 하면 다음으로
    }
}

// 사슬을 조립한다
auth.setNext(rateLimit).setNext(cache).setNext(controller);

무엇을 푸는가

if (조건A) 처리A else if (조건B) 처리B

  • else if (조건C) 처리C — ← 조건이 늘 때마다 이 코드를 고쳐야 한다

  • 각 처리를 객체로 떼고 사슬로 연결하면

    • 순서 변경·추가·제거가 조립부만 바꾸면 된다 (OCP)

두 가지 변형

  • 순수형 — 하나가 처리하면 사슬이 끝난다 (예외 처리, 승인 요청)

  • 변형형 — 전부가 순서대로 관여한다 (필터 체인, 미들웨어)

  • 실무에서 흔한 것은 변형형이다

실무에서

javax.servlet.Filter · FilterChain          // 서블릿 필터
Spring Security FilterChainProxy            // 인증·인가 필터 사슬
OkHttp Interceptor · Netty ChannelPipeline
SLF4J·Logback 로거 계층 (부모로 전파)

대가

  • 아무도 처리하지 않으면 요청이 조용히 사라진다 → 종단 처리기를 둔다
  • 사슬이 길면 디버깅이 어렵다 (어디서 걸렸는지 추적)
  • 순서 의존성이 생긴다 (인증이 캐시보다 앞에 있어야 한다)

면접 함정

  • "모든 핸들러가 반드시 실행된다" → 순수형은 처리되면 멈춘다.
  • Decorator와 혼동 → Decorator는 기능 추가, CoR은 처리 위임이 목적이다.

서블릿 필터로 감을 잡는다

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
    log.info("before");
    chain.doFilter(req, res);      // ← 안 부르면 사슬이 여기서 끊긴다
    log.info("after");
}

인증 실패 시 이걸 이용해 차단하고, 실수로 빠뜨리면 요청이 컨트롤러에 도달하지 않는다(흔한 버그).

순서가 곧 규칙이다

인증 → 인가 → 레이트리밋 → 캐시 → 컨트롤러

캐시가 인증보다 앞이면 → 인증 안 된 요청에 캐시된 응답을 준다 (보안 사고) 레이트리밋이 인증보다 앞이면 → 인증 비용 전에 차단해 자원을 아낀다 (의도적)

그래서 Spring Security는 필터 순서를 명시적으로 관리한다.

관찰 가능성

사슬이 길면 "어디서 걸렸나"를 알기 어렵다. 각 핸들러에 처리 여부·소요 시간을 남기고, 요청 ID를 사슬 전체에 전파한다(MDC·trace id).

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 행위 패턴 ① — 요청을 객체로 다룬다