[Spring MVC] DispatcherServlet
boot-2.2.6(spring-5.2.5) 기준으로 작성하였다.
스프링 웹 어플리케이션을 개발하면서 항상 나의 시작점은 @Controller
였다.
쿼리 스트링으로 id=pplenty
작성하여 호출하면 당연하게 메서드에 매개변수 String id
를 추가하여 전달 받는다.
Http 요청을 파싱하고 전달인자를 만들어 메서드를 호출해주는 것은 어떤 방식으로 이루어질지가 궁금하다.
브라우저를 통한 HTTP 요청이 내가 작성한 Controller 의 특정 메서드까지 도달하기 까지의 과정을 분석해보자.
DispatcherServlet
스프링은 모든 요청을 하나의 서블릿(DispatcherServlet)으로 받아서, 처리할 수 있는 핸들러에게 요청을 전달해준다. 이러한 방식을 Front Controller 패턴
이라 부른다. 또한 핸들러로부터 반환된 값을 기반으로 HTTP 응답을 만드는 View 를 찾아 데이터를 전달한다.
스프링 MVC 의 핵심 프로세스를 담고 있는 DispatcherServlet
에 대해 정리해보기로 한다.
용어 정리
스프링은 네이밍이 잘 되어 있어 이름만으로도 어느 정도 역할을 유추해낼 수 있다. HTTP 요청을 처리하는 과정에서 자주 등장하는 인터페이스에 대해 먼저 정리해보았다.
HandlerMapping
: HTTP 요청과 핸들러를 매핑 해주는 역할을 한다. getHandler 메서드를 통해 HTTP 요청에 매핑된 핸들러를 가져올 수 있다.
대표적인 구현체로는 RequestMappingHandlerMapping 가 있다. ApplicationContext 가 refresh 되는 과정에서 생성 및 초기화된다.Handler
: HTTP 요청을 핸들링하는 역할을 한다.@Controller
클래스의@RequestMapping
이 붙은 메서드가 여기에 포함된다.HandlerAdapter
: Handler method 를 실행(invoke)해주는 역할을 한다. ModelAndView 객체를 반환한다.HandlerMethodArgumentResolver
: HandlerMethod 의 파라미터와 HTTP 요청 객체를 분석하여, Handler 가 실행될 때 전달되어질 Argument 를 찾는다.HandlerMethodReturnValueHandler
: HandlerAdapter 내부에서 Handler 가 반환하는 값을 처리하는 역할을 한다.
ModelAndView 객체를 만들거나, 경우에 따라서는 HTTP 응답을 직접 응답하기도 한다.Model
: 핸들러에서 처리/가공되어 View 로 전달되는 데이터를 담은 객체이다.View
: model 의 값을 인자로 받아, HTTP 응답을 만들어 응답한다.(render)ModelAndView
: LinkedHashMap 를 상속받은 ModelMap 과 View(또는 view name) 의 정보를 담고 있다.ViewResolver
: view name 을 해석해 어떤 View 로 응답을 만들지 결정한다.
HTTP 요청 처리 과정
당연한 얘기지만 DispatcherServlet
도 서블릿 구현체이기 때문에 doService 를 @Override
하고 있고, 요청/응답 객체를 전달 인자로 doDispatch 함수를 호출한다.
doDispatch 함수에 HTTP 요청을 처리하여 응답하는 모든 과정이 담겨 있다. 처리하는 과정 아래와 같이 주요한 부분을 순서대로 정리해보았다.
실제 코드는 스프링 깃헙 에서 볼 수 있다.(물론 스프링 프로젝트에서 직접 열어봐도 된다.)
HandlerExecutionChain getHandler(HttpServletRequest request)
HTTP 요청을 핸들링할 핸들러를 HandlerMapping 을 통해 조회한다. HandlerMapping 구현체 중 하나가 RequestMappingHandlerMapping 이다.
요청된 URL 에 매칭되는 핸들러(Handler)와 인터셉터 정보(HandlerInterceptor[])로 HandlerExecutionChain 을 생성하여 반환한다.
HandlerAdapter getHandlerAdapter(Object handler)
핸들러를 실행시켜줄 적절한 어댑터를 가져온다.
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response)
핸들러 체인에 등록된 인터셉터들의preHandle
함수를 실행한 뒤, false 가 나오면 바로 함수를 반환한다.
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
어댑터를 통해 핸들러를 실행한고 ModelAndView 를 반환한다. 경우에 따라서는 이 부분에서 응답이 완료 되는 경우도 있다.
예를 들면@ResponseBody
를 포함한 컨트롤러의 경우, RequestResponseBodyMethodProcessor(HandlerMethodReturnValueHandler 의 구현체) 에 의해 OutputStream 으로 직접 응답하고 null 을 반환한다.
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv)
핸들러 체인에 등록된 인터셉터들의postHandle
함수를 실행한다. 핸들러 실행후 반환된 ModelAndView 도 함께 인자로 받는다.
void processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)
결과를 처리하는 부분이다. 핸들러 실행으로 반환된 view 가 존재할 경우, 이 부분에서 뷰 종류를 판별하고 뷰를 이용해 응답(render)한다.