

Spring MVC 동작 순서
- 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(Controller) 를 조회한다.
- 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.
- 핸들러 어댑터 실행: 핸들러 어댑터를 실행
- 핸들러 실행: 핸들러 어댑터로 인해 실제 핸들러가 실행된다.
- ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView 로 변환하여 반환.
- viewResolver 호출: 뷰 리졸버를 찾아 실행한다.
- View 반환: 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 변환 후 Rendering 역할을 담당하는 뷰 객체를 반환한다.
- 뷰 렌더링: 뷰를 통해 뷰를 렌더링 한다.
Spring MVC도 프론트 컨트롤러 패턴으로 구현되어 있다. Spring MVC에서 프론트 컨트롤러 역할은 DispatcherServlet이 맡고 있으며, 이는 곧 Spring MVC의 핵심이다.
DispatcherServlet
DispatcherServlet의 서블릿 등록
- DispatcherServlet의 상위 클래스를 따라가다 보면 HttpServlet을 상속받아 서블릿으로 동작하고 있는 것을 알 수 있다.
- 스프링 부트가 내장 WAS (내장 톰캣 서버 등) 를 띄우는 동시에 DispatcherServlet을 서블릿으로 자동 등록한다.
- 그 과정에서 모든 경로('urlpatterns="/"')에 대하여 매핑해준다.
- 하지만 위와 같이 모든 경로에 대하여 매핑이 되면 해당 작업의 우선 순위가 가장 낮아져 더욱 자세한 하위 경로에 대한 매핑도 잘 동작한다.
doDispatch
- 서블릿이 호출된 후 HttpServlet이 제공하는 Service()가 호출된다.
- 최종적으로 제일 중요한
DispatcherServlet.doDispatch()가 호출된다.
1protected void doDispatch(HttpServletRequest request, HttpServletResponse response){2 ...3 ModelAndView mv = null; // modelAndView 사용4 ...5 // 1. 핸들러 조회6 mappedHandler = getHandler(processedRequest);7 if (mappedHandler == null) {8 noHandlerFound(processedRequest, response); // 핸들러가 없으면 404 오류9 return;10 }11 // 2. 핸들러 어댑터 조회12 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());13 ...14
15 // 3. 핸들러 어댑터 실행 -> 4. 핸들러 실행 -> 5. ModelAndView 반환16 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());17 ...18
19 // render()를 반환하는 processDispatchResult 가 실행된다.20 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException){21 ...22 // 뷰 렌더링 호출23 render(mv, request, response) {24 View view;25 String viewName = mv.getViewName();26 ...27 // 6. 뷰 리졸버를 통해서 뷰 찾기, 7. View 반환28 view = resolveViewName(viewName, mv.getModelInternal(), locale, request);29
30 // 8. 뷰 렌더링31 view.render(mv.getModelInternal(), request, response);32 }33 }34}주요 인터페이스
- 핸들러 매핑: org.springframework.web.servlet.HandlerMapping
- 핸들러 어댑터: org.springframework.web.servlet.HandlerAdapter
- 뷰 리졸버: org.springframework.web.servlet.ViewResolver
- 뷰: org.springframework.web.servlet.View
핸들러 매핑과 핸들러 어댑터
컨트롤러를 호출 하는것에 있어서 2가지의 전제 조건이 성립해야 한다.
- HandlerMapping(핸들러 매핑): 핸들러 매핑에서 해당 컨트롤러를 찾을 수 있어야 한다.
- HandlerAdapter(핸들러 어댑터): 핸들러 매핑을 통해서 찾은 핸들러를 실행할 수 있는 핸들러 어댑터가 필요하다.
Spring Boot 가 자동 등록하는 핸들러 매핑과 핸들러 어댑터
1// HandlerMapping20 = RequestMappingHandlerMapping // @RequestMapping 에서 사용 (대부분 이 방법을 사용)31 = BeanNameUrlHandlerMapping // 스프링 빈의 이름으로 핸들러를 찾는다.1// HandlerAdapter20 = RequestMappingHandlerAdapter // @RequestMapping 에서 사용31 = HttpRequestHandlerAdapter // HttpRequestHandler 처리42 = SimpleControllerHandlerAdapter // Controller 인터페이스 처리핸들러 매핑과 핸들러 어댑터를 위 순서대로 찾아 실행하며, 만약 없다면 다음 순서로 넘어가 찾는다.
핸들러 매핑 및 핸들러 어댑터의 조회 및 실행 과정
- 핸들러 매핑을 통한 핸들러 조회: HandlerMapping 을 위 순서대로 실행하여 핸들러를 찾는다.
- 핸들러 어댑터 조회: HandlerAdapter 의 supports()를 순서대로 호출한다.
- 핸들러 어댑터 실행: DispatcherServlet 이 조회한 핸들러 어댑터를 실행하면서 핸들러 정보도 함께 넘겨준다.
@RequestMapping
가장 우선 순위가 높은 핸들러 매핑과 어댑터는
RequestMappingHandlerMapping과RequestMappingHandlerAdapter이다. 실무에서는 99.9% 이 방식의 컨트롤러를 사용한다.
뷰 리졸버 (InternalResourceViewResolver)
- 스프링 부트는
InternalResourceViewResolver라는 뷰 리졸버를 자동으로 등록해준다. - application.properties 에 등록한 설정 정보를 사용해서 등록한다.
1spring.mvc.view.prefix=/WEB-INF/views/2spring.mvc.view.suffix=.jsp뷰 리졸버의 동작 방식
스프링 부트가 자동 등록하는 뷰 리졸버:
11 = BeanNameViewResolver // 빈 이름으로 뷰를 찾아서 반환한다.22 = InternalResourceViewResolver // JSP를 처리 할 수 있는 뷰를 반환한다.동작 과정
- 핸들러 어댑터 호출: ModelAndView("new-form") 을 통해 논리 뷰 이름을 획득
- ViewResolver 호출: 뷰 이름으로 viewResolver 를 순서대로 호출
- InternalResourceViewResolver:
InternalResourceView를 반환 - 뷰 - InternalResourceView:
forward()를 호출해서 처리 - view.render(): JSP를 실행
참고: Thymeleaf 뷰 템플릿을 사용하면
ThymeleafViewResolver를 등록해야 하며, 라이브러리만 추가하면 스프링 부트가 자동으로 등록해준다.