티스토리 뷰
DispatcherServlet vs DispatcherHandler
DispatcherServlet에서 Spring MVC의 핵심 구성 요소로 HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러(Front Controller) 패턴을 기반으로 동작하는 서블릿이다. 그러면 DispatcherHandler는 무엇일까? DispatcherHandler는 Spring WebFlux에서 DispatcherServlet처럼 요청을 처리하는 역할을 하는 비동기(reactive) 프론트 컨트롤러이다. 둘을 자세히 비교해보자.
DispatcherServlet
DispatcherServlet도 부모 클래스에서 HttpServlet을 상속받아서 사용하고, 모든 경로(urlPatterns="/")에 대해서 매핑한다. 그리고 김영한님 강의를 보면 확인할 수 있는 그림과 동작 순서는 다음과 같다.
- 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다.
- 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.
- 핸들러 어댑터 실행: 핸들러 어댑터를 실행한다.
- 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행한다.
- ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환한다.
- viewResolver 호출: 뷰 리졸버를 찾고 실행한다.
- View 반환: 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다.
- 뷰 렌더링: 뷰를 통해서 뷰를 렌더링한다.
요청 흐름

서블릿이 호출되면 HttpServlet이 제공하는 service()가 호출된다. 스프링 MVC는 위 코드처럼 DispatcherServlet의 부모인 FrameworkServlet에서 service()를 오버라이드해두었다.
FrameworkServlet.service()를 시작으로 doGet() -> processRequest() -> doService()를 거쳐 DispatcherServlet의 doDispatch()가 호출된다.
doDispatch() 코드 분석
public class DispatcherServlet extends FrameworkServlet {
@Nullable
private List<HandlerMapping> handlerMappings;
@Nullable
private List<HandlerAdapter> handlerAdapters;
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
// ...
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
Exception dispatchException = null;
// 1. 핸들러 조회
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
// 2. 핸들러 어댑터 조회 - 핸들러를 처리할 수 있는 어댑터
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
// 3. 핸들러 어댑터 실행 -> 4. 핸들러 어댑터를 통해 핸들러 실행 -> 5. ModelAndView 반환
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
}
}
핸들러 매핑(HandlerMapping) 조회

요청을 처리하기 위해서는 핸들러 매핑에서 핸들러(컨트롤러)를 찾을 수 있어야 한다. 예를 들어, 우리가 자주 사용하는 컨트롤러는 애노테이션 기반의 컨트롤러인 @RequestMapping가 적용된 컨트롤러를 사용하기 때문에 RequestMappingHandlerMapping가 이에 해당한다.

해당하는 핸들러 매핑(HandlerMapping)이 있는 경우 처리하고자 하는 컨트롤러를 찾은 것을 확인할 수 있다.
핸들러 어댑터(HandlerAdapter)

핸들러 매핑을 통해 찾은 핸들러를 실행할 수 있는 핸들러 어댑터가 필요하다. 앞서 RequestMappingHandlerMapping으로부터 찾은 핸들러는 RequestMappingHandlerAdapter를 사용하여 핸들러(컨트롤러)를 실행할 수 있다.
조회된 RequestMappingHandlerAdapter 객체를 통해 handler 메서드에 request, response, handler를 넘겨줌으로써 사용자가 원하는 요청을 처리할 수 있도록 핸들러(컨트롤러)를 실행할 수 있게 된다.

컨트롤러에서 지금까지 실행된 StackTrace을 확인하면 handle 메서드 이후 여러 메서드를 거쳐 사용자가 원하는 컨트롤러가 실행된 것을 확인할 수 있다.
processDispatchResult

