Spring/Spring Boot

[Spring Boot] 3. Web: Servlet Web Application

noahkim_ 2023. 10. 12. 16:05

1. The “Spring Web MVC Framework”

  • Spring Web MVC는 풍부한 기능을 가진 "model-view-controller" 웹 프레임워크 입니다.

 

Controller

  • @Controller (or @RestController) 빈으로 들어오는 요청을 처리하도록 돕습니다
  • @Controller 빈의 메서드는 @RequestMapping을 사용하여 요청을 URI 기반으로 매핑할 수 있습니다.

 

ConversionService

  • 객체의 타입을 다른 타입으로 변환하는 인터페이스입니다.
  • Spring Boot에서는 ApplicationConversionService를 기본 구현체로 사용합니다.
    • @ConfigurationProperties 바인딩, SpringWeb MVC 포맷에서 주로 사용됩니다.

 

ApplicationConversionService
  • 변환 기능을 중앙화하여 제공하는 구현체로, 타입 변환 작업을 일관성있고 효과적으로 수행할 수 있게 합니다.
  • 많은 기본 컨버터와 포맷터를 가집니다.
    • 컨버터나 포맷터를 추가할 수 있습니다.

 

@ConfigurationProperties 바인딩
  • ApplicationConversionService는 DateTimeFormatter에 변환작업을 위임하여 처리됩니다.
  • Period, Duration, DataSize와 같은 타입의 특수한 Formatter를 기본으로 지원합니다.
    • ConfigurationPropertiesBindingPostProcessor에 의해 DataBinder에 위임되며 특수한 Formatter가 동작합니다.

 

SpringWeb MVC
  • SpringWeb MVC 에서는 Period, Duration, DataSize와 같은 타입의 특수한 Formatter를 기본으로 지원하지 않습니다.
  • ApplicationConversionService를 기반으로 하지만 설정이나 활성화되는 컨버터/포맷터가 다르므로 차이가 발생합니다.
  • 이를 적용하기 위해 ConversionService 커스터마이징 기능을 제공합니다.
    • WebMvcConfigurer 빈의 addFormatters()로 Formatter를 등록할 수 있습니다.
    • ApplicationConversionService 빈의 addConverter()로 Converter를 등록할 수 있습니다.
    • spring.mvc.format.* properties를 사용할 경우 자동으로 DateTimeFormatter에 위임하여 처리합니다.

 

HttpMessageConverters

HttpMessageConverter
  • Spring MVC는 HttpMessageConverter의 구현체를 사용하여 HTTP request or response를 Java 객체로 직렬화합니다.
  • JSON, XML 타입을 기본 라이브러리인 jackson을 사용하여 직렬화합니다.
    • JSON : MappingJackson2HttpMessageConverter
    • XML : MappingJackson2XmlHttpMessageConverter, Jaxb2RootElementHttpMessageConverter
      • jackson xml extension이 없을 경우 JAXB를 사용하여 처리합니다.
  • 커스터마이징한 HttpMessageConverter를 Bean으로 등록
    • WebMvcConfigurer에 configureMessageConverters 혹은 extendMessageConverters 메서드를 오버라이드 합니다.
    • HttpMessageConverters를 Bean으로 등록합니다.
@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = new AdditionalHttpMessageConverter();
        HttpMessageConverter<?> another = new AnotherHttpMessageConverter();
        return new HttpMessageConverters(additional, another);
    }
}

 

MessageCodesResolver

  • binding 실패 시에, 실패 메시지를 위한 에러코드를 만드는 역할을 담당합니다.
  • 에러코드 생성 전략을 property를 통해 선택할 수 있습니다
    • spring.mvc.message-codes.resolver-format
    • PREFIX_ERROR_CODE : errorCode.objectname.field 순으로 생성됩니다.
    • POSTFIX_ERROR_CODE : field.objectname.errorCode 순으로 생성됩니다.

 

Static Content

  • Spring Boot는 정적 리소스를 제공합니다
    • 기본 디렉토리 : /static 
    • 대체 디렉토리 : /public, /resource, /META-INF/resources
  • spring.mvc.static-path-pattern : 정적 리소스의 요청 URL 패턴을 설정할 수 있습니다.
  • spring.web.resources.static-locations : 정적 리소스 기본 디렉토리를 설정할 수 있습니다.
  • Spring MVC는 ResourceHttpRequestHandler를 사용하여 리소스를 서빙합니다.
    • 추가적인 ResourceHttpRequestHandler 등록은 WebMvcConfigurer의 addResourceHandlers 메서드를 사용합니다.

 

