스프링 부트 공식 레퍼런스를 한글로 번역한 문서입니다.
전체 목차는 여기에 있습니다.
목차
- 12.4.1. Write a JSON REST Service
- 12.4.2. Write an XML REST Service
- 12.4.3. Customize the Jackson ObjectMapper
- 12.4.4. Customize the @ResponseBody Rendering
- 12.4.5. Handling Multipart File Uploads
- 12.4.6. Switch Off the Spring MVC DispatcherServlet
- 12.4.7. Switch off the Default MVC Configuration
- 12.4.8. Customize ViewResolvers
12.4. Spring MVC
스프링 부트에는 스프링 MVC를 포함하고 있는 스타터가 많이 있다. 몇 가지 스타터들은 스프링 MVC를 직접 가지고 있기 보단 의존성을 포함하고 있다. 이번 섹션에선 스프링 MVC와 스프링 부트에 관련해서 흔히 하는 질문들에 답해보겠다.
12.4.1. Write a JSON REST Service
스프링 부트 애플리케이션에 있는 모든 스프링 @RestController
는 클래스패스에 Jackson2가 있다면 기본적으로 JSON 응답을 렌더링할 거다:
@RestController
public class MyController {
@RequestMapping("/thing")
public MyThing thing() {
return new MyThing();
}
}
MyThing
을 Jackson2로 직렬화할 수만 있다면 (일반적인 POJO나 Groovy 객체라면 ok), localhost:8080/thing
은 기본적으로 이 객체를 JSON으로 표현해서 응답한다. 브라우저에선 XML을 먼저 받는 accept 헤더를 보내는 경우가 있어서 간혹 XML 응답을 볼 수도 있다.
12.4.2. Write an XML REST Service
클래스패스에 Jackson XML 익스텐션이 있다면 (jackson-dataformat-xml
) 이 익스텐션을 사용해 XML 응답을 렌더링할 수 있다. 앞에서 JSON 예시를 보여줬던 코드에서도 동작한다. Jackson XML 렌더러를 사용하려면 프로젝트에 아래 의존성을 추가해라:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
클래스패스에 Jackson의 XML 익스텐션은 없고 JAXB가 있다면, 다음 예제와 같이 MyThing
에 @XmlRootElement
를 추가해주면 XML로 렌더링할 수 있다:
@XmlRootElement
public class MyThing {
private String name;
// getters/setters ...
}
JAXB는 자바 8에서만 바로 사용 가능하다. 더 최신 버전을 사용하고 있다면 프로젝트에 아래 의존성을 추가해라:
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
서버에서 JSON 대신 XML을 렌더링하도록 만들려면
Accept: text/xml
헤더를 전송해야 할 수도 있다 (아니면 브라우저를 사용하거나).
12.4.3. Customize the Jackson ObjectMapper
스프링 MVC는 (클라이언트와 서버 사이드 모두) HttpMessageConverters
를 사용해서 HTTP exchange의 컨텐츠 변환을 협상한다. 클래스패스에 Jackson이 있을 때는 자동 설정된 Jackson2ObjectMapperBuilder
인스턴스가 미리 디폴트 컨버터들을 제공해줄 거다.
ObjectMapper
(Jackson XML 컨버터에선 XmlMapper
) 인스턴스(기본으로 만들어진다)에는 다음과 같은 프로퍼티가 커스텀되어 있다:
MapperFeature.DEFAULT_VIEW_INCLUSION
비활성화DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
비활성화SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
비활성화
스프링 부트에선 이 동작을 좀 더 쉽게 커스텀할 수 있다.
ObjectMapper
와 XmlMapper
인스턴스는 environment를 사용해 설정할 수 있다. Jackson은 처리 과정을 다양한 측면으로 커스텀할 수 있게끔 폭넓은 on/off 기능들을 제공한다. 이 기능들은 6개의 enum(Jackson에 들어있다)으로 표현하며, 이 enum 값은 environment의 프로퍼티에 매핑된다:
Enum | Property | Values |
---|---|---|
com.fasterxml.jackson.databind. DeserializationFeature |
spring.jackson.deserialization.<feature_name> | true , false |
com.fasterxml.jackson.core. JsonGenerator.Feature |
spring.jackson.generator.<feature_name> | true , false |
com.fasterxml.jackson.databind. MapperFeature |
spring.jackson.mapper.<feature_name> | true , false |
com.fasterxml.jackson.core. JsonParser.Feature |
spring.jackson.parser.<feature_name> | true , false |
com.fasterxml.jackson.databind. SerializationFeature |
spring.jackson.serialization.<feature_name> | true , false |
com.fasterxml.jackson.annotation. JsonInclude.Include |
spring.jackson.default-property-inclusion | always ,non_null ,non_absent ,non_default ,non_empty |
예를 들어 pretty print를 활성화하려면 spring.jackson.serialization.indent_output=true
를 설정해라. 완화된 바인딩 규칙을 적용하기 때문에 indent_output
처럼, enum 상수값 INDENT_OUTPUT
과 대소문자가 완벽하게 일치하지 않아도 된다.
이 environment 기반 설정은 자동 설정된 Jackson2ObjectMapperBuilder
빈에 적용되며, 자동 설정된 ObjectMapper
빈 등, 이 빌더를 사용해 생성하는 모든 매퍼에 적용된다.
컨텍스트의 Jackson2ObjectMapperBuilder
는 Jackson2ObjectMapperBuilderCustomizer
빈을 하나 이상 사용해서 커스텀할 수 있다. 이 customizer 빈은 순서를 지정할 수 있으며 (부트의 자체 customizer는 order가 0으로 설정돼 있다), 순서를 잘 활용하면 부트의 커스텀 로직 전후에 별도 커스텀 로직을 실행할 수 있다.
com.fasterxml.jackson.databind.Module
타입 빈은 자동 설정된 Jackson2ObjectMapperBuilder
에 자동으로 등록돼서, 이 빌더로 생성하는 모든 ObjectMapper
인스턴스에 적용된다. 이 메커니즘을 통해 애플리케이션에 새 기능을 추가할 때 전역으로 사용할 커스텀 모듈을 제공할 수 있다.
디폴트 ObjectMapper
를 완전히 교체하고 싶을 때는 ObjectMapper
타입 @Bean
을 정의하고 @Primary
로 마킹하거나, 빌더를 통해 접근하는 게 좋다면 Jackson2ObjectMapperBuilder
@Bean
을 정의해라. 두 경우 모두 ObjectMapper
의 자동 설정은 전부 비활성화된다.
MappingJackson2HttpMessageConverter
타입 @Bean
들을 제공하면 MVC 설정에 있는 기본값이 대체된다. 간편한 HttpMessageConverters
타입 빈도 제공하고 있다 (디폴트 MVC 설정에서는 항상 사용할 수 있다). 여기에는 디폴트 메세지 컨버터와 사용자가 개선한 메세지 컨버터에 접근할 수 있는 유용한 메소드들이 들어있다.
자세한 내용은 “@ResponseBody 렌더링 커스텀하기” 섹션과 WebMvcAutoConfiguration
소스 코드를 참고해라.
12.4.4. Customize the @ResponseBody Rendering
스프링은 HttpMessageConverters
를 사용해서 @ResponseBody
(또는 @RestController
의 응답)를 렌더링한다. 스프링 부트 컨텍스트에 빈을 적절한 타입으로 넣어주면 컨버터를 더 추가할 수 있다. 추가하는 빈이 이미 디폴트로 포함시킨 타입일 때는 (JSON 변환에선 MappingJackson2HttpMessageConverter
등) 기본값이 대체된다. 간편한 HttpMessageConverters
타입 빈을 제공하고 있으며, 디폴트 MVC 설정에선 이 타입 빈을 항상 이용할 수 있다. 여기에는 디폴트 메세지 컨버터와 사용자가 개선한 메세지 컨버터에 접근할 수 있는 유용한 메소드들이 들어있다 (예를 들어 커스텀 RestTemplate
에 수동으로 주입하고 싶을 때 유용할 수 있다).
평소 MVC를 사용할 때 처럼 WebMvcConfigurer
빈들을 제공하면, 이 빈으로도 configureMessageConverters
메소드를 재정의해서 컨버터에 기여할 수 있다. 하지만 평소 MVC 때와는 달리 필요한 컨버터를 추가로만 제공할 수 있다 (스프링 부트에서도 같은 메커니즘으로 기본값을 제공하기 때문). 마지막으로 자체 @EnableWebMvc
설정을 제공해서 스프링 부트의 디폴트 MVC 설정에서 빠져나오면, WebMvcConfigurationSupport
에 있는 getMessageConverters
를 활용해 모든 작업을 수동으로 제어할 수 있다.
자세한 내용은 WebMvcAutoConfiguration
소스 코드를 확인해봐라.
12.4.5. Handling Multipart File Uploads
스프링 부트는 서블릿 3 javax.servlet.http.Part
API를 포용해서 파일 업로드를 지원한다. 기본적으로 스프링 부트는 스프링 MVC에서 단일 요청으로 사용할 수 있는 파일 사이즈는 파일 당 최대 1MB, 전체 파일 데이터는 최대 10MB로 설정한다. 중간 데이터를 저장할 위치와 (ex. /tmp
디렉토리), 데이터를 디스크로 플러시할 임계치 등, 이런 설정들은 MultipartProperties
클래스에 정의돼 있는 프로퍼티를 통해 커스텀할 수 있다. 예를 들어 파일 사이즈에 제한을 두지 않으려면 spring.servlet.multipart.max-file-size
프로퍼티를 -1
로 설정해라.
스프링의 멀티파트 지원을 이용하면 멀티파트로 인코딩한 파일을 간편하게 받을 수 있다. 스프링 MVC 컨트롤러 핸들러 메소드에 MultipartFile
타입 파라미터를 선언하고 @RequestParam
어노테이션을 달아주면 된다.
자세한 내용은 MultipartAutoConfiguration
소스 코드를 참고해라.
Apache Commons File Upload같은 별도 의존성을 도입하는 것보단, 컨테이너가 내장하고 있는 멀티파트 업로드 지원을 활용하는 걸 권장한다.
12.4.6. Switch Off the Spring MVC DispatcherServlet
기본적으로 모든 컨텐츠는 애플리케이션의 루트(/
)에서 서빙한다. 다른 경로에 매핑하고 싶다면 아래와 같이 설정해주면 된다:
spring.mvc.servlet.path=/mypath
spring:
mvc:
servlet:
path: "/mypath"
다른 서블릿도 사용한다면, 각각을 Servlet
이나 ServletRegistrationBean
타입 @Bean
으로 선언하면 되며, 그러면 스프링 부트가 알아서 컨테이너에 등록해줄 거다. 이렇게 서블릿이 등록되면 DispatcherServlet
을 호출하지 않고 DispatcherServlet
의 하위 컨텍스트에 매핑할 수 있다.
DispatcherServlet
을 직접 설정하는 건 흔치 않지만, 그래도 정말 필요하다면 DispatcherServletPath
타입 @Bean
도 함께 정의해서 커스텀 DispatcherServlet
의 경로를 제공해야 한다.
12.4.7. Switch off the Default MVC Configuration
MVC 설정을 전부 직접 제어하려면 @EnableWebMvc
어노테이션으로 자체 @Configuration
을 제공하는 게 가장 쉽다. 이렇게 하면 MVC 설정을 모두 도맡아야 한다.
12.4.8. Customize ViewResolvers
ViewResolver
는 스프링의 핵심 컴포넌트로, @Controller
의 뷰 이름을 실제 View
구현체로 바꿔준다. ViewResolver
는 주로 REST 스타일 서비스가 아닌 UI 애플리케이션에서 사용한다는 점을 참고해라 (@ResponseBody
를 렌더링할 때는 View
를 사용하지 않는다). 선택할 수 있는 ViewResolver
구현체는 다양하며, 스프링 자체에선 어떤 것을 꼭 사용해야 한다같은 의견은 제시하지 않는다. 반면 스프링 부트는 클래스패스와 애플리케이션 컨텍스트에서 찾은 것들에 따라 한 두개를 미리 마련해줄 거다. DispatcherServlet
은 애플리케이션 컨텍스트에서 찾은 리졸버를 모두 사용해서 결과가 나올 때까지 순서대로 리졸브해본다. 자체 구현체를 추가할 때는 리졸버가 어디에 어떤 순서로 추가되는지 알고 있어야 한다.
WebMvcAutoConfiguration
에선 컨텍스트에 아래와 같은 ViewResolver
들을 추가한다:
- ‘defaultViewResolver’라는 이름을 가진
InternalResourceViewResolver
. 이 구현체에선DefaultServlet
으로 렌더링할 수 있는 물리적 리소스를 찾는다 (스태틱 리소스와 JSP 페이지도 있다면 포함). 서블릿 컨텍스트에서 물리적인 리소스를 찾기 전엔 뷰 이름에 프리픽스와 suffix를 적용한다 (기본값은 둘 다 비어 있지만spring.mvc.view.prefix
,spring.mvc.view.suffix
를 통해 외부 설정으로 접근할 수 있다). 같은 타입으로 빈을 정의하면 재정의할 수 있다. - ‘beanNameViewResolver’라는 이름을 가진
BeanNameViewResolver
. 뷰 리졸버 체인에서 유용하게 쓰이는 리졸버로,View
타입 중에서 같은 이름을 가진 빈을 고른다. 이 구현체는 재정의하거나 교체할 필요 없다. - ‘viewResolver’라는 이름을 가진
ContentNegotiatingViewResolver
는View
타입 빈이 실제로 존재하는 경우에만 추가된다. 이 구현체는 compoiste 리졸버로, 다른 구현체들에 위임해서 클라이언트가 전송한 ‘Accept’ HTTP 헤더와 일치하는 항목을 찾는다. 더 자세히 알아보고 싶다면ContentNegotiatingViewResolver
관련 블로그가 유용할 거다. 소스 코드를 직접 살펴봐도 좋다. ‘viewResolver’라는 빈을 정의하면ContentNegotiatingViewResolver
자동 설정을 끌 수 있다. - 타임리프를 사용한다면 ‘thymeleafViewResolver’라는 이름을 가진
ThymeleafViewResolver
도 추가된다. 이 구현체는 뷰 이름에 프리픽스와 suffix를 감싼 뒤 리소스를 찾는다. 이때 사용하는 프리픽스는spring.thymeleaf.prefix
, suffix는spring.thymeleaf.suffix
다. 각각의 기본값은 ‘classpath:/templates/’와 ‘.html’이다. 같은 이름으로 빈을 정의하면ThymeleafViewResolver
를 재정의할 수 있다. - FreeMarker를 사용한다면 ‘freeMarkerViewResolver’라는 이름을 가진
FreeMarkerViewResolver
도 추가된다. 이 구현체는 뷰 이름에 프리픽스와 suffix를 붙인 뒤 로더 경로에서 리소스를 찾는다 (로더 경로는spring.freemarker.templateLoaderPath
를 통해 외부에서 관리하며, 기본값은 ‘classpath:/templates/’다). 프리픽스는spring.freemarker.prefix
로, suffix는spring.freemarker.suffix
로 관리한다. 프리픽스의 기본값은 비어있으며, suffix의 기본값은 ‘.ftlh’다. 같은 이름으로 빈을 정의하면FreeMarkerViewResolver
를 재정의할 수 있다. - Groovy 템플릿을 사용한다면 (실제로 클래스패스에
groovy-templates
가 있는 경우) ‘groovyMarkupViewResolver’라는 이름을 가진GroovyMarkupViewResolver
도 추가된다. 이 구현체는 뷰 이름에 프리픽스와 suffix를 붙인 뒤 (spring.groovy.template.prefix
와spring.groovy.template.suffix
를 통해 외부에서 관리한다) 로더 경로에서 리소스를 찾는다. 프리픽스의 기본값은 ‘classpath:/templates/’, suffix의 기본값은 ‘.tpl’이다. 같은 이름으로 빈을 정의하면GroovyMarkupViewResolver
를 재정의할 수 있다. - Mustache를 사용한다면 ‘mustacheViewResolver’라는 이름을 가진
MustacheViewResolver
도 추가된다. 이 구현체는 뷰 이름에 프리픽스와 suffix를 감싼 뒤 리소스를 찾는다. 이때 사용하는 프리픽스는spring.mustache.prefix
, suffix는spring.mustache.suffix
다. 각각의 기본값은 ‘classpath:/templates/’와 ‘.mustache’다. 같은 이름으로 빈을 정의하면MustacheViewResolver
를 재정의할 수 있다.
더 자세한 내용은 아래 코드를 참고해라:
WebMvcAutoConfiguration
ThymeleafAutoConfiguration
FreeMarkerAutoConfiguration
GroovyTemplateAutoConfiguration
Next :Jersey
Jersey와 관련된 how to 가이드 (스프링 시큐리티 사용하기, 스프링 MVC와 함께 사용하기 등)
전체 목차는 여기에 있습니다.