29.1 The “Spring Web MVC Framework”

Spring Web MVC framework (간단히 "Spring MVC"라고도 함)는 풍부한 "모델 뷰 컨트롤러"웹 프레임 워크입니다. Spring MVC는 들어오는 HTTP 요청을 처리하기 위해 특별한 @Controller 또는 @RestController 빈을 생성하게한다. 컨트롤러의 메서드는 @RequestMapping 어노테이션을 사용하여 HTTP에 매핑됩니다.

다음 코드는 JSON 데이터를 처리하는 일반적인 @RestController를 보여줍니다.

@RestController
@RequestMapping(value="/users")
public class MyRestController {

	@RequestMapping(value="/{user}", method=RequestMethod.GET)
	public User getUser(@PathVariable Long user) {
		// ...
	}

	@RequestMapping(value="/{user}/customers", method=RequestMethod.GET)
	List<Customer> getUserCustomers(@PathVariable Long user) {
		// ...
	}

	@RequestMapping(value="/{user}", method=RequestMethod.DELETE)
	public User deleteUser(@PathVariable Long user) {
		// ...
	}

}

Spring MVC는 핵심 Spring 프레임 워크의 일부이며 자세한 정보는 reference documentation에서 확인할 수있다. spring.io/guides에서 Spring MVC를 사용할 수있는 여러 가이드가있다.

29.1.1 Spring MVC 자동 구성

Spring Boot는 대부분의 어플리케이션에서 잘 동작하는 Spring MVC를위한 자동 설정 기능을 제공한다.

자동 설정은 Spring의 기본값 위에 다음과 같은 기능을 추가합니다 :

스프링 부트 MVC 기능을 유지하고 추가적인 MVC configuration (인터셉터, 포맷터, 뷰 컨트롤러 및 기타 기능)을 원하는 경우 @EnableWebMvc가 아닌 WebMvcConfigurer유형의 @Configuration 클래스를 추가 할 수 있습니다. RequestMappingHandlerMapping, RequestMappingHandlerAdapter또는 ExceptionHandlerExceptionResolver의 사용자 정의 인스턴스를 제공하려는 경우 WebMvcRegistrationsAdapter인스턴스를 선언하여 이러한 구성 요소를 제공 할 수 있습니다.

Spring MVC를 완벽하게 제어하려면 @EnableWebMvc로 주석이 달린 @Configuration을 추가하면 됩니다.

@EnableWebMvc

  • 어노테이션 기반의 Spring MVC를 구성할 때 필요한 빈 설정들을 자동으로 해주는 어노테이션. 또한 기본적으로 등록해주는 빈들 이외에 추가적으로 개발자가 필요로 하는 빈들의 등록을 손쉽게할 수 있도록 도와준다. (웹 MVC를 이용하는 데에 있어서 스프링 컨테이너가 가져야 할 기본적인 빈 컴포넌트를 등록해서 편하게 MVC를 구축할 수 있는 Configuration 환경을 제공해준다)

예) 스프링 3에서 등장한 @MVC(@RequestMapping, @RequestBody, @ResponseBody) 등의 스타일을 위해서 등록되어야 하는 RequestMappingHandler, RequestMappingHandlerAdapter, ExceptionHandlerExceptionResolver 등의 등록을 자동으로 해준다.

자동 구성된 스프링 MVC 구성을 큰 변경 없이 추가적으로 조작하는 방법?

  • @Configuration 선언!

    • WebMvcConfigurer

      • @EnableWebMvc 어노테이션에서 제공하는 빈을 커스터마이징할 수 있는 기능을 제공하는 인터페이스 (자동 구성된 스프링 MVC 구성에 Formatter, MessageConverter 등을 추가 등록할 수 있다)

    • WebMvnRegistrations

      • RequestMappingHandlerMapping, RequestMappingHandlerAdapter, ExceptionHandlerExceptionResolver를 재정의할 수 있다.

