# 29.1 The “Spring Web MVC Framework”

[Spring Web MVC framework](https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web.html#mvc) (간단히 "Spring MVC"라고도 함)는 풍부한 "모델 뷰 컨트롤러"웹 프레임 워크입니다. Spring MVC는 들어오는 HTTP 요청을 처리하기 위해 특별한 `@Controller` 또는 `@RestController` 빈을 생성하게한다. 컨트롤러의 메서드는 `@RequestMapping` 어노테이션을 사용하여 HTTP에 매핑됩니다.&#x20;

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

```java
@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](https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web.html#mvc)에서 확인할 수있다. [spring.io/guides](https://spring.io/guides)에서 Spring MVC를 사용할 수있는 여러 가이드가있다.&#x20;

#### 29.1.1 Spring MVC 자동 구성&#x20;

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

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

* `ContentNegotiatingViewResolver` 및 `BeanNameViewResolver` 빈 포함
* WebJars에 대한 지원을 포함하여 정적 리소스 제공 지원 (covered [later in this document](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-static-content))).
* `Converter`, `GenericConverter`, 및 `Formatter` 빈 자동 등록
* `HttpMessageConverters` 지원 (covered [later in this document](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-message-converters)).
* `MessageCodesResolver 자동 등록` (covered [later in this document](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-message-codes)).
* 정적`index.html` 지원
* 사용자 정의`Favicon` 지원 (covered [later in this document](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-favicon)).
* `ConfigurableWebBindingInitializer` 빈 자동 사용 (covered [later in this document](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-web-binding-initializer)).

**스프링 부트 MVC 기능을 유지하고 추가적인** [**MVC configuration**](https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web.html#mvc) **(인터셉터, 포맷터, 뷰 컨트롤러 및 기타 기능)을 원하는 경우 `@EnableWebMvc`가 아닌 `WebMvcConfigurer`유형의 `@Configuration` 클래스를 추가 할 수 있습니다.** `RequestMappingHandlerMapping`, `RequestMappingHandlerAdapter`또는 `ExceptionHandlerExceptionResolver`의 사용자 정의 인스턴스를 제공하려는 경우 `WebMvcRegistrationsAdapter`인스턴스를 선언하여 이러한 구성 요소를 제공 할 수 있습니다.&#x20;

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

{% hint style="info" %}
`@EnableWebMvc`

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

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

{% hint style="info" %}
자동 구성된 스프링 MVC 구성을 큰 변경 없이 추가적으로 조작하는 방법?

* **`@Configuration`** 선언!
  * **`WebMvcConfigurer`**
    * **@EnableWebMvc 어노테이션에서 제공하는 빈을 커스터마이징할 수 있는 기능을 제공하는 인터페이스** (자동 구성된 스프링 MVC 구성에 Formatter, MessageConverter 등을 추가 등록할 수 있다)
  * `WebMvnRegistrations`
    * RequestMappingHandlerMapping, RequestMappingHandlerAdapter, ExceptionHandlerExceptionResolver를 재정의할 수 있다.
      {% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHdvKAf6G9BmRJGVEk%2F-LgHlSyubqta5jCbJx_7%2Fimage.png?alt=media\&token=ee6bdea1-928a-48d7-9367-454665bc980e)

{% hint style="info" %}
자동 구성된 스프링 MVC 구성을 개발자가 완벽히 제어하는 방법?&#x20;

* 첫 번째 방법:**`@Configuration` 과 `@EnableWebMvc`를 함께 선언!**
  * `@EnableWebMvc`를 선언하면 WebMvcConfigurationSupport에서 구성한 스프링 MVC 구성을 불러온다.
    {% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHdvKAf6G9BmRJGVEk%2F-LgHoAvOQnsBDkYgGUo5%2Fimage.png?alt=media\&token=d63eccd8-e6f4-44e4-8427-1eb115cd4115)

{% hint style="info" %}

* 두 번째 방법: **`@Configuration` 과 `@EnableWebMvc`를 함께 선언한 클래스가 WebMvcConfigurer 인터페이스 구현**
  * WebMvcConfigurationSupport에서 자동구성한 스프링 MVC 구성에 Formatter, MessageConverter 등을 추가적으로 등록할 수 있다.
    {% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHdvKAf6G9BmRJGVEk%2F-LgHoJCOnxidYkojfh_k%2Fimage.png?alt=media\&token=f6a369c0-85f0-4bc9-b4f2-a6127d284a74)

{% hint style="info" %}

* 세 번째 방법: **`@EnableWebMvc` 없이 스프링 MVC 구성을 변경하는 방법**
  {% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHdvKAf6G9BmRJGVEk%2F-LgHoPNJaVfiUfYfmo2P%2Fimage.png?alt=media\&token=64ae530a-5356-4e73-8bbc-583ec777ecb0)

#### 29.1.2 HttpMessageConverters

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

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

```java
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);
	}

}
```

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

#### 29.1.3 사용자 정의 JSON Serializers and Deserializers

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

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

```java
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`로 메타 주석이 붙어 있기 때문에 일반적인 구성 요소 검색 규칙이 적용됩니다.

{% hint style="info" %}
Ex. "favoriteColor" JSON을 객체화 ! (Serialize)&#x20;
{% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgL7HHmDY2AXz5tr5o0%2F-LgLBNN2GLo8XJ5IwQYV%2Fimage.png?alt=media\&token=fdbf6acb-d928-4963-8f91-9c57ec6c0a66)

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgL7HHmDY2AXz5tr5o0%2F-LgLBYgVDtdjA605trWZ%2Fimage.png?alt=media\&token=0b8dc7d1-4e50-4c27-bcc5-36f50f156888)

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgL7HHmDY2AXz5tr5o0%2F-LgLBdKsPe2qtc4aMihR%2Fimage.png?alt=media\&token=46644b5e-ec6f-49c7-9802-b432a16cd888)

Spring Boot는 또한 [`JsonObjectSerializer`](https://github.com/spring-projects/spring-boot/tree/v2.1.5.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonObjectSerializer.java)및 [`JsonObjectDeserializer`](https://github.com/spring-projects/spring-boot/tree/v2.1.5.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonObjectDeserializer.java)기본 클래스를 제공하여 객체를 직렬화 할 때 표준 Jackson 버전에 대한 유용한 대안을 제공합니다. 자세한 내용은 Javadoc의 [`JsonObjectSerializer`](https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/api/org/springframework/boot/jackson/JsonObjectSerializer.html)및 [`JsonObjectDeserializer`](https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/api/org/springframework/boot/jackson/JsonObjectDeserializer.html)를 참조하십시오.&#x20;

#### 29.1.4 MessageCodesResolver

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

{% hint style="info" %}
MessageCodesResolver

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

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

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

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

* DefaultMessageCodeResolver를 기본으로 사용
  {% endhint %}

#### 29.1.5 정적 콘텐츠

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

{% hint style="info" %}
`ResourceHttpRequestHandler`

* 정적 리소스에 대한 처리 수행. 경로 잡거나 등등
  {% endhint %}

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

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

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

{% hint style="info" %}
기본 리소스 위치

* 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로 접근
    {% endhint %}

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

{% hint style="info" %}
spring.mvc.static-locations: 리소스 찾을 위치 변경 가능

* 기존의 기본 리소스 위치를 사용하지 않고 변경한 위치만 사용하므로 권장하지 않는 방법이다.
* 이 방법 보다는 WebMvcConfigurer를 구현상속받아서 addResourceHandlers로 커스터마이징 하는 방법이 더 좋다. 기본 리소스 위치를 사용하면서 추가로 필요한 리소스위치만 정의해서 사용할 수 있다.
  * localhost:8080/m/hello.html 접근시 resources/m/hello.html 리턴
    {% endhint %}

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

| ![\[Tip\]](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/images/tip.png)                                        |
| -------------------------------------------------------------------------------------------------------------------------------------- |
| **응용 프로그램이 jar로 패키지화되어있는 경우 `src/main/webapp` 디렉토리를 사용하지 마십시오. 이 디렉토리는 일반적인 표준이지만, war 패키징에서만 작동하며 병?을 생성하면 대부분의 빌드 도구에서 자동으로 무시됩니다.** |

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

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

{% hint style="info" %}
Webjars

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

\[참고사이트] <https://java.ihoney.pe.kr/428>&#x20;
{% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHylqVAhaDIj6HUSkz%2F-LgI-ZYCtkJYExX26uUK%2Fimage.png?alt=media\&token=75c46f0e-e18b-4757-8023-113f769742b2)

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHylqVAhaDIj6HUSkz%2F-LgI-g0MkXAUmq9khG8h%2Fimage.png?alt=media\&token=c38e09c3-a35b-48be-9152-7759583aadc9)

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHylqVAhaDIj6HUSkz%2F-LgI-ng8tmUFzuXDLQ8B%2Fimage.png?alt=media\&token=9af714f7-39bf-4071-baaf-da2e625a41c8)

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

| ![\[Note\]](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/images/note.png)                    |
| -------------------------------------------------------------------------------------------------------------------- |
| JBoss를 사용하는 경우 `webjars-locator-core` 대신 `webjars-locator-jboss-vfs` 종속성을 선언해야합니다. 그렇지 않으면 모든 Webjars가 `404`로 해결됩니다. |

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

{% hint style="info" %}
스프링 리소스 체인 활성화&#x20;

```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한 값이므로 **파일의 내용이 변경되지 않는다면 파일 이름에도 변경이 생기지 않아 캐시가 효율적으로 동작한다.**
{% endhint %}

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