위 코드처럼 @Controller를 사용하여 ModelAndView를 반환하는 경우에는 뷰를 통해 뷰를 렌더링한다. 예제에서는 Thymeleaf를 사용했는데 그러면 ThymeleafView를 통해 해당 뷰가 렌더링된다.
DispatcherHandler
DispatcherHandler도 DispatcherServlet과 마찬가지로 HandlerMapping을 통해서 핸들러(컨트롤러)를 찾고, 핸들러를 실행시키기 위한 HandlerAdapter를 찾아 핸들러를 실행시켜 사용자 요청을 처리한다. DispatcherHandler의 처리 과정은 다음과 같다.
- DispatcherHandler가 요청을 받음
- HandlerMapping을 통해 적절한 핸들러(HandlerMethod, HandlerFunction 등)을 찾음
- HandlerAdapter가 핸들러를 실행할 수 있는지 supports()로 확인
- handler()을 호출하여 핸들러 실행 -> HandleResult 반환
- 결과를 HandlerresultHandler가 받아 최종 응답 생성
HandlerMapping
public interface HandlerMapping {
Mono<Object> getHandler(ServerWebExchange exchange);
}
public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware {
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return this.createNotFoundError();
} else {
return CorsUtils.isPreFlightRequest(exchange.getRequest()) ?
this.handlePreFlight(exchange) :
Flux.fromIterable(this.handlerMappings)
.concatMap((mapping) -> { // handlerMapping 순회
return mapping.getHandler(exchange); // 지원하는지 확인
})
.next() // 첫 번째로 매핑되는 핸들러 선택
.switchIfEmpty(this.createNotFoundError()) // 매칭되는 핸들러가 없다면 404 반환
.onErrorResume((ex) -> {
return this.handleResultMono(exchange, Mono.error(ex));
}).flatMap((handler) -> {
return this.handleRequestWith(exchange, handler); // 찾은 핸들러를 실행
});
}
}
}
HandlerMapping은 Spring Webflux에서 요청을 적절한 핸들러(컨트롤러)로 매핑하는 인터페이스이다. ServerWebExchange가 주어져 지원하는 handler를 Mono로 반환하고, 지원하는 handler가 없다면 Mono.empty를 반환한다.
DispatcherHandler에서 코드를 확인하면 HandlerMapping을 concatMap 오퍼레이터를 사용하여 순회하면서 mapping.getHandler(exchange)를 통해 지원하는 핸들러(컨트롤러)를 찾고 지원하는 핸들러가 없다면 createNotFoundError가 실행되고, 있는 경우 handleRequestsWith(exchange, handler)를 실행하게 된다.
ServerWebExchange
public interface ServerWebExchange {
ServerHttpRequest getRequest();
ServerHttpResponse getResponse();
Map<String, Object> getAttributes();
// Session 정보를 담고 있는 WebSession publisher를 반환
Mono<WebSession> getSession();
// ...
}
위에서 사용되는 ServerWebExchange 객체는 Spring WebFlux에서 HTTP 요청과 응답을 캡슐화한 객체로 Spring MVC의 ServletRequest와 ServletResponse를 대체한다고 볼 수 있다. 각각의 getter를 사용하여 ServerHttpRequest, ServerHttpReponse 객체를 통해 요청 및 응답 정보를 관리할 수 있다.
HandlerMapping 종류

