10. HandlerMapping → DispatcherServlet: HandlerExecutionChain 반환
HandlerMapping.getHandler() 가 HandlerExecutionChain 을 반환합니다. 이것은 단순히 핸들러(컨트롤러 메서드)만 반환하는 것이 아니라, 해당 요청을 처리할 때 필요한 모든 것을 한 번에 패킹하여 반환합니다.
시퀀스 다이어그램
sequenceDiagram
participant DispatcherServlet
participant HandlerMapping
participant Chain as HandlerExecutionChain
DispatcherServlet->>HandlerMapping: getHandler(request)
HandlerMapping->>HandlerMapping: 핸들러 매칭
HandlerMapping->>HandlerMapping: 적용 인터셉터 수집
HandlerMapping->>Chain: new(handler, interceptors...)
Chain-->>HandlerMapping: 생성 완료
HandlerMapping-->>DispatcherServlet: HandlerExecutionChain 반환
Note over DispatcherServlet,Chain: chain = { handler + interceptorList }
HandlerExecutionChain이란?
HandlerExecutionChain = {
handler: 실제 실행할 컨트롤러 메서드,
interceptorList: [인터셉터1, 인터셉터2, ...]
}
“핸들러와 인터셉터를 한 묶음으로 반환” 하는 이유는 무엇일까요?
왜 이런 설계를 했을까?
문제 상황: 핸들러만 반환한다면?
// ❌ 나쁜 설계: 핸들러만 반환
handler = getHandler();
// 그럼 인터셉터는?
// 1) 어디서 가져오지?
// 2) 어느 순서로 실행하지?
// 3) 같은 요청에 필요한 여러 인터셉터는 어떻게 조합하지?
해결책: 체인으로 함께 반환
// ✅ 좋은 설계: 핸들러 + 인터셉터를 함께 반환
HandlerExecutionChain chain = getHandler();
// 이미 조합되어 있으니:
// 1) handler = chain.getHandler() ← 핸들러 꺼내기
// 2) chain.applyPreHandle() ← 인터셉터들 정방향 실행
// 3) chain.applyPostHandle() ← 인터셉터들 역방향 실행
실제 코드 분석
HandlerExecutionChain의 구조
// HandlerExecutionChain.java
public class HandlerExecutionChain {
private final Object handler; // 실제 컨트롤러 메서드
@Nullable
private HandlerInterceptor[] interceptors; // 인터셉터 배열
private List<HandlerInterceptor> interceptorList; // 인터셉터 리스트
private int interceptorIndex = -1; // 마지막 실행된 인터셉터의 인덱스
// 생성자: 핸들러와 인터셉터들을 한 번에 받음
public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
this.handler = handler;
this.interceptors = interceptors;
}
}
HandlerMapping에서 체인 생성
// RequestMappingHandlerMapping.java (구현 클래스의 예)
@Override
@Nullable
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 1️⃣ HandlerExecutionChain 생성 (핸들러만 먼저)
HandlerExecutionChain chain = new HandlerExecutionChain(handler);
// 2️⃣ 해당 핸들러에 적용되는 모든 인터셉터를 추가
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor.matches(request, handler)) {
chain.addInterceptor(interceptor);
}
}
// 3️⃣ 완성된 체인 반환
return chain;
}
핵심: 왜 “샌드위치” 구조가 가능한가?
HandlerExecutionChain이 handler와 interceptor를 함께 반환하기 때문입니다.
┌─────────────────────────────────────┐
│ applyPreHandle() (정방향) │ ← 인터셉터 1, 2, 3 순서로
├─────────────────────────────────────┤
│ handler.execute() │ ← 컨트롤러 실행
├─────────────────────────────────────┤
│ applyPostHandle() (역방향) │ ← 인터셉터 3, 2, 1 순서로
└─────────────────────────────────────┘
이 모든 것이 하나의 chain 객체에 담겨 있어서 가능합니다!
🤔 탐구 질문
Step 10을 이해했다면, 이제 궁금한 점:
- preHandle과 postHandle이 역방향인 이유는?
- 인터셉터 3개가 있을 때, preHandle은 1→2→3 순서로 실행되는데
- postHandle은 3→2→1 순서로 실행되네요. 왜일까요?
- 만약 preHandle에서 인터셉터 2가 false를 반환하면?
- 인터셉터 3은 실행이 안 되겠네요
- 그럼 afterCompletion은 어떻게 되나요?
다음 단계인 Step 11: preHandle() 실행으로 넘어가기 전에, 위 질문들을 생각해보세요! 🧠