자동 구성된 스프링 MVC 구성을 개발자가 완벽히 제어하는 방법?

  • 첫 번째 방법:@Configuration@EnableWebMvc를 함께 선언!

    • @EnableWebMvc를 선언하면 WebMvcConfigurationSupport에서 구성한 스프링 MVC 구성을 불러온다.

  • 두 번째 방법: @Configuration@EnableWebMvc를 함께 선언한 클래스가 WebMvcConfigurer 인터페이스 구현

    • WebMvcConfigurationSupport에서 자동구성한 스프링 MVC 구성에 Formatter, MessageConverter 등을 추가적으로 등록할 수 있다.

  • 세 번째 방법: @EnableWebMvc 없이 스프링 MVC 구성을 변경하는 방법

29.1.2 HttpMessageConverters

Spring MVC는 HTTP 요청과 응답을 변환하기 위해 HttpMessageConverter인터페이스를 사용한다. 의미있는 기본값이 기본적으로 포함됩니다. 예를 들어, 객체는 JSON (Jackson 라이브러리를 사용하여) 또는 XML (Jackson XML 확장이 사용 가능한 경우 이를 사용하거나 Jackson XML 확장을 사용할 수없는 경우 JAXB를 사용하여 자동으로 변환 될 수 있음)으로 자동적으로 변환될 수 있습니다. 기본적으로 문자열은 UTF-8로 인코딩됩니다.

컨버터를 추가하거나 사용자 정의해야하는 경우 다음 목록과 같이 Spring Boot의 HttpMessageConverters클래스를 사용할 수 있습니다.

import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration
public class MyConfiguration {

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

}

컨텍스트에있는 HttpMessageConverterbean이 컨버터 목록에 추가됩니다. 같은 방법으로 기본 컨버터를 재정의 할 수도 있습니다.

29.1.3 사용자 정의 JSON Serializers and Deserializers

Jackson을 사용하여 JSON 데이터를 serialize 및 deserialize하는 경우 사용자 고유의 JsonSerializerJsonDeserializer 클래스를 작성할 수 있습니다. 커스텀 Serializer는 일반적으로 모듈을 통해 Jackson에 등록되지만 Spring Boot는 Spring Bean을 직접 등록하기 쉬운 @JsonComponent 어노테이션을 제공합니다.

JsonSerializer또는 JsonDeserializer구현에서 직접 @JsonComponent 어노테이션을 사용할 수 있습니다. 다음 예제와 같이 내부 클래스로 serializer / deserializers가 포함 된 클래스에서 사용할 수도 있습니다.

import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.jackson.*;

@JsonComponent
public class Example {

	public static class Serializer extends JsonSerializer<SomeObject> {
		// ...
	}

	public static class Deserializer extends JsonDeserializer<SomeObject> {
		// ...
	}

}

ApplicationContext의 모든 @JsonComponent Bean은 자동으로 Jackson에 등록됩니다. @JsonComponent@Component로 메타 주석이 붙어 있기 때문에 일반적인 구성 요소 검색 규칙이 적용됩니다.

Ex. "favoriteColor" JSON을 객체화 ! (Serialize)

Spring Boot는 또한 JsonObjectSerializerJsonObjectDeserializer기본 클래스를 제공하여 객체를 직렬화 할 때 표준 Jackson 버전에 대한 유용한 대안을 제공합니다. 자세한 내용은 Javadoc의 JsonObjectSerializerJsonObjectDeserializer를 참조하십시오.

29.1.4 MessageCodesResolver

Spring MVC는 오류를 바인딩해서 오류 메시지를 렌더링하기위한 오류 코드를 생성하는 전략을 가지고있습니다: MessageCodesResolver. spring.mvc.message-codes-resolver.format속성을 PREFIX_ERROR_CODE또는 POSTFIX_ERROR_CODE로 설정하면 Spring Boot가 자동으로 생성합니다 (DefaultMessageCodesResolver.Format의 enumeration 참조).

