Front Controller PatternSpring MVC 처리 흐름DispatcherServlet에서 컨트롤러로 HTTP 요청 위임핸들러 매핑 전략Handler AdapterHandler Adapter 전략HandlerMethodArgumentResolver 커스텀 구현DispatcherServlet의 뷰 호출과 모델 참조ViewResolver 종류ViewViewResolver 추가하기정적 리소스 처리하기(ResourceHandler)Resource Resolver 추가Model, ControllerModel?
Front Controller Pattern


- 중앙 집중형 컨트롤러를 제일 앞에다가 두고(서블릿이 하나) 등록되어져 있는 컨트롤러들에게 이 컨트롤러를 호출할 지 말지를 결정해서 필요하면 찾아서 실제 로직을 처리하는 부분을 위임. 응답 받은 거 가지고 뷰도 만들어주고
- Spring은 이 FrontController Pattern을 활용하여 DispatcherServlet이라는 것을 제공해 줌 (스프링 MVC를 사용하면 서블릿을 생성할 필요가 없음.
@Controller
와@RequestMapping
으로 DispatcherServlet에 알아서 컨트롤러가 등록됨)
- 앞 단의 Servlet에서 service()를 호출하면 doGet(), doPost() 이런게 아니라 다 DispatcherServlet으로 넘어감
Spring MVC 처리 흐름

