스프링 웹 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()가 호출됩니다.
지금부터는 실제 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 패턴의 적용과 동작원리를 파악하시면 웹의 전체적인 흐름을 이해하는데 큰 도움이 되실 겁니다.
읽어주셔서 감사합니다.
'[Back-end] > [Spring]' 카테고리의 다른 글
[Spring] 스프링의 빈후처리기(AnnotationAwareAspectJAutoProxyCreator) (0) | 2021.12.07 |
---|---|
[Spring] 빈 후처리기(BeanPostProcessor) (0) | 2021.12.05 |
[Spring] 다이내믹 프록시(DynamicProxy) (1) | 2021.11.30 |
[Spring] 프록시와 디자인패턴 (1) | 2021.11.30 |
[Spring Boot] @SpringBootApplication이란? (0) | 2021.09.14 |