MessageCodesResolver

Errors 인터페이스 : 커멘드 객체의 검증 결과를 저장한다.

BindingResult 인터페이스 : 요청 파라미터 값을 커맨드 객체에 복사한 결과를 저장하며 에러 코드로부터 에러 메시지를 가져온다.

AbstractbindingResult 클래스 : BindingResult 인터페이스의 기본 구현 클래스. 검증 결과를 저장하고 에러 메시지를 추출하는 등의 기능을 제공한다.

BindingResult 인터페이스의 기본 구현 클래스인 AbstractBindingResult 클래스는 MessageCodeResolver를 사용하여 에러 코드에 대한 에러 메시지를 추출한다.

  • DefaultMessageCodeResolver를 기본으로 사용

29.1.5 정적 콘텐츠

기본적으로 스프링 부트는 classpath 또는 ServletContext루트에서 /static(또는 /public 또는 /resources 또는 /META-INF/resources) 디렉토리의 정적 컨텐츠를 제공합니다. Spring MVC의 ResourceHttpRequestHandler를 사용하므로 addResourceHandlers메소드를 재정의하는 WebMvcConfigurer를 추가하여 해당 동작을 수정할 수 있습니다.

ResourceHttpRequestHandler

  • 정적 리소스에 대한 처리 수행. 경로 잡거나 등등

독립형 웹 응용 프로그램에서, 컨테이너의 기본 서블릿도 활성화되어 Spring에서 처리하지 않기로 결정한 경우 ServletContext의 루트에서 컨텐트를 제공하는 대체 역할을 합니다. Spring이 항상 DispatcherServlet을 통해 요청을 처리 할 수 ​​있기 때문에 대부분의 경우 이 작업은 수행되지 않습니다