HandlerMapping 인터페이스의 여러 구현체가 존재한다. 개발자가 구현한 핸들러에 따라 지원하는 HandlerMapping을 찾아 핸들러를 반환한다. RequestMappingHandlerMappingf은 @RequestMapping 기반 매핑을 지원하고, RouterFunctionMapping은 함수형 라우팅을, SimpleUrlHandlerMapping은 URL 패턴을 특정 핸들러에 직접 매핑되어 각각 해당하는 메서드 또는 함수를 반환한다고 할 수 있다.
@RestController
@RequestMapping("/api")
public class HelloController {
@GetMapping("/hello")
public Mono<String> hello() {
return Mono.just("hello");
}
}
만약 위 코드처럼 @RequestMapping이 붙은 컨트롤러를 생성하고 /api/hello 요청이 들어오면, RequestMappingHandlerMapping가 해당 요청을 지원하기 때문에 hello() 메서드를 HandlerMethod로 반환한다. 그리고 HandlerAdapter가 해당 핸들러를 실행시킨다.
HandlerAdapter와 HandlerResultHandler
public interface HandlerAdapter {
// handlerMapping을 통해서 받은 handler를 지원하는지 여부
boolean supports(Object handler);
// ServerWebExchange와 handler를 받아서 요청을 처리하고 HandlerResult를 Mono로 반환
Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler);
}
public interface HandlerResultHandler {
// handlerAdapter를 통해서 받은 handlerResult를 지원하는지 여부
boolean supports(HandlerResult result);
// ServerWebExchange와 result를 받아서 응답 작업을 수행하고 작업이 완료된 시점을 Mono<Void>로 반환
Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result);
}
public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware {
private Mono<Void> handleRequestWith(ServerWebExchange exchange, Object handler) {
if (ObjectUtils.nullSafeEquals(exchange.getResponse().getStatusCode(), HttpStatus.FORBIDDEN)) {
return Mono.empty();
} else {
if (this.handlerAdapters != null) {
Iterator var3 = this.handlerAdapters.iterator();
while(var3.hasNext()) { // handlerAdapter 순회
HandlerAdapter adapter = (HandlerAdapter)var3.next();
if (adapter.supports(handler)) { // 현재 handler를 지원하는지 확인
// 지원하는 handler를 찾으면 실행
Mono<HandlerResult> resultMono = adapter.handle(exchange, handler);
// 핸들러 실행 결과를 최종적으로 처리하는 메서드 호출
return this.handleResultMono(exchange, resultMono);
}
}
}
return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}
}
}
위 HandlerAdapter는 Spring WebFlux에서 요청을 처리할 핸들러(HandlerMethod, HandlerFunction, WebHandler 등)를 실행하는 역할을 담당하는 인터페이스이다. 위 handlerRequestWith 메서드에서 확인할 수 있듯이 supports()를 사용하여 전달된 handler 객체를 처리할 수 있는지 확인하고, 처리 가능하다면 handle()에 ServerWebExchange와 handler를 넘겨 사용하여 실제 사용자 요청을 처리하는 것을 확인할 수 있다.
private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult handlerResult, String description) {
if (this.resultHandlers != null) {
Iterator var4 = this.resultHandlers.iterator();
while(var4.hasNext()) {
HandlerResultHandler resultHandler = (HandlerResultHandler)var4.next();
if (resultHandler.supports(handlerResult)) {
description = description + " [DispatcherHandler]";
return resultHandler.handleResult(exchange, handlerResult).checkpoint(description);
}
}
}
return Mono.error(new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue()));
}
HandlerResultHandler는 핸들러 실행 결과(HandlerResult)를 HTTP 응답으로 변환하는 역할을 하는 인터페이스이다. 위 코드에서 확인할 수 있듯이 adapter.handle()가 실행된 결과 Mono<HandlerResult> 객체를 넘겨받아 handlerResultMono에서 실행하고 있다. handlerResult 메서드 내부에서는 지원하는 HandlerResultHandler 구현체를 찾아 handleResult를 실행한다.
HandlerAdapter
HandlerAdapter는 HandlerMapping에서 반환된 객체들을 처리하기 위해 아래와 같이 네 가지 케이스로 구분할 수 있다.
- RequestMappingHandlerAdapter: HandlerMethod 지원 및 처리
- HandlerFunctionAdapter: HandlerFunction 지원 및 처리
- SimpleHandlerAdapter: WebHandler 지원 및 처리
- WebSocketHandlerAdapter: WebSocketHandler 지원 및 처리
HandlerResultHandler
HandlerResultHandler는 HandlerAdapter를 통해 실행된 결과(HandlerResult)를 처리하기 위해 아래와 같이 네 가지 케이스로 구분할 수 있다.
- ResponseEntityResultHandler: ResponseEntity를 처리하여 상태 코드, 헤더, 본문 등을 응답으로 설정한다.
- ResponseBodyResultHandler: HTTP 본문에 데이터를 직접 설정하여 응답을 생성한다.
- @ResponseBody가 이에 해당
- ViewResolutionResultHandler: ModelAndView를 처리하여 뷰를 렌더링하고, HTML 응답을 생성한다.
- @ModelAttribute, String, Rendering, Model, Map, View 등이 이에 해당
- ServerResponseResultHandler: WebFlux 함수형 라우팅에서 ServerResponse를 처리하여 응답을 생성한다.
출처
스프링 MVC 1
[Spring] Dispatcher-Servlet(디스패처 서블릿)이란? 디스패처 서블릿의 개념과 동작 과정
Spring Webflux - DispatchHandler
'Java > Spring' 카테고리의 다른 글
Netty (0) | 2025.02.05 |
---|---|
JPA (0) | 2024.03.26 |
회원가입 동시성 이슈 테스트 - DB Unique 활용하기 (0) | 2023.10.30 |
빈 후처리기(BeanPostProcessor) (0) | 2023.10.17 |
프록시 팩토리를 통한 AOP (1) | 2023.10.16 |
- Total
- Today
- Yesterday
- mdcfilter
- transaction
- TDD
- 트랜잭션
- 비관적 락
- 분산 락
- socket
- jvm 메모리 구조
- dispatcherservlet vs dispatcherhandler
- NeXTSTEP
- 카프카
- 넥스트스탭
- dispatcherhandler
- Java
- mysql
- postgresql
- 람다
- spring webflux
- Synchronized
- spring session
- nginx
- annotation
- 구름톤챌린지
- sql
- redis session
- 구름톤 챌린지
- pessimistic lock
- nginx configuration
- Kafka
- webflux dispatcherhandler
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |