Docs/Spring

Spring Doc - Core - Web MVC

이쟁쟁 2021. 5. 3. 20:14

Spring framework Document 읽기

Spring Web MVC

  • Spring Web MVC는 Servlet API 기반으로 만들어진 웹 프레임워크 이며 Spring의 시작부터 포함되어 있었습니다.
  • Spring 5.0 부터는 Spring WebFlux가 소개되었고 MVC를 대체하여 사용할 수 있습니다.

DispatcherServlet

  • Spring MVC도 다른 web framework들과 마찬가지로 front controller 패턴을 중심으로 설계되었습니다. DispatcherServlet 에서 request 처리를 위임 하기 위한 알고리즘을 제공하고 실제 동작 수행은 각 작업에 맞는 컴포넌트에서 수행하게 됩니다.

  • DispatcherServlet 도 다른 servlet과 마찬가지로 web.xml 혹은 java configuration 방식으로 선언 및 맵핑이 필요합니다. 이렇게 설정한 DispatcherServlet은 request mapping, view resolution, exception handling 등에 필요한 컴포넌트를 찾는데 사용합니다.

    public class MyWebApplicationInitializer implements WebApplicationInitializer {
    
    @Override
    public void onStartup(ServletContext servletCxt) {
    
        // Load Spring web application configuration
        AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
        ac.register(AppConfig.class);
        ac.refresh();
    
        // Create and register the DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(ac);
        ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("/app/*");
      }
    }
    <web-app>
    
      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
      <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/app-context.xml</param-value>
      </context-param>
    
      <servlet>
          <servlet-name>app</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value></param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
      </servlet>
    
      <servlet-mapping>
          <servlet-name>app</servlet-name>
          <url-pattern>/app/*</url-pattern>
      </servlet-mapping>
    
    </web-app>

    Spring boot는 initialization 과정이 다르기 때문에 servlet container의 lifecycle hook을 사용해서 등록하지 않고, Spring configuration을 통해서 embedded servlet container를 부트스트랩 하며 이때 선언된 FilterServlet 도 함께 감지(detect)하여 컨테이너에 등록됩니다.

  • DispatcherServlet 의 설정을 위해서 WebApplicationContext 가 사용됩니다. WebApplicationContextServletContext 와 연관된 Servlet 에 대한 링크를 가지고 있으며 ServletContext 에 바인딩 되어 있기 때문에 어플리케이션에서 WebApplicationContext 에 접근이 필요한 경우 RequestContextUtils 의 static 메서드를 통해 접근할 수 있습니다.

Special Bean Types

  • DispatcherServlet 은 request를 처리하기 위해 특별한 bean들에게 역할을 위임합니다. 이 bean들은 Spring에 기본적으로 구현되어있지만 property 들을 커스터마이즈 하거나 상속받아서 대체하는게 가능합니다.
  • 이름 설명
    HandlerMapping -request를 pre/post interceptor들에 따라 handler에게 맵핑합니다.
    -@RequestMapping 을 지원하는 RequestMappingHandlerMappingSimpleUrlHandlerMapping 두가지가 주요 구현체로 존재합니다.
    HandlerAdapter - Handler가 실제로 어떻게 실행되는지와 별개로 DispatcherServlet 이 request를 handler에 맵핑할 수 있도록 도와주는 역할을 합니다.
    -@RequestMapping 같은 어노테이션이 달린 컨트롤러가 request를 처리하려면 어노테이션에 대한 처리를 해야하는데 이것을 HandlerAdapter 에서 수행해 줍니다.
    HandlerExceptionResolver 에러 페이지 띄우기, 다른쪽으로 예외 던지기 등 Exception을 처리하는 전략을 담당합니다.
    ViewResolver Handler에서 리턴된 String based view 이름을 response에 렌더링할 실제 View 로 만들어주는 작업을 수행합니다.
    LocaleResolver
    LocaleContextResolver
    사용자의 timezone에 맞는 Locale 을 가지고오는데 사용됩니다.
    MultipartResolver Multi-part request를 파싱하기 위한 라이브러리에서 사용가능한 추상화를 제공합니다.
    FlashMapManager Redirect와 같이 request의 attribute를 다른 request로 전달하기 위한 FlashMap 을 저장하고 꺼내오는데 사용됩니다.

Processing

  • DispatcherServlet 은 아래의 과정으로 request를 처리합니다.
    1. Request의 attribute를 사용하여 해당 request를 처리할 수 있는 element에 WebApplicationContext 를 바인딩합니다.
      • Default로 DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE 키에 따라 바인딩 됩니다.
    2. Locale resolver가 필요한 경우라면 request에 resolver가 바인딩
    3. Theme를 사용하는 경우 theme resolver가 바인딩
    4. Multipart request면서 multipart resolver를 명시해둔 경우 MultipartHttpServletRequest 로 랩핑됩니다.
    5. Request에 적합한 handler 검색 후 관련된 execution chain(preprocessor, postprocessor, controller)이 수행,
      Annotated controller의 경우 view를 반환하는 대신 HandlerAdapter 에서 response를 렌더
    6. Model이 반환된 경우 view가 렌더링 되며, 그렇지 않은경우 request는 이미 처리 된 것이기 때문에 view가 렌더링 되지 않음
  • WebApplicationContext 에 정의된 HandlerExceptionResolver 이 request 처리 과정에서 발생한 exception을 처리하는데 사용됩니다.
  • DispatcherServlet 은 Servlet API에 명시된 last-modification-date 값도 지원합니다.

Interception

  • 모든 HanlderMapping의 구현체는 handler interceptors를 지원 합니다.
  • PreHandle
    • boolean 값을 리턴하며 handler가 수행되기 전에 실행됩니다.
    • true 가 리턴되면 execution chain이 계속 수행되며, false 가 리턴되면 interceptor에서 요청 처리가 완료되었다고 가정하여 execution chain이 종료됩니다.
  • PostHandle
    • Handler가 수행된 이후 실행됩니다.
    • HandlerAdapter 에서 요청이 처리되는 경우에는(@ResponseBody, ResponseEntity 등) response에 추가적인 조작을 하기엔 너무 늦은 시점이 되기 때문에 그리 유용하지 못하며,
      이 경우에는 ResponseBodyAdvice 의 구현체를 추가하거나 RequestMappingHandlerAdapter bean을 추가해야 합니다.
  • afterCompletion
    • Request가 완전히 종료된 이후에 수행됩니다.

View Resolution

  • Spring MVC는 특별한 view 관련 기술을 사용하지 않고도 model을 렌더링 할 수 있도록 ViewResolver, View interface를 제공합니다.
  • ViewResolver 는 view의 이름과 실제 View 를 맵핑해 줍니다.

Handling

  • ViewResolver bean 추가 하여 View resolver chain에 여러개의 view resolver를 등록 할 수 있습니다.

Redirecting

  • redirect: prefix를 갖는 view 이름은 특별하게 취급되며 redirect에 사용됩니다.
    • UrlBasedViewResolver 에서 위의 prefix를 인식하고 redirect가 필요한지 판단하게 됩니다.
  • Controller에서 RedirectView 를 리턴한것과 동일한 효과를 갖지만 현재 context path를 기반으로 한 상대경로로 redirect 대상을 명시할 수 있습니다.
  • Controller에서 @ResponseStatus 를 명시한 경우 RedirectView 에서 설정한 상태코드보다 우선 반영 됩니다.

Forwarding

  • forward: prefix를 갖는 view 이름은 RequestDispatcher.forward() 를 호출하는 InternalResourceView 를 생성하며 UrlBasedViewResolver 를 통해 처리됩니다.

Content Negotiation

  • ContentNegotiatingViewResolver 는 직접 view를 resolve 하지 않으며, view를 처리할 다른 resolver에게 위임하는데 사용됩니다.
    • HTTP 헤더나 query parameter로 위임 대상을 확인합니다.

Locale

  • LocaleResolver 를 사용하여 request 에 맞는 언어로 응답을 해 줄 수 있습니다.