기본적으로 리소스는 /** 에 매핑되지만 spring.mvc.static-path-pattern 속성을 사용하여 튜닝 할 수 있습니다. 예를 들어, 모든 리소스를 /resources/** 로 재배치하는 것은 다음과 같이 수행 할 수 있습니다.

spring.mvc.static-path-pattern=/resources/**

기본 리소스 위치

  • classpath:/static

  • classpath:/public

  • classpath:/resources/

  • classpath:/META-INF/resources

    • 예) "/hello.html" 접근시 /static/hello.html 응답

  • spring.mvc.static-path-pattern: 맵핑 설정 변경 가능

    • application.properties에서 spring.mvc.static-path-pattern: /resources/** 으로 설정 변경시

    • localhost:8080/hello.html => localhost:8080/resources/hello.html로 접근

spring.resources.static-locations 등록 정보 (기본값을 디렉토리 위치 목록으로 바꾸기)를 사용하여 정적 자원 위치를 사용자 정의 할 수도 있습니다. 루트 서블릿 컨텍스트 경로 "/"가 위치로 자동 추가됩니다.

spring.mvc.static-locations: 리소스 찾을 위치 변경 가능

  • 기존의 기본 리소스 위치를 사용하지 않고 변경한 위치만 사용하므로 권장하지 않는 방법이다.

  • 이 방법 보다는 WebMvcConfigurer를 구현상속받아서 addResourceHandlers로 커스터마이징 하는 방법이 더 좋다. 기본 리소스 위치를 사용하면서 추가로 필요한 리소스위치만 정의해서 사용할 수 있다.

    • localhost:8080/m/hello.html 접근시 resources/m/hello.html 리턴

앞서 언급 한 "표준"정적 리소스 위치 외에도 Webjars content에 특별한 경우가 있습니다. /webjars/**에 경로가있는 자원은 Webjars 형식으로 패키지 된 경우 jar 파일에서 제공됩니다.

응용 프로그램이 jar로 패키지화되어있는 경우 src/main/webapp 디렉토리를 사용하지 마십시오. 이 디렉토리는 일반적인 표준이지만, war 패키징에서만 작동하며 병?을 생성하면 대부분의 빌드 도구에서 자동으로 무시됩니다.

Spring Boot는 또한 Spring MVC가 제공하는 고급 리소스 처리 기능을 지원하므로 캐시 무효화 정적 리소스 또는 Webjars 용 버전 불가지론 URL 사용과 같은 사용 사례를 허용합니다.

Webjars에 대해 버전에 관계없는 URL을 사용하려면 webjars-locator-core 종속성을 추가하십시오. 그런 다음 Webjar를 선언하십시오. 예를 들어 jQuery를 사용하면 "/webjars/jquery/jquery.min.js"를 추가하면 "/webjars/jquery/x.y.z/jquery.min.js"가됩니다. 여기서 x.y.z는 Webjar 버전입니다.

Webjars

  • 클라이언트에서 사용하는 웹 라이브러리(jquery와 bootstrap)를 JAR 파일 안에 패키징한 것

[참고사이트] https://java.ihoney.pe.kr/428

webjars-locator-core 의존성을 추가하면 리소스에서 webjars를 사용할 때, 버전을 생략할 수 있다. 이런 버전생략이 가능한 원리는 스프링에서 제공하는 리소스 체이닝이라는 기술과 관련이 있다. 리소스 트랜스포머를 체이닝하는 기능이 이러한 버전 생략을 가능하게 해준다. 깊게 알아야할 내용은 아니지만 버전 생략이 가능하다 라는 정도만 알아두면 된대..

JBoss를 사용하는 경우 webjars-locator-core 대신 webjars-locator-jboss-vfs 종속성을 선언해야합니다. 그렇지 않으면 모든 Webjars가 404로 해결됩니다.

캐시 무효화를 사용하기 위해 다음 구성에서는 모든 정적 리소스에 대한 캐시 무효화 솔루션을 구성하여 URL에 <link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>와 같은 콘텐츠 해시를 효과적으로 추가합니다.

스프링 리소스 체인 활성화

spring.resources.chain.strategy.content.enabled

ResourceChainRegistration 의 기본 aspect을 설정할 수 있다. 이는 사용자가 캐시파열cache busting을 구현하기 위한 고유한 리소스 이름을 만들수 있게 해준다. spring.resources.chain.strategy.content. 프로퍼티는 만약 사용자가 사용자의 fingerprint에 "고정된 버전"fixed version을 쓰길 원하면 리소스의 내용에 기초해 fingerprinting을 설정하는데 쓰인다.

Resource versioning

이것을 Fingerprinting URL이라고 부르기도 한다. 스프링에 추가된 기능은 RoR의 asset pipeline에 기초를 둔다. 이 기능의 요점은 다음과 같다. 가령, /css/mystyle.css 이라는 파일이 있다고 하면 자동적으로 /css/mystyle-f1l521f.css 같은 이름으로 바꿔서 제공한다. 이렇게 되면 HTTP header의 캐시를 얼마든지 길게 하더라도 언제나 최신의 리소스를 제공할 수 있게 된다. 특히, 저 버전은 css파일의 내용을 hash한 값이므로 파일의 내용이 변경되지 않는다면 파일 이름에도 변경이 생기지 않아 캐시가 효율적으로 동작한다.

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

리소스 링크는 Thymeleaf 및 FreeMarker에 대해 자동 구성되는 ResourceUrlEncodingFilter덕분에 런타임에 템플릿에 다시 작성됩니다. JSP를 사용할 때 이 필터를 수동으로 선언해야합니다. 다른 템플릿 엔진은 현재 자동으로 지원되지 않지만 사용자 지정 템플릿 매크로 / 도우미와 ResourceUrlProvider를 사용할 수 있습니다.

예를 들어 자바 스크립트 모듈 로더와 같이 리소스를 동적으로로드 할 때 파일 이름 바꾸기는 옵션이 아닙니다. 그래서 다른 전략도 지원되고 결합 될 수 있습니다. "고정"전략은 다음 예와 같이 파일 이름을 변경하지 않고 URL에 정적 버전 문자열을 추가합니다.

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
spring.resources.chain.strategy.fixed.enabled=true
spring.resources.chain.strategy.fixed.paths=/js/lib/
spring.resources.chain.strategy.fixed.version=v12

이 구성을 사용하면 "/js/lib/"아래에있는 JavaScript 모듈은 고정 된 버전 관리 전략 ( "/v12/js/lib/mymodule.js")을 사용하지만 다른 리소스는 여전히 내용 하나 (<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>).

지원되는 옵션에 대해서는 ResourceProperties를 참조하십시오.

이 기능은 전용 blog post와 Spring Framework의 reference documentation에 자세히 설명되어 있습니다.

29.1.6 Welcome Page

Spring Boot는 정적 및 템플릿된 웰컴 페이지를 모두 지원합니다. 먼저 구성된 정적 컨텐츠 위치에서 index.html 파일을 찾습니다. 하나도 찾지 못하면 index템플릿을 찾습니다. 둘 중 하나가 발견되면 자동으로 응용 프로그램의 시작 페이지로 사용됩니다.

29.1.7 사용자 정의 Favicon

스프링 부트는 구성된 정적 컨텐츠 위치와 classpath의 루트 (순서대로)에서 favicon.ico를 찾습니다. 이러한 파일이 있는 경우 자동으로 응용 프로그램의 favicon으로 사용됩니다.

파비콘

  • 웹 페이지에 접속했을 때 상단 탭에 보여지는 아이콘. (즐겨찾기에 웹 페이지를 등록할 때도 사용 - 웹 사이트를 대표하는 로고의 개념과 비슷)

29.1.8 경로 일치 및 콘텐츠 협상

Spring MVC는 요청 경로를 보고 애플리케이션에 정의된 매핑 (예 : 컨트롤러 메소드의 @GetMapping 어노테이션)과 일치시켜 들어오는 HTTP 요청을 핸들러에 매핑 할 수있습니다.

Spring Boot는 기본적으로 접미어 패턴 일치를 비활성화하도록 선택합니다. 즉 "GET /projects/spring-boot.json"과 같은 요청은 @GetMapping("/projects/spring-boot") 매핑과 일치하지 않습니다. 이것은 best practice for Spring MVC applications로 간주됩니다. 이 기능은 적절한 "Accept"요청 헤더를 보내지 않은 HTTP 클라이언트에 대해 과거에는 주로 유용했습니다. 우리는 올바른 Content Type을 클라이언트에 보내야합니다. 요즘, 콘텐츠 협상은 훨씬 더 신뢰할 수 있습니다.

적절한 "Accept"요청 헤더를 지속적으로 보내지 않는 HTTP 클라이언트를 다룰 수있는 다른 방법이 있습니다. 접미어 일치를 사용하는 대신 "GET /projects/spring-boot?format=json"과 같은 요청이 @GetMapping("/projects/spring-boot")에 매핑되도록 쿼리 매개 변수를 사용할 수 있습니다. T

spring.mvc.contentnegotiation.favor-parameter=true

# We can change the parameter name, which is "format" by default:
# spring.mvc.contentnegotiation.parameter-name=myparam

# We can also register additional file extensions/media types with:
spring.mvc.contentnegotiation.media-types.markdown=text/markdown

주의 사항을 이해하고 여전히 접미어 패턴 일치를 사용하려는 응용 프로그램을 원한다면 다음 구성이 필요합니다.

spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-suffix-pattern=true

또는 모든 접미어 패턴을 열지 않고 등록 된 접미사 패턴을 지원하는 것이 더 안전합니다.

spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-registered-suffix-pattern=true

# You can also register additional file extensions/media types with:
# spring.mvc.contentnegotiation.media-types.adoc=text/asciidoc

29.1.9 ConfigurableWebBindingInitializer

스프링 MVC는 WebBindingInitializer를 사용하여 특정 요청에 대해 WebDataBinder를 초기화한다. 자신만의 ConfigurableWebBindingInitializerBean을 생성하면 스프링 부트는 자동으로 스프링 MVC를 사용하도록 설정합니다.

WebBindingInitializer를 사용하여 특정 요청에 대해 WebDataBinder를 초기화?

커스텀한 컨버터를 바인딩할 때 자동으로 쓸 수 있도록하려면 2가지 방법 존재!

  • @InitBinder 사용

    • 필요할 때마다 매번 생성해서 사용

  • ConfigurableWebBinderInitializer 사용

    • 일괄 등록!

29.1.10 Template Engines

REST 웹 서비스뿐만 아니라 Spring MVC를 사용하여 동적 HTML 컨텐츠를 제공 할 수도있다. Spring MVC는 Thymeleaf, FreeMarker 및 JSP를 비롯한 다양한 템플릿 기술을 지원합니다. 또한 많은 다른 템플릿 엔진에는 자체적인 Spring MVC 통합이 포함됩니다.

Spring Boot는 다음 템플릿 엔진에 대한 자동 구성 지원을 포함합니다.

가능한 경우 JSP를 피해야합니다. 임베디드 서블릿 컨테이너에서 사용할 때 몇 가지 known limitations이 있습니다.

이 템플릿 엔진 중 하나를 기본 구성으로 사용하면 템플릿이 src/main/resources/templates에서 자동으로 선택됩니다.

응용 프로그램을 실행하는 방법에 따라 IntelliJ IDEA는 클래스 경로를 다르게 정렬합니다. 주 방법에서 IDE에서 응용 프로그램을 실행하면 Maven 또는 Gradle을 사용하거나 응용 프로그램을 실행할 때와 패키지 순서가 다릅니다. 이로 인해 스프링 부트가 클래스 경로에서 템플릿을 찾지 못하게 될 수 있습니다. 이 문제가 발생하면 IDE의 클래스 경로를 재정렬하여 모듈의 클래스와 리소스를 먼저 배치 할 수 있습니다. 또는 다음과 같이 classpath의 모든 templates디렉토리를 검색하도록 템플리트 접두사를 구성 할 수 있습니다. classpath*:/templates/.

29.1.11 Error Handling

기본적으로 스프링 부트는 모든 오류를 적절한 방식으로 처리하는/error 매핑을 제공하며 서블릿 컨테이너에 "전역" 오류 페이지로 등록됩니다. 시스템 클라이언트의 경우, 오류, HTTP 상태 및 예외 메시지와 함께 JSON 응답을 생성합니다. 브라우저 클라이언트의 경우 동일한 데이터를 HTML 형식으로 렌더링하는 "whitelabel" error view가 있습니다 (error를 해결하는 View추가). 기본 동작을 완전히 바꾸려면 ErrorController를 구현하고 해당 유형의 Bean 정의를 등록하거나 ErrorAttributes유형의 Bean을 추가하여 기존 메커니즘을 사용하지만 내용을 바꿉니다.

BasicErrorController는 사용자 정의 ErrorController의 기본 클래스로 사용할 수 있습니다. 이 기능은 새 내용 유형에 대한 처리기를 추가하려는 경우에 특히 유용합니다. 기본값은 text/html을 구체적으로 처리하고 다른 모든 항목에 대체 기능을 제공하는 것입니다. 이렇게 하려면 BasicErrorController를 확장하고 produces속성이있는 @RequestMapping이있는 공용 메서드를 추가 한 다음 새 유형의 Bean을 만듭니다.

다음 예제와 같이 @ControllerAdvice로 주석 된 클래스를 정의하여 특정 컨트롤러 및 / 또는 예외 유형에 대해 반환 할 JSON 문서를 사용자 정의 할 수도 있습니다.

@ControllerAdvice(basePackageClasses = AcmeController.class)
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {

	@ExceptionHandler(YourException.class)
	@ResponseBody
	ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
		HttpStatus status = getStatus(request);
		return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
	}

	private HttpStatus getStatus(HttpServletRequest request) {
		Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
		if (statusCode == null) {
			return HttpStatus.INTERNAL_SERVER_ERROR;
		}
		return HttpStatus.valueOf(statusCode);
	}

}

앞의 예에서 YourExceptionAcmeController와 동일한 패키지에 정의 된 컨트롤러에 의해 throw 된 경우 CustomErrorTypePOJO의 JSON 표현이 ErrorAttributes표현 대신 사용됩니다.

사용자 정의 Error Pages

주어진 상태 코드에 대한 사용자 정의 HTML 오류 페이지를 표시하려면 /error 폴더에 파일을 추가하십시오. 오류 페이지는 정적 HTML (정적 리소스 폴더 아래에 추가됨)이거나 템플릿을 사용하여 빌드 될 수 있습니다. 파일의 이름은 정확한 상태 코드 또는 시리즈 마스크 여야합니다.

예를 들어 404를 정적 HTML 파일에 매핑하려면 폴더 구조가 다음과 같습니다.

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

FreeMarker 템플릿을 사용하여 5xx 오류를 모두 매핑하려면 폴더 구조가 다음과 같습니다.

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftl
             +- <other templates>

보다 복잡한 매핑의 경우 다음 예제와 같이 ErrorViewResolver인터페이스를 구현하는 bean을 추가 할 수도 있습니다.

public class MyErrorViewResolver implements ErrorViewResolver {

	@Override
	public ModelAndView resolveErrorView(HttpServletRequest request,
			HttpStatus status, Map<String, Object> model) {
		// Use the request or status to optionally return a ModelAndView
		return ...
	}

}

또한 @ExceptionHandler methods@ControllerAdvice와 같은 일반적인 Spring MVC 기능을 사용할 수 있습니다. 그런 다음 ErrorController는 처리되지 않은 예외를 선택합니다.

Spring MVC 외부의 에러 페이지 매핑

Spring MVC를 사용하지 않는 응용 프로그램의 경우 ErrorPageRegistrar인터페이스를 사용하여 ErrorPages를 직접 등록 할 수 있습니다. 이 추상화는 기본 내장된 서블릿 컨테이너와 직접 작동하며 Spring MVC DispatcherServlet이 없어도 작동합니다.

@Bean
public ErrorPageRegistrar errorPageRegistrar(){
	return new MyErrorPageRegistrar();
}

// ...

private static class MyErrorPageRegistrar implements ErrorPageRegistrar {

	@Override
	public void registerErrorPages(ErrorPageRegistry registry) {
		registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
	}

}

Jersey와 Wicket과 같은 일부 비 Spring 웹 프레임 워크에서 일반적인 것처럼 Filter에 의해 처리되는 경로로 ErrorPage를 등록하면 Filter는 다음과 같이 ERROR디스패처로 명시적으로 등록되어야합니다.

@Bean
public FilterRegistrationBean myFilter() {
	FilterRegistrationBean registration = new FilterRegistrationBean();
	registration.setFilter(new MyFilter());
	...
	registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
	return registration;
}

기본 FilterRegistrationBean에는 ERROR디스패처 유형이 포함되어 있지 않습니다.

주의 : 서블릿 컨테이너에 배포할 때 Spring Boot는 오류 페이지 필터를 사용하여 오류 상태의 요청을 해당 오류 페이지로 전달합니다. 응답이 아직 커밋되지 않은 경우에만 요청을 올바른 오류 페이지로 전달할 수 있습니다. 기본적으로 WebSphere Application Server 8.0 이상은 서블릿의 서비스 메소드 완료시 응답을 커밋합니다. com.ibm.ws.webcontainer.invokeFlushAfterServicefalse로 설정하여이 작동을 사용 불가능하게해야합니다.

29.1.12 Spring HATEOAS

하이퍼 미디어를 사용하는 RESTful API를 개발하면 Spring Boot는 대부분의 애플리케이션에서 잘 작동하는 Spring HATEOAS의 자동 구성을 제공한다. 자동 구성은 @EnableHypermediaSupport를 사용할 필요성을 대체하고 aLinkDiscoverers(클라이언트 측 지원) 및 응답을 원하는 표현으로 올바르게 마샬링하도록 구성된 ObjectMapper를 포함하여 하이퍼 미디어 기반 응용 프로그램을 쉽게 만들 수 있도록 여러 Bean을 등록합니다. ObjectMapper는 다양한 spring.jackson.*속성을 설정하거나, 존재하는 경우 Jackson2ObjectMapperBuilderbean을 설정하여 사용자 정의됩니다.

@EnableHypermediaSupport를 사용하여 Spring HATOAS의 설정을 제어 할 수있다. 이렇게 하면 앞에서 설명한 ObjectMapper사용자 정의가 사용 불가능하게됩니다.

Spring HATEOAS는 @EnableHypermediaSupport 어노테이션을 사용하여 특정 하이퍼 미디어 표현 형식을 활성화 할 수 있습니다. 이 어노테이션은 HypermediaType enum을 인수로 취합니다. 현재는 HAL 만 지원됩니다.

  • HATEOAS (Hypermedia As The Engine Of Application State)

    • 원칙은 클라이언트가 응용 프로그램 서버에 의해 동적으로 제공되는 하이퍼 미디어를 통해 네트워크 응용 프로그램과 완전히 상호 작용한다는 것.

  • HAL

    • 하이퍼텍스트 애플리케이션 언어 JSON 또는 XML 코드로 외부 리소스 연결 하이퍼미디어를 정의하는 표준안

29.1.13 CORS 지원

Cross-origin resource sharing (CORS)는 most browsers에서 구현되는 W3C specification으로, IFRAME 또는 JSONP와 같이 덜 안전하고 덜 강력한 방법을 사용하는 대신 어떤 유형의 상호 도메인 요청이 유연하게 지정되도록 지정할 수 있습니다.

버전 4.2부터는 Spring MVC가 CORS를 지원합니다. 컨트롤러 메소드 사용하기 Spring Boot 애플리케이션에서 @CrossOrigin 어노테이션이 있는 controller method CORS configuration에는 특정 구성이 필요하지 않습니다. Global CORS configuration은 다음 예제와 같이 WebMvcConfigurerbean을 사용자 정의된 addCorsMappings(CorsRegistry) 메소드로 등록하여 정의 할 수 있습니다.

CORS(Cross-origin resource sharing)

  • 웹 페이지의 제한된 자원을 외부 도메인에서 접근을 허용해주는 메커니즘

스프링 RESTful Service에서 CORS를 설정은 @CrossOrigin 어노테이션을 사용하여 간단히 해결 할 수 있다. RestController를 사용한 클래스 자체에 적용할 수 도 있고, 특정 REST API method에도 설정 가능하다. 또한, 특정 도메인만 접속을 허용할 수도 있다.

1) @CrossOrigin 어노테이션을 통해 적용하는 방식

  • @CrossOrigin(origins = “허용주소:포트”)

2) WebMvcConfigurer를 통해 적용하는 방식

@Configuration
public class MyConfiguration {

	@Bean
	public WebMvcConfigurer corsConfigurer() {
		return new WebMvcConfigurer() {
			@Override
			public void addCorsMappings(CorsRegistry registry) {
				registry.addMapping("/api/**");
			}
		};
	}
}

Last updated