- DispatcherServlet의 HTTP 요청 접수
- DispatcherServlet에서 컨트롤러로 HTTP 요청 위임(전달)
- 컨트롤러에서 모델 생성과 정보 등록 — 실제로 서비스를 호출하고 서비스에서 Entity를 만들게 되고 Entity를 가지고 비즈니스 로직이 처리되고 결과가 반환(컨트롤러에게). 그럼 컨트롤러는 그것을 통해 화면에 전달할 모델을 만듦
- 컨트롤러의 결과 리턴 : 모델과 뷰의 정보(뷰 이름)
- DispatcherServlet이 뷰에다가 모델을 전달하면서 뷰가 컨텐츠를 만들어 줌(이렇게 해서 렌더링 된 결과를 만들어줌)
- HTTP 응답 만들어서 반환
public class MyWebApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) { // Load Spring web application configuration AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(AppConfig.class); // Create and register the DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(context); ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); } }
- 위와 같은 방식으로 DispatcherServlet을 등록할 수 있음
- 여러개의 DispatcherServlet을 등록할 수 있지만, Spring에서는 DispatcherServlet은 하나만 만들고 밑에 Controller를 여러개 만드는 방식을 선호함
DispatcherServlet에서 컨트롤러로 HTTP 요청 위임
Flow : Handler Mapping → Handler Adapter가 해당 메서드의 파라미터로 들어온 값을 binding → Handler Interceptor 에서 요청 가로채서 처리 → 후 호출

- 요청의 url, 파라미터 정보, http 메서드를 참고하여 어떤 컨트롤러(핸들러) 에게 전달해야 할지 정함 ⇒ 핸들러 매핑
- request가 handler에게 매핑되는 과정
- DispatcherServlet이 요청을 받음
- HandlerMapping 인터페이스를 구현한 여러 개의 클래스 리스트를 갖고 있음. 해당 클래스의 전략을 활용하여 요청을 컨트롤러의 함수에 매핑함
핸들러 매핑 전략

Handler Adapter
- DispatcherServlet이 요청을 위임하는 대상인 컨트롤러에는 아무런 제약이나 선결 조건이 없어서, 어떤 종류의 오브젝트라도 컨트롤러로 사용할 수 있음
- 근데, 자바 오브젝트 사이에 무엇인가 요청이 전달되려면 메소드가 호출되어야 하고, 그러려면 DispatcherServlet이 컨트롤러 오브젝트 메소드를 호출할 수 있는 방법이 필요한데, 이때 Adapter가 필요한 것!
- 어떤 컨트롤러(핸들러)에게 전달할 지 정해지고 나서 그 핸들러의 파라미터 정보에 값들을 넘기기 위해서 값을 변환해 주는 역할을 하는 것이 Handler Adapter
- Handler Adapter가 handler의 실행을 트리거 하기 직전에 Handler Interceptor가 호출됨
- 모든 웹 요청의 정보가 담긴
HttpServletRequest 오브젝트
를 Handler Adapter에게 전달을 해주면 Adapter가 적절히 변환을 해서Controller의 메소드가 받을 수 있는 파라미터로 변환
해서 전달해줌
- 어떠한 Adapter를 쓸지는 HandlerAdapter 전략에 의해서 결정됨
Handler Adapter 전략

- RequestMappingHandlerAdapter를 가장 많이 씀. @RequestMapping이라는 어노테이션으로 컨트롤러 작성하면 RequestMappingHandlerAdapter가 작동됨
- @RequestMapping 어노테이션 이용 시, 아래 전략 사용하게 됨
- → @RequestMappingHandlerMapping
- → @RequestMappingHandlerAdapter

HandlerMethodArgumentResolver 커스텀 구현
- DispatcherServlet은 Controller를 실행해줄 HandlerAdapter를 찾는다.이 때, Adapter를 찾고 handle을 실행하기 위해 필요한 파라미터를 생성하기 위해 Resolver는 실행된다.
DispatcherServlet의 뷰 호출과 모델 참조

- Controller가 리턴한 결과를 가지고 어떠한 뷰를 만들어내야 할지를 결정하는 곳이 ViewResolver
- Controller에서 뷰의 이름과 모델 데이터를 전달하면 DispatcherServlet 내부에 ViewResolver가 포함되어 있고 View가 체인으로 묶여서 저장이 되어 있음
- ViewResolver는 해당 View체인을 보면서 Controller가 반환한 뷰의 이름에 해당하는 렌더링 해야 할 뷰를 반환 하고, View에서 그것을 렌더링 해서 ResponseBody에 실어서 전달
- ViewResolver는 view name을 View로 매핑함
ViewResolver 종류

- ContentNegotiatingViewResolver가 디폴트임. 전달되는 Content에 따라 어떤 View를 Resolve할 지가 정해짐
View
ViewResolver 추가하기
static class AppConfig implements WebMvcConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.jsp().viewNames("jsp/*"); SpringResourceTemplateResolver springResourceTemplateResolver = new SpringResourceTemplateResolver(); springResourceTemplateResolver.setApplicationContext(applicationContext); springResourceTemplateResolver.setPrefix("/WEB-INF/"); springResourceTemplateResolver.setSuffix(".html"); ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine(); springTemplateEngine.setTemplateResolver(springResourceTemplateResolver); viewResolver.setTemplateEngine(springTemplateEngine); viewResolver.setOrder(1); viewResolver.setViewNames(new String[]{"views/*"}); registry.viewResolver(viewResolver); } }
정적 리소스 처리하기(ResourceHandler)
public class PrgmWebApplicationInitializer implements WebApplicationInitializer { @EnableWebMvc // Spring MVC가 필요한 빈들 자동으로 등록됨 @Configuration @ComponentScan(basePackages = "org.prgms.customer") @EnableTransactionManagement static class AppConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/").setCachePeriod(); // ... } } @Override public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); applicationContext.register(AppConfig.class); var dispatcherServlet = new DispatcherServlet(applicationContext); var servletRegistratrion = servletContext.addServlet("test", dispatcherServlet); servletRegistratrion.addMapping("/"); servletRegistratrion.setLoadOnStartup(1); } }
Resource Resolver 추가
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/").setCachePeriod(60) .resourceChain(true) .addResolver(new EncodedResourceResolver()); }

- Resource중에서 알아서 최신 버전으로 찾아준다거나(resolve) 하는 등의 정책을 정해줄 수 있음
Model, Controller
// 첫번째 방법 @GetMapping("/customers") public ModelAndView findCustomers(){ List<Customer> customerList = customerService.getAllCustomers(); return new ModelAndView("views/customers", Map.of("serverTime", LocalDateTime.now(), "customers", customerList)); } // 다른 방법 @GetMapping("/customers") public String findCustomers(Model model){ List<Customer> customerList = customerService.getAllCustomers(); model.addAttribute("serverTime", LocalDateTime.now()); model.addAttribute("customers", customerList); return "views/customers"; }
Model?
- Spring framework의 Model instance를 구현하는 객체
- key-value 쌍의 컬렉션임
- model의 content는 application의 상태를 나타내고, 뷰를 렌더링할 때 이용됨
- model에 있는 value값은 비즈니스 로직을 포함할 수도 있음