이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • HandlerMapping은 왜 ControllerMapping이 아니라 HandlerMapping인가?
  • HandlerAdapter는 왜 Handler 검색과 별도로 존재하는가?
  • ArgumentResolver와 ReturnValueHandler는 HandlerAdapter와 어떻게 연결되는가?

개요

Spring MVC에서 DispatcherServlet은 요청을 처리할 Handler를 찾기 위해 HandlerMapping을 사용하고, 찾은 Handler를 실행하기 위해 HandlerAdapter를 사용한다. 검색과 실행은 분리되어 있다.

기존 MVC 문서의 HandlerMapping, HandlerAdapter 설명은 이 분리 구조를 정확히 짚고 있다. HandlerMapping은 요청에 맞는 Handler를 찾아 반환하고, HandlerAdapter는 그 Handler를 실제로 호출한다.

원리

요청 /orders/1이 들어왔다고 하자.

@GetMapping("/orders/{id}")
public OrderResponse getOrder(@PathVariable Long id) {
    return orderService.getOrder(id);
}

HandlerMapping은 URL, HTTP method, path pattern, produces/consumes 조건 등을 바탕으로 이 메서드를 Handler로 선택한다. 실제로는 Handler만 단독으로 넘기기보다 적용될 Interceptor까지 포함한 HandlerExecutionChain을 DispatcherServlet에 돌려준다. 이후 HandlerAdapter가 메서드 파라미터 id를 만들고, 메서드를 호출하고, 반환값을 처리 흐름으로 넘긴다.

Handler라는 넓은 개념

Spring MVC가 ControllerMapping이 아니라 HandlerMapping이라고 부르는 이유는 요청 처리 대상이 @Controller만이 아니기 때문이다. 정적 리소스 처리, HttpRequestHandler, 다른 handler type도 가능하다.

HandlerMapping은 “요청을 처리할 수 있는 대상”을 찾는다. 이 대상이 어떤 형태이든 DispatcherServlet은 직접 실행하지 않고 Adapter에게 맡긴다. Interceptor 적용 여부도 이 실행 체인에 묶이므로 “Handler를 찾는다”는 말에는 실제 실행 전후 흐름을 구성한다는 의미가 포함된다.

HandlerAdapter의 책임

HandlerAdapter는 Handler 실행 방식을 표준화한다. 특히 @RequestMapping 메서드를 실행하는 RequestMappingHandlerAdapter는 다음 작업을 수행한다.

  • 메서드 파라미터를 해석한다.
  • 필요한 argument를 만든다.
  • Controller 메서드를 호출한다.
  • 반환값을 해석한다.
  • ModelAndView 또는 response body 처리 흐름으로 결과를 넘긴다.

이 구조가 있기 때문에 Controller 메서드는 HttpServletRequest를 직접 파싱하지 않고도 다양한 타입의 인자를 선언할 수 있다.

실전 팁

  • mapping이 안 잡히면 HandlerAdapter보다 HandlerMapping 조건을 먼저 본다.
  • Interceptor가 요청을 막는다면 HandlerMapping은 성공했지만 Controller 실행은 시작되지 않을 수 있다.
  • 파라미터 바인딩이 이상하면 HandlerAdapter 내부의 ArgumentResolver 흐름을 의심한다.
  • 반환값이 이상하면 ReturnValueHandler나 MessageConverter 경로를 본다.
  • @GetMapping, @PostMapping은 결국 @RequestMapping의 특화된 형태다.
  • URL은 맞는데 HTTP method가 다르면 405, 요청 body media type이 맞지 않으면 415, 응답으로 만들 수 있는 media type이 맞지 않으면 406이 날 수 있다.

위험 신호!

  • HandlerMapping과 HandlerAdapter를 하나의 “컨트롤러 실행기”로 뭉뚱그린다.
  • Controller 메서드 파라미터가 자동 주입되는 이유를 설명하지 못한다.
  • URL이 맞는데도 405/415/406이 나는 문제를 path 문제로만 본다.
  • Interceptor에서 preHandle이 false를 반환했는데 Controller mapping 문제로만 추적한다.
  • 모든 요청 처리 대상을 @Controller로만 생각한다.
  • ArgumentResolver, ReturnValueHandler의 존재를 모른 채 바인딩 문제를 디버깅한다.

확인 질문

확인 질문

  • HandlerMapping은 무엇을 하는가?
    • 현재 요청을 처리할 Handler를 찾는다.
  • HandlerAdapter는 무엇을 하는가?
    • 찾은 Handler를 실행할 수 있는 방식으로 호출한다.
  • @RequestMapping 메서드 인자와 반환값 처리는 어디와 연결되는가?
    • RequestMappingHandlerAdapter의 ArgumentResolver와 ReturnValueHandler 흐름과 연결된다.

참고 문서