1.11. WebFlux Config

편집일시: 2021-04-12 14:22 조회수: 216 댓글수: 0
[Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config) WebFlux Java 구성은 어노테이션이 선언된 컨트롤러 또는 함수 엔드포인트로 요청을 처리하는데 필요한 컴포넌트를 선언하고 구성을 정의하는 API를 제공한다. 즉, Java 구성 의해 작성된 기본 Bean을 이해할 필요는 없다. 그래도 더 이해하고 싶다면, `WebFluxConfigurationSupport`을 보거나, [특별한 Bean 유형](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-special-bean-types)의 내용에 대한 자세한 내용 읽어 봐라. 구성 API에는 없는 고급 사용자 지정 내용은 [고급 구성 모드](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-config-advanced-java)를 사용하여 구성을 완전히 제어 할 수 있다. ## 1.11.1. WebFlux 구성 활성화 [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-enable) 다음의 예와 같이, Java config에 `@EnableWebFlux` 어노테이션을 사용할 수 있다. Java ``` @Configuration @EnableWebFlux public class WebConfig { } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig ``` 위의 예에서는 다수의 Spring WebFlux [인프라스트럭처 Bean(infrastructure beans)](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-special-bean-types)을 등록하고 JSON, XML 등과 같이 클래스 경로에 사용할 수 있는 종속성에 적용한다. ## 1.11.2. WebFlux 구성 API [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-customize) Java 구성은 다음 예제와 같이 `WebFluxConfigurer` 인터페이스를 구현할 수 있다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { // Implement configuration methods... } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { // Implement configuration methods... } ``` ## 1.11.3. 변환, 포맷(Conversion, formatting) [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-conversion) 디폴트로 다양한 숫자 및 날짜 형식의 포맷터(formatters)를 제공하고, `@NumberFormat`와 `@DateTimeFormat`을 선언하 사용자가 정의한 포맷터를 지정할 수도 있다. Java config에서 사용자 정의 포맷터와 변환기를 등록하려면 다음과 같이 한다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void addFormatters(FormatterRegistry registry) { // ... } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun addFormatters(registry: FormatterRegistry) { // ... } } ``` 기본적으로 Spring WebFlux는 날짜 값을 파싱 및 포맷팅을 할 때, 요청 로케일을 고려한다. 이 날짜가 "입력" 양식 필드를 가진 문자열로 표현 양식으로 작동한다. 그러나 "날짜"와 "시간" 양식 필드의 경우 브라우저는 HTML 명세에 정의되어 있는 고정 형식을 사용한다. 이런 경우, 날짜 및 시간 형식은 다음과 같이 정의 할 수 있다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void addFormatters(FormatterRegistry registry) { DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); registrar.setUseIsoFormat(true); registrar.registerFormatters(registry); } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun addFormatters(registry: FormatterRegistry) { val registrar = DateTimeFormatterRegistrar() registrar.setUseIsoFormat(true) registrar.registerFormatters(registry) } } ``` > `FormatterRegistrar` 구현을 언제 사용하는지에 대한 자세한 내용은[ `FormatterRegistrar` SPI](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#format-FormatterRegistrar-SPI) 및 `FormattingConversionServiceFactoryBean` 참조해라. ## 1.11.4. 검증(Validation) [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-validation) 기본적으로 [Bean 검증](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validation-beanvalidation-overview)이 클래스 패스에 존재하는 경우(Hibernate Validator 등), `LocalValidatorFactoryBean`는 `@Controller` 메소드 인수의 `@Valid`와 `@Validated`으로 사용하기 위한 글로벌 [Validator](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validator)로 등록된다. Java 구성은 다음 예와 같이 글로벌 `Validator` 인스턴스를 지정할 수 있다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public Validator getValidator(); { // ... } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun getValidator(): Validator { // ... } } ``` 다음의 예와 같이, `Validator` 구현을 로컬로 등록 할 수 있다. Java ``` @Controller public class MyController { @InitBinder protected void initBinder(WebDataBinder binder) { binder.addValidators(new FooValidator()); } } ``` Kotlin ``` @Controller class MyController { @InitBinder protected fun initBinder(binder: WebDataBinder) { binder.addValidators(FooValidator()) } } ``` > `LocalValidatorFactoryBean`를 어딘가에 삽입해야 하는 경우는 Bean을 작성하고 `@Primary`을 선언하여, MVC config에서 선언된 것과 충돌을 방지한다. ## 1.11.5. 콘텐츠 타입 리졸버(Content Type Resolvers) [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-content-negotiation) Spring WebFlux는 요청으로 부터 `@Controller` 인스턴스 요청된 미디어 타입을 결정하는 방법을 설정할 수 있다. 기본적으로 `Accept` 헤더만 체크되지만, 쿼리 매개 변수 기반으로 하도록 활성화 할 수 있다. 다음의 예는 요청된 콘텐츠 타입 리솔루션을 사용자 지정하는 방법을 보여준다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void configureContentTypeResolver(RequestedContentTypeResolverBuilder builder) { // ... } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun configureContentTypeResolver(builder: RequestedContentTypeResolverBuilder) { // ... } } ``` ## 1.11.6. HTTP 메시지 코덱 [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-message-converters) 다음 예제는 요청 및 응답 본문 읽기 및 쓰기 방법을 사용자 지정하는 방법을 보여준다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) { configurer.defaultCodecs().maxInMemorySize(512 * 1024); } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) { // ... } } ``` `ServerCodecConfigurer`는 디폴트 reader와 writer 세트를 제공한다. 이것을 사용하여 reader와 writer를 추가하거나 디폴트을 정의하고 디폴트을 완전히 대체 할 수 있다. Jackson JSON와 XML의 경우 [`Jackson2ObjectMapperBuilder`](https://docs.spring.io/spring-framework/docs/5.3.5/javadoc-api/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.html)의 사용을 고려하자. [`Jackson2ObjectMapperBuilder`](https://docs.spring.io/spring-framework/docs/5.3.5/javadoc-api/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.html)는 Jackson의 기본 속성을 다음과 같이 정의한다. - [`DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES`](https://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/DeserializationFeature.html#FAIL_ON_UNKNOWN_PROPERTIES)는 비활성화한다. - [`MapperFeature.DEFAULT_VIEW_INCLUSION`](https://fasterxml.github.io/jackson-databind/javadoc/2.6/com/fasterxml/jackson/databind/MapperFeature.html#DEFAULT_VIEW_INCLUSION)는 비활성화한다. 또한, 다음 알려진 모듈이 클래스 경로에서 감지 된 경우 자동으로 등록한다. - [` jackson-datatype-joda`](https://github.com/FasterXML/jackson-datatype-joda) : Joda-Time 타입을 지원한다. - [`jackson-datatype-jsr310`](https://github.com/FasterXML/jackson-datatype-jsr310) : Java 8 Date, Time API 타입을 지원한다. - [`jackson-datatype-jdk8`](https://github.com/FasterXML/jackson-datatype-jdk8) : Optional 등의 다른 Java 8 종류의 지원한다. - [`jackson-module-kotlin`](https://github.com/FasterXML/jackson-module-kotlin) : Kotlin 클래스와 데이터 클래스를 지원한다. ## 1.11.7. 뷰 리졸버(View Resolvers) [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-view-resolvers) 다음의 예는 뷰 리졸버을 구성하는 방법을 보여준다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { // ... } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun configureViewResolvers(registry: ViewResolverRegistry) { // ... } } ``` `ViewResolverRegistry`은 Spring Framework가 통합된 뷰 기술에 대한 간단한 등록 방법을 제공한다. 다음 예제에서는 FreeMarker를 사용하고 있다(여기에는 기본 FreeMarker 뷰 기술의 구성도 필요하다) Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.freeMarker(); } // Configure Freemarker... @Bean public FreeMarkerConfigurer freeMarkerConfigurer() { FreeMarkerConfigurer configurer = new FreeMarkerConfigurer(); configurer.setTemplateLoaderPath("classpath:/templates"); return configurer; } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun configureViewResolvers(registry: ViewResolverRegistry) { registry.freeMarker() } // Configure Freemarker... @Bean fun freeMarkerConfigurer() = FreeMarkerConfigurer().apply { setTemplateLoaderPath("classpath:/templates") } } ``` 다음의 예와 같이, `ViewResolver` 구현을 등록 할 수 있다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { ViewResolver resolver = ... ; registry.viewResolver(resolver); } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun configureViewResolvers(registry: ViewResolverRegistry) { val resolver: ViewResolver = ... registry.viewResolver(resolver } } ``` [컨텐츠 협상(Content Negotiation)](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-multiple-representations)을 지원하고 뷰 리졸버(HTML 이외)을 통해 다른 형식을 렌더링하려면 `spring-web`에서 사용 가능한 [코덱](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-codecs) 중 하나를 받아들이는 `HttpMessageWriterView` 구현에 따라 하나 이상의 기본 뷰를 구성 할 수 있다. 다음의 예는 그 방법을 보여준다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.freeMarker(); Jackson2JsonEncoder encoder = new Jackson2JsonEncoder(); registry.defaultViews(new HttpMessageWriterView(encoder)); } // ... } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun configureViewResolvers(registry: ViewResolverRegistry) { registry.freeMarker() val encoder = Jackson2JsonEncoder() registry.defaultViews(HttpMessageWriterView(encoder)) } // ... } ``` Spring WebFlux와 통합된 뷰 기술에 대한 자세한 내용은 [뷰 기술](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-view)을 참조해라. ## 1.11.8. 정적 리소스(Static Resources) [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-static-resources) 이 옵션은 [`Resource`](https://docs.spring.io/spring-framework/docs/5.3.5/javadoc-api/org/springframework/core/io/Resource.html) 기반 위치 목록에서 정적 리소스를 제공하기 위한 편리한 방법을 제공한다. 다음 예제에서는 `/resources`로 시작하는 요청을 지정하면, 상대 경로를 사용하여 클래스 경로의 `/static`에 관련된 정적 자원을 찾아 처리한다. 브라우저의 캐시를 최대한 사용하고, 브라우저가 수행하는 HTTP 요청을 줄이기 위해, 자원에는 1년간의 유지 기간이 설정하고 있다. `Last-Modified` 헤더가 있는 경우에 그 값을 평가하고 `304` 상태 코드가 반환된다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/public", "classpath:/static/") .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)); } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun addResourceHandlers(registry: ResourceHandlerRegistry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/public", "classpath:/static/") .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)) } } ``` 리소스 핸들러는 [ `ResourceResolver`](https://docs.spring.io/spring-framework/docs/5.3.5/javadoc-api/org/springframework/web/reactive/resource/ResourceResolver.html) 구현, [`ResourceTransformer`](https://docs.spring.io/spring-framework/docs/5.3.5/javadoc-api/org/springframework/web/reactive/resource/ResourceTransformer.html) 구현의 체인도 지원하고 있다. 이것들을 사용하여 최적화된 리소스를 조작하기 위한 툴 체인을 만들 수 있다. 콘텐츠, 고정 응용 프로그램 버전 또는 기타 정보에서 계산된 MD5 해시를 기반으로 버전 관리된 리소스 URL에 `VersionResourceResolver`을 사용할 수 있다. `ContentVersionStrategy`(MD5 해시)은 몇 가지 주의해야 할 예외(모듈 로더에서 사용된 JavaScript 자원 등)이 있는 경우에 적합하다. 다음의 예제는 Java 구성에 `VersionResourceResolver`을 사용하는 방법을 보여준다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/public/") .resourceChain(true) .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**")); } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { override fun addResourceHandlers(registry: ResourceHandlerRegistry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/public/") .resourceChain(true) .addResolver(VersionResourceResolver().addContentVersionStrategy("/**")) } } ``` `ResourceUrlProvider` 를 사용하여 URL을 다시 쓰고, 리졸뷰와 트랜스포머의 전체 체인을 적용 할 수 있다(예를 들어, 버전을 삽입하기 위해). WebFlux 구성은 `ResourceUrlProvider` 을 제공하기 위해, 다른 주입할 수 있다. Spring MVC와 달리 현재 WebFlux는 리졸뷰와 트랜스포머의 논블로킹 체인을 사용할 수 있는 뷰 기술이 없기 때문에, 정적 리소스 URL을 투명하게 바꾸는 방법은 없다. 로컬 자원만을 제공하는 경우 해결 방법은 `ResourceUrlProvider`를 직접 (예를 들어, 사용자 지정 요소를 통해) 사용하여 블로킹하는 것이다. `EncodedResourceResolver`(예를 들어, Gzip, Brotli 인코딩)와 `VersionedResourceResolver` 모두를 사용하는 경우, 인코딩되지 않은 파일에 따라 콘텐츠 기반 버전이 항상 안정적으로 계산되도록 이 순서로 등록해야 한다. [WebJars](https://www.webjars.org/documentation)는 `org.webjars:webjars-locator-core` 라이브러리가 클래스 경로에 존재할 때 자동으로 등록되는 `WebJarsResourceResolver`에도 지원된다. 리졸뷰는 jar 버전을 포함하는 URL을 다시 쓸때, 버전 없는 수신 URL(예를 들어, `/jquery/jquery.min.js`에서 `/jquery/1.2.0/jquery.min.js` 등)과 조합 할 수도 있다. ## 1.11.9. 경로 매칭 [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-path-matching) 경로 매칭에 대한 옵션을 설정할 수 있다. 개별 옵션에 대한 자세한 내용은 [`PathMatchConfigurer`](https://docs.spring.io/spring-framework/docs/5.3.5/javadoc-api/org/springframework/web/reactive/config/PathMatchConfigurer.html) javadoc를 참조해라. 다음 예제는 `PathMatchConfigurer`의 사용 방법을 보여준다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer .setUseCaseSensitiveMatch(true) .setUseTrailingSlashMatch(false) .addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class)); } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { @Override fun configurePathMatch(configurer: PathMatchConfigurer) { configurer .setUseCaseSensitiveMatch(true) .setUseTrailingSlashMatch(false) .addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController::class.java)) } } ``` > Spring WebFlux는 디코딩 된 경로 세그먼트 값에 액세스하기 위해 `RequestPath` 라는 요청 경로 분석 된 표현에 따라 세미콜론 콘텐츠 (즉, 경로 또는 행렬 변수)를 제거한다. 즉, Spring MVC와 달리 요청 경로를 디코딩하거나 경로 일치를 위해 세미콜론 콘텐츠를 삭제할지 여부를 지정할 필요가 없다. > > Spring WebFlux 또한 또한되는 Spring MVC, 그리고 달리 접미사 패턴 매칭을 지원하지 않는 것이 좋습니다 , 그것은 멀리 의존에서 이동한다. ## 1.11.10. WebSocketService WebFlux Java 구성은 WebSocket 핸들러의 호출을 지원하는 `WebSocketHandlerAdapter` Bean을 선언한다. 즉, WebSocket 핸드 셰이크 요청을 처리하기 위해 필요한 것은`WebSocketHandler`을 `SimpleUrlHandlerMapping`를 통해 URL에 매핑 할뿐이다. 경우에 따라서는 `WebSocket` 서버의 속성을 구성 할 수 있는 `WebSocketService` 서비스를 제공하고 `WebSocketHandlerAdapter` Bean을 작성해야 한다. 예를 들면, 아래와 같다. Java ``` @Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public WebSocketService getWebSocketService() { TomcatRequestUpgradeStrategy strategy = new TomcatRequestUpgradeStrategy(); strategy.setMaxSessionIdleTimeout(0L); return new HandshakeWebSocketService(strategy); } } ``` Kotlin ``` @Configuration @EnableWebFlux class WebConfig : WebFluxConfigurer { @Override fun webSocketService(): WebSocketService { val strategy = TomcatRequestUpgradeStrategy().apply { setMaxSessionIdleTimeout(0L) } return HandshakeWebSocketService(strategy) } } ``` ## 1.11.11. 고급 설정 모드(Advanced Configuration Mode) [Web MVC](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-config-advanced-java) `@EnableWebFlux`은 `DelegatingWebFluxConfiguration` 을 가져온다 : - WebFlux 응용 프로그램에 디폴트로 Spring 설정을 제공한다. - `WebFluxConfigurer` 구현을 감지하여 위임하고 그 구성을 사용자 정의한다. 고급 설정 모드의 경우, 다음의 예와 같이, `WebFluxConfigurer` 구현하는 대신에 `@EnableWebFlux` 을 제거하고 `DelegatingWebFluxConfiguration`을 직접 확장할 수 있다. Java ``` @Configuration public class WebConfig extends DelegatingWebFluxConfiguration { // ... } ``` Kotlin ``` @Configuration class WebConfig : DelegatingWebFluxConfiguration { // ... } ``` 기존의 메소드를 `WebConfig`에 보유 할 수 있지만, 기본 클래스에서 Bean 선언을 오버라이딩할 수 있고, 클래스 경로에 다른 `WebMvcConfigurer` 구현을 얼마든지 할 수 있다.

이전 글 : 1.10. HTTP 캐싱
다음 글 : 1.12. HTTP/2