HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤩
개발
/
Spring
Spring
/
🥾
DispatcherServlet, Spring MVC
🥾

DispatcherServlet, Spring MVC

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

Front Controller Pattern

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

Spring MVC 처리 흐름

notion image
  1. DispatcherServlet의 HTTP 요청 접수
  1. DispatcherServlet에서 컨트롤러로 HTTP 요청 위임(전달)
  1. 컨트롤러에서 모델 생성과 정보 등록 — 실제로 서비스를 호출하고 서비스에서 Entity를 만들게 되고 Entity를 가지고 비즈니스 로직이 처리되고 결과가 반환(컨트롤러에게). 그럼 컨트롤러는 그것을 통해 화면에 전달할 모델을 만듦
  1. 컨트롤러의 결과 리턴 : 모델과 뷰의 정보(뷰 이름)
  1. DispatcherServlet이 뷰에다가 모델을 전달하면서 뷰가 컨텐츠를 만들어 줌(이렇게 해서 렌더링 된 결과를 만들어줌)
  1. 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 에서 요청 가로채서 처리 → 후 호출
notion image
  • 요청의 url, 파라미터 정보, http 메서드를 참고하여 어떤 컨트롤러(핸들러) 에게 전달해야 할지 정함 ⇒ 핸들러 매핑
  • request가 handler에게 매핑되는 과정
    • DispatcherServlet이 요청을 받음
    • HandlerMapping 인터페이스를 구현한 여러 개의 클래스 리스트를 갖고 있음. 해당 클래스의 전략을 활용하여 요청을 컨트롤러의 함수에 매핑함

핸들러 매핑 전략

notion image

Handler Adapter

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

Handler Adapter 전략

notion image
  • RequestMappingHandlerAdapter를 가장 많이 씀. @RequestMapping이라는 어노테이션으로 컨트롤러 작성하면 RequestMappingHandlerAdapter가 작동됨
  • @RequestMapping 어노테이션 이용 시, 아래 전략 사용하게 됨
    • → @RequestMappingHandlerMapping
    • → @RequestMappingHandlerAdapter
어댑터를 이용한 임의의 컨트롤러 호출 방식
어댑터를 이용한 임의의 컨트롤러 호출 방식

HandlerMethodArgumentResolver 커스텀 구현

HandlerMethodArgumentResolver 사용하기
  1. DispatcherServlet은 Controller를 실행해줄 HandlerAdapter를 찾는다.이 때, Adapter를 찾고 handle을 실행하기 위해 필요한 파라미터를 생성하기 위해 Resolver는 실행된다.

DispatcherServlet의 뷰 호출과 모델 참조

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

ViewResolver 종류

notion image
  • 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)

[참고] https://www.baeldung.com/spring-mvc-static-resources
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()); }
notion image
  • 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값은 비즈니스 로직을 포함할 수도 있음