Path Matching and Content Negotiation

  • HTTP 요청의 url을 컨트롤러에 매핑하여 요청을 처리합니다.
  • 이때, HTTP request path의 접미사 패턴 일치는 사용하지 않습니다.
    • querystring을 사용하여 컨텐츠 형식을 지정합니다.
    • http request header의 컨텐츠 협상 헤더인 'Accept'를 사용합니다.
  • 기본적으로 요청에 대한 핸들러(컨트롤러)가 없다면 404 응답을 반환합니다.
  • 정적 컨텐츠가 기본적으로 /**로 매핑되므로 핸들러가 없다면 정적 리소스로 제공할 수 있습니다.
  • NoHandlerFoundException을 던지도록 설정하기
    • spring.mvc.throw-exception-if-no-handler-found : true
    • spring.mvc.static-path-pattern : /resources/**
    • spring.web.resources.add-mappings : false (정적 리소스 제공 비활성화)
  • Spring MVC는 요청 경로를 컨트롤러에 매핑하기 위한 두 가지 전략을 지원합니다.
  • 원하는 매칭 전략을 spring.mvc.pathmatch.matching-strategy property를 통해 구성할 수 있습니다.

 

PathPatternParser
  • Spring 5.3부터 도입된 새로운 패턴매칭 전략입니다.
  • AntPathMatcher보다 효율적인 방식으로 패턴 매칭을 수행합니다.
  • 일부 경로 패턴 변형에 대한 제한이 있습니다
    • 패턴분석을 엄격하게 수행합니다.
    • 여러 섹션을 abridged 하는 패턴은 사용할 수 없습니다.
    • /{varName:.*}
  • DispatcherServlet의 경로 접두사와 호환되지 않습니다.

 

AntPatternMatcher
  • 기본 매칭 전략입니다.
  • Ant 스타일의 패턴을 사용하여 URL 경로와 매칭합니다.
  • PathPatternParser보다 유연하지만, 대규모 애플리케이션에서는 덜 효율적일 수 있습니다.

 

ConfigurableWebBindingInitializer

WebDataBinder
  • 웹 요청의 파라미터를 Java Bean에 바인딩하는 역할을 담당합니다.
    • Data Binding : 요청 파라미터, 세션 속성 및 Servlet 요청 속성을 모델 객체에 바인딩합니다.
    • Type Conversion : 등록된 PropertyEditor나 Converter를 사용하여 타입 변환을 수행합니다.
    • Validation : 등록된 Validator를 사용하여 데이터 유효성 검사를 수행합니다.

 

WebBindingInitializer
  • 각 요청에 대한 WebDataBinder의 초기화를 담당합니다.
    • PropertyEditor, Validator, Converter등을 WebDataBinder에 등록합니다.
  • 공통의 바인딩 설정을 여러 컨트롤러에서 재사용하기 위한 목적을 가집니다.

 

ConfigurableWebBindingInitializer
  • WebBindingInitializer 인터페이스를 구현하는 클래스입니다.
  • 컨트롤러 메소드에서 HTTP 요청의 매개변수를 바인딩 하기 위해 WebDataBinder 초기화를 수행합니다.

 

CORS Support

  • Cross-origin resource sharing 은 브라우저의 기본 보안 정책인 동일 출처 정책을 완화하기 위한 정책입니다.
  • cross-domain의 요청에 대한 인가를 세세하게 설정할 수 있습니다. 
    • origin, http method, http header
  • CORS 설정은 핸들러당 설정부터 전역적인 설정까지 셋팅이 가능합니다.

 

3. Embedded Servlet Container Support

Registering Servlets, Filters, and Listeners as Spring Beans

  • embedded servlet container를 사용할 때, servlet, filter, listener를 빈으로 등록할 수 있습니다.
    • ServletRegistrationBean, FilterRegistrationBean, ServletListenerRegistrationBean
    • 세부설정을 제공하며 빈 등록이 가능합니다.
    • application.properties property 값을 참조하는데 유용합니다.
    • Bean의 이름이 경로 접두사로 사용됩니다.

 

Filter
  • Filter 순서를 설정할 수 있습니다.
    • @Order
    • Ordered 인터페이스
    • FilterRegistrationBean의 setOrder
  • 요청 본문을 읽는 Filter의 우선순위
    • 요청 스트림은 일회용이므로 다른 Filter나 Controller에서 요청 본문을 읽을 수 없습니다.
    • 가장 높은 우선순위로 가리키는 것을 피해야 합니다 (Ordered.HIGHEST_PRECEDENCE) 
  • 요청을 감싸는 Filter의 우선순위
    • 원래의 요청 객체를 변경하거나 추가적인 동작을 수행하기 위해 다른 요청 객체로 감쌉니다.
    • Ordered.REQUEST_WRAPPER_FILTER_MAX_ORDER 이하로 설정되어야 합니다.
      • 요청 감싸기 동작이 다른 필터들에 올바르게 반영되도록 보장하기 위함입니다. 
  • DelegatingFilterProxyRegistrationBean
    • 다른 빈과 상호작용하는 Filter 빈들은 매우 빠른 시점에 초기화되므로 완전한 상태가 아닐 수 있습니다
    • 아와 같은 경우를 방지하기 위해 DelegatingFilterProxy를 프록시 필터로 등록하여 Bean Filter에 작업을 위임하도록 합니다.

 

Servlet Context Initialization

  • ServletContextInitializer 인터페이스의 onStartup 메서드를 구현하여 ServletContex 초기화를 담당합니다.
  • WebApplicationInitializer를 가지고 구현합니다.
    • web.xml 을 대체할 수 있습니다.

 

Scanning for Servlets, Filters, and listeners

  • @WebServlet, @WebFilter, @WebListener로 빈을 등록할 수 있습니다.
  • @ServletComponentScan 어노테이션으로 위의 빈을 스캔할 수 있습니다.

 

The ServletWebServerApplicationContext

  • Spring Boot는 내장 서블릿 컨테이너별 다른 타입의 ApplicationContext를 생성합니다.
  • 서블릿 컨테이너별 ServletWebServerFactory가 ServletWebServerApplicationContext를 빈으로 등록합니다.
    • WebServer를 제어할 수 있습니다.
    • ServletContext를 빈으로 등록합니다.
  • Bean의 초기화 중 ServletContext 초기화는 서로 다른 생명주기와 초기화 시점을 갖습니다.
    • Bean의 초기화 중 ServletContext 접근이 필요할 수 있습니다.
    • ServletContext가 초기화 되지 않은 상태이면 Bean이 접근할 수 없어 Bean의 초기화가 실패합니다.
  • 이를 위해 ApplicationListener를 사용하여 서버 시작후에 ServletContext에 접근하여 초기화를 완전하게 시도합니다
public class MyDemoBean implements ApplicationListener<ApplicationStartedEvent> {

    private ServletContext servletContext;

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        this.servletContext = ((WebApplicationContext) applicationContext).getServletContext();
    }
}

 

SameSite Cookie

  • 쿠키의 크로스 사이트 요청 동작을 제어하는 속성입니다.
  • Spring Boot에서는 이 속성을 설정하거나 변경하는 여러 방법을 제공합니다.
    • server.servlet.session.cookie.same-site 
    • CookieSameSiteSupplier를 구현하여 특정 조건의 쿠키의 SameSite attribute를 지정해줍니다.
@Configuration
public class CookieConfig {

    @Bean
    public CookieSameSiteSupplier cookieSameSiteSupplier() {
        return (Cookie cookie) -> {
            if (cookie.getName().matches("myapp.*")) {
                return "Lax";  // SameSite의 Lax 값을 반환합니다.
            }
            return null;  // 그 외의 쿠키는 변경하지 않습니다.
        };
    }
}

 

attribute value
  • Strict
    • 동일한 사이트의 요청에만 전송됩니다.
  • Lax
    • 탐색 요청을 통해 이루어지는 크로스 사이트 요청에는 쿠키 전송이 허용됩니다.
    • 외부 사이트에서 폼 제출이나 AJAX 요청을 통한 크로스 사이트 요청은 쿠키가 전송되지 않습니다.
  • None
    • 모든 크로스 사이트 요청에 쿠키가 전송됩니다.

 

 

 

참고