| ![\[Note\]](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/images/note.png)                                                                                                                                                                                                                                                   |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 리소스 링크는 Thymeleaf 및 FreeMarker에 대해 자동 구성되는 `ResourceUrlEncodingFilter`덕분에 런타임에 템플릿에 다시 작성됩니다. JSP를 사용할 때 이 필터를 수동으로 선언해야합니다. 다른 템플릿 엔진은 현재 자동으로 지원되지 않지만 사용자 지정 템플릿 매크로 / 도우미와 [`ResourceUrlProvider`](https://docs.spring.io/spring/docs/5.1.7.RELEASE/javadoc-api/org/springframework/web/servlet/resource/ResourceUrlProvider.html)를 사용할 수 있습니다. |

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

```
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"/>`).&#x20;

지원되는 옵션에 대해서는 [`ResourceProperties`](https://github.com/spring-projects/spring-boot/tree/v2.1.5.RELEASE/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ResourceProperties.java)를 참조하십시오.&#x20;

| ![\[Tip\]](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/images/tip.png)                                                                                                                                                                                           |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 이 기능은 전용 [blog post](https://spring.io/blog/2014/07/24/spring-framework-4-1-handling-static-web-resources)와 Spring Framework의 [reference documentation](https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web.html#mvc-config-static-resources)에 자세히 설명되어 있습니다. |
|                                                                                                                                                                                                                                                                                           |

#### 29.1.6 Welcome Page

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

#### 29.1.7 사용자 정의 Favicon

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

{% hint style="info" %}
파비콘&#x20;

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

#### 29.1.8 경로 일치 및 콘텐츠 협상&#x20;

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

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

적절한 "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
```

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

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

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

```
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`를 초기화한다. 자신만의 `ConfigurableWebBindingInitializer`Bean을 생성하면 스프링 부트는 자동으로 스프링 MVC를 사용하도록 설정합니다.**

{% hint style="info" %}
`WebBindingInitializer`를 사용하여 특정 요청에 대해 `WebDataBinder`를 초기화?
{% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHylqVAhaDIj6HUSkz%2F-LgI4uHkiINexqkGLI6m%2Fimage.png?alt=media\&token=9e3144b2-bbc8-455d-b1a1-71ce024c9d48)

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

* @InitBinder 사용
  * 필요할 때마다 매번 생성해서 사용
    {% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHylqVAhaDIj6HUSkz%2F-LgI5Re_SLOo5IPfDgiG%2Fimage.png?alt=media\&token=2bf4bb96-d999-4c14-a35b-c32929c8231c)

{% hint style="info" %}

* ConfigurableWebBinderInitializer 사용
  * 일괄 등록!
    {% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHylqVAhaDIj6HUSkz%2F-LgI5iY-qwlmg46bW35O%2Fimage.png?alt=media\&token=0ac32532-3160-4fed-8744-78d3a73a6ef3)

#### 29.1.10 Template Engines

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

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

* [FreeMarker](https://freemarker.apache.org/docs/)
* [Groovy](http://docs.groovy-lang.org/docs/next/html/documentation/template-engines.html#_the_markuptemplateengine)
* [Thymeleaf](https://www.thymeleaf.org/)
* [Mustache](https://mustache.github.io/)

| ![\[Tip\]](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/images/tip.png)                                                                                |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 가능한 경우 JSP를 피해야합니다. 임베디드 서블릿 컨테이너에서 사용할 때 몇 가지 [known limitations](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-jsp-limitations)이 있습니다. |

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

| ![\[Tip\]](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/images/tip.png)                                                                                                                                                                                                                                                 |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 응용 프로그램을 실행하는 방법에 따라 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을 추가하여 기존 메커니즘을 사용하지만 내용을 바꿉니다.**&#x20;

| ![\[Tip\]](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/images/tip.png)                                                                                                                                                                                       |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BasicErrorController`는 사용자 정의 `ErrorController`의 기본 클래스로 사용할 수 있습니다. 이 기능은 새 내용 유형에 대한 처리기를 추가하려는 경우에 특히 유용합니다. 기본값은 `text/html`을 구체적으로 처리하고 다른 모든 항목에 대체 기능을 제공하는 것입니다. 이렇게 하려면 `BasicErrorController`를 확장하고 `produces`속성이있는 `@RequestMapping`이있는 공용 메서드를 추가 한 다음 새 유형의 Bean을 만듭니다. |

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

```java
@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);
	}

}
```

앞의 예에서 **`YourException`이 `AcmeController`와 동일한 패키지에 정의 된 컨트롤러에 의해 throw 된 경우 `CustomErrorType`POJO의 JSON 표현이 `ErrorAttributes`표현 대신 사용됩니다.**&#x20;

**사용자 정의 Error Pages**

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

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

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

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

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

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

```java
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**](https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web.html#mvc-exceptionhandlers)**와** [**`@ControllerAdvice`**](https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web.html#mvc-ann-controller-advice)**와 같은 일반적인 Spring MVC 기능을 사용할 수 있습니다.** 그런 다음 `ErrorController`는 처리되지 않은 예외를 선택합니다.&#x20;

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

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

```java
@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"));
	}

}
```

| ![\[Note\]](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/images/note.png)                                        |
| ---------------------------------------------------------------------------------------------------------------------------------------- |
| Jersey와 Wicket과 같은 일부 비 Spring 웹 프레임 워크에서 일반적인 것처럼 `Filter`에 의해 처리되는 경로로 `ErrorPage`를 등록하면 `Filter`는 다음과 같이 `ERROR`디스패처로 명시적으로 등록되어야합니다. |

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

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

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

#### 29.1.12 Spring HATEOAS

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

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

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

* HATEOAS (Hypermedia As The Engine Of Application State)&#x20;

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

* HAL&#x20;
  * 하이퍼텍스트 애플리케이션 언어 JSON 또는 XML 코드로 외부 리소스 연결 하이퍼미디어를 정의하는 표준안
    {% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHylqVAhaDIj6HUSkz%2F-LgI8uu7_WVH848favGy%2Fimage.png?alt=media\&token=509b7ed3-6ab9-4802-934e-c6090e98d0c8)

#### 29.1.13 CORS 지원

[Cross-origin resource sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) (CORS)는 [most browsers](https://caniuse.com/#feat=cors)에서 구현되는 [W3C specification](https://www.w3.org/TR/cors/)으로, IFRAME 또는 JSONP와 같이 덜 안전하고 덜 강력한 방법을 사용하는 대신 어떤 유형의 상호 도메인 요청이 유연하게 지정되도록 지정할 수 있습니다.&#x20;

**버전 4.2부터는 Spring MVC가 CORS를 지원합니다.** 컨트롤러 메소드 사용하기 Spring Boot 애플리케이션에서 [**`@CrossOrigin`**](https://docs.spring.io/spring/docs/5.1.7.RELEASE/javadoc-api/org/springframework/web/bind/annotation/CrossOrigin.html) **어노테이션이 있는** [**controller method CORS configuration**](https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web.html#controller-method-cors-configuration)**에는 특정 구성이 필요하지 않습니다.** [**Global CORS configuration**](https://docs.spring.io/spring/docs/5.1.7.RELEASE/spring-framework-reference/web.html#global-cors-configuration)**은 다음 예제와 같이 `WebMvcConfigurer`bean을 사용자 정의된 `addCorsMappings(CorsRegistry)` 메소드로 등록하여 정의 할 수 있습니다.**&#x20;

{% hint style="info" %}
CORS(Cross-origin resource sharing)

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

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

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

* @CrossOrigin(origins = “허용주소:포트”)
  {% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHylqVAhaDIj6HUSkz%2F-LgIARVvLtzYXbgkAAAd%2Fimage.png?alt=media\&token=2c01cd96-741e-4fd7-84f2-2e7bd9ffede5)

{% hint style="info" %}
2\) WebMvcConfigurer를 통해 적용하는 방식
{% endhint %}

![](https://3140643518-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LWdhssr0w9IqAHmA-ke%2F-LgHylqVAhaDIj6HUSkz%2F-LgIAzauDi-2Wcz85ykv%2Fimage.png?alt=media\&token=81dc78af-0ac3-46e1-97e5-307db6a7c207)

```java
@Configuration
public class MyConfiguration {

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