[Back-end]/[Spring]

[Spring] DispatcherServlet이란?

DevLoki 2021. 9. 18. 02:27

스프링 웹 MVC 프레임워크의 레퍼런스의 첫 문장은 다음과 같습니다.

The Spring Web model-view-controller (MVC) framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale and theme resolution as well as support for uploading files.
스프링 웹 MVC 프레임워크는 디스패처서블릿(DispatcherServlet)을 중심으로 설계되었다.

[Spring] DispatcherServlet이란?

 

디스패처서블릿은 HTTP 요청들을 매핑된 컨트롤러로 배차해주는 역할을 수행하는 중앙 서블릿입니다.

 

 

사용자의 요청을 처리하는 과정에서 다음과 같은 특수 Bean들을 사용합니다.

Bean Type 역할
HandlerMapping 요청들과 핸들러들을 HandlerMapping 구현체의 기준에 따라 매핑해줍니다.
HandlerAdapter 매핑된 핸들러를 호출합니다. 어댑터 패턴을 적용했기 때문에 디스패처서블릿이 핸들러의 어노테이션이나 파라미터에 대해 몰라도 됩니다.
HandlerExceptionResolver 예외들을 뷰에게 매핑해주고 다양한 예외처리 코드를 가능하게 해줍니다.
ViewResolver 문자열 기반의 논리적 뷰이름을 실제 뷰로 변환해줍니다.
LocaleResolver 사용자의 로케일을 기반으로 국제화된 뷰를 제공해줍니다.
ThemeResolver 웹 애플리케이션이 사용가능한 theme을 리졸브 해줍니다.
MultiPartResolver multi-part 요청들을 파싱하여 파일업로드와 같은 기능들을 지원합니다.
FlashMapResolver 입출력데이터를 FlashMap에 저장 및 반환하여 속성들을 한 요청끼리 전달할 수 있게 해줍니다. 주로 리다이렉트 시 사용됩니다.

 

별도로 설정하지 않았다면 DispatcherServlet.properties를 통해 어떤 우선순위로 특수 Bean이 적용되는지 확인할 수 있습니다.

 

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

 

FrontController 패턴

 

Front-Controller 패턴은 웹 애플리케이션에서 주로 사용되는 디자인 패턴으로, 컨트롤러에서 중복으로 처리해야 하는 공통 기능들을 처리하는 입구 역할을 하는 컨트롤러를 두는 개념입니다.

일반적이 페이지 컨트롤러 패턴 적용

Front-Controller 패턴을 적용하면 다수의 서블릿이 아닌 하나의 프런트 컨트롤러 서블릿으로 다수의 클라이언트 요청을 받을 수 있고, 다양한 HTTP 요청에 대해 공통 처리가 가능하여 코드 재사용성과 유연성을 높이며 중복을 줄일 수 있습니다. 또한 중앙 집중식 제어를 통해 보안이 강화되며 사용자 트랙킹에도 유리합니다. 

 

프론트 컨트롤러 패턴 적용

DispatcherServlet이 바로 위 그림에서 공통의 로직을 처리하는 Front-Controller 역할을 수행하게 됩니다. 

  • Spring MVC의 Front-Controller = DispatcherServlet

DispatcherServlet의 동작 원리

 

DispatcherServlet은 다음과 같이 HttpServlet을 상속받아서 사용하기 때문에

서블릿이 호출되면 HttpServlet.service() -> FrameworkServlet.service() -> DispatcherServlet.doDispatch()가 호출됩니다.

Diagram for DispatcherServlet

지금부터는 실제 DispatcherServlet의 소스코드를 직접 보며 동작 원리를 설명하겠습니다.

DispatcherServlet 동작 순서

 

스프링 애플리케이션이 실행되면 DispatcherServlet을 자동으로 등록하고 컴포넌트 스캔을 통해 @RequestMapping 어노테이션이 있는 핸들러들을 모두 매핑해둡니다.

 

  •  Http요청이 들어오면 DispatcherServlet은 HandlerMapping에서 요청과 매핑된 핸들러를 조회하고 가져옵니다. 만약 매핑된 핸들러를 찾지 못하면 noHandlerFound() 메서드를 실행하며 예외를 던집니다.
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
        
    // Determine handler for the current request.
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
      noHandlerFound(processedRequest, response);
      return;
    }

 

  • HandlerAdapter 목록에서 support() 메서드(해당 어댑터가 매개변수로 주어진 핸들러를 지원하는 여부를 판별하는 메서드)를 통해 일치하는 HandlerAdapter를 찾아 가져옵니다.
    // Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

 

  • HandlerAdapter를 통해 handle() 메서드를 호출하고 이 안에서 비즈니스 로직이 실행됩니다. HandlerAdapter는 리턴 값으로 viewname과 model을 반환합니다. 
    ModelAndView mv = null
    // Actually invoke the handler.
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 

  • ViewResolver를 통해 viewName을  실제 view로 반환받습니다.

 

    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
    render(mv, request, response);
}

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
        // We need to resolve the view name.
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
    }

 

  • render메서드를 통해 view를 생성하고 HTTP 응답 메시지를 생성하여 클라이언트에게 반환합니다.
    view.render(mv.getModelInternal(), request, response);
}

 

Spring Framework 웹 MVC의 핵심인 DispatcherServlet에 대해 알아보았습니다. Front-Controller 패턴의 적용과 동작원리를 파악하시면 웹의 전체적인 흐름을 이해하는데 큰 도움이 되실 겁니다.

 

읽어주셔서 감사합니다.