Spring Web Reactive | 1. Spring WebFlux | 1.2. Reactive Core
spring-web
모듈에는 리액티브 Web 응용 프로그램에 대한 다음의 기본적인 지원이 포함되어 있다.
-
서버 요청 처리에는 두 가지 레벨의 지원이 있다.
-
HttpHandler
: Reactor Netty, Undertow, Tomcat, Jetty와 같은 Servlet 3.1+ 컨테이너 용 어댑터와 함께 논블록킹 I/O 및 Reactive Streams 역압력를 사용하여 HTTP 요청을 처리하는 기본 규약이다. -
WebHandler
API : 요청 처리를 위한 약간 높은 수준의 범용 Web API. 그리고, 어노테이션이 선언된 컨트롤러와 함수 엔드 포인트 등의 구체적인 프로그래밍 모델이 구축된다.
-
-
클라이언트는 non-blocking I/O와 Reactive Streams 역압력을 사용하여 HTTP 요청을 실행하기 위한 기본적인
ClientHttpConnector
계약과 Reactor Netty, 리액티브 Jetty HttpClient 및 Apache HttpComponents의 어댑터가 있다. 응용 프로그램에서 사용되는 높은 수준의 WebClient에서 계약을 기반으로 구축되어 있다. -
클라이언트 및 서버의 경우 HTTP 요청 및 응답 내용을 serialize 및 deserialize 용 코덱.
1.2.1. HttpHandler
HttpHandler
는 요청과 응답을 처리하는 단일의 메서드를 가지는 단순한 규칙이 가지고 있다. 이것은 의도적으로 최소화 할 수 있으며, 그 주요 유일한 목적은 다양한 HTTP 서버 API를 최소한으로 추상화하는 것이다.
다음 표에는 지원되는 서버 API를 제공한다.
서버 이름 | 사용되는 서버 API | Reactive Streams 지원 |
---|---|---|
Netty | Netty API | Reactor Netty |
Undertow | Undertow API | spring-web : Undertow에서 Reactive Streams 브리지 |
Tomcat | Servlet 3.1 non-blocking I/O. ByteBuffers와 byte []를 읽고 쓰는 Tomcat API | spring-web : Servlet 3.1 non-blocking I/O에서 Reactive Streams 브리지 |
Jetty | Servlet 3.1 non-blocking I/O. ByteBuffers vs byte []를 작성 Jetty API | spring-web : Servlet 3.1 non-blocking I/O에서 Reactive Streams 브리지 |
Servlet 3.1 컨테이너 | Servlet 3.1 non-blocking I/O | spring-web : Servlet 3.1 non-blocking I/O에서 Reactive Streams 브리지 |
다음 표에는 서버의 종속성을 표시한다. (지원되는 버전 참조)
서버 이름 | 그룹 Id | Artifact 이름 |
---|---|---|
Reactor Netty | io.projectreactor.netty | reactor-netty |
Undertow | io.undertow | undertow-core |
Tomcat | org.apache.tomcat.embed | tomcat-embed-core |
Jetty | org.eclipse.jetty | jetty-server, jetty-servlet |
다음 부분 코드는 각 서버 API로 HttpHandler
어댑터를 사용하는 것을 보여준다.
Reactor Netty
Java
HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create().host(host).port(port).handle(adapter).bind().block();
Kotin
val handler: HttpHandler = ...
val adapter = ReactorHttpHandlerAdapter(handler)
HttpServer.create().host(host).port(port).handle(adapter).bind().block()
Tomcat
Java
HttpHandler handler = ...
Servlet servlet = new TomcatHttpHandlerAdapter(handler);
Tomcat server = new Tomcat();
File base = new File(System.getProperty("java.io.tmpdir"));
Context rootContext = server.addContext("", base.getAbsolutePath());
Tomcat.addServlet(rootContext, "main", servlet);
rootContext.addServletMappingDecoded("/", "main");
server.setHost(host);
server.setPort(port);
server.start();
Kotin
val handler: HttpHandler = ...
val servlet = TomcatHttpHandlerAdapter(handler)
val server = Tomcat()
val base = File(System.getProperty("java.io.tmpdir"))
val rootContext = server.addContext("", base.absolutePath)
Tomcat.addServlet(rootContext, "main", servlet)
rootContext.addServletMappingDecoded("/", "main")
server.host = host
server.setPort(port)
server.start()
Jetty
Java
HttpHandler handler = ...
Servlet servlet = new JettyHttpHandlerAdapter(handler);
Server server = new Server();
ServletContextHandler contextHandler = new ServletContextHandler(server, "");
contextHandler.addServlet(new ServletHolder(servlet), "/");
contextHandler.start();
ServerConnector connector = new ServerConnector(server);
connector.setHost(host);
connector.setPort(port);
server.addConnector(connector);
server.start();
Kotin
val handler: HttpHandler = ...
val servlet = JettyHttpHandlerAdapter(handler)
val server = Server()
val contextHandler = ServletContextHandler(server, "")
contextHandler.addServlet(ServletHolder(servlet), "/")
contextHandler.start();
val connector = ServerConnector(server)
connector.host = host
connector.port = port
server.addConnector(connector)
server.start()
Servlet 3.1+ 컨테이너
Servlet 3.1+ 컨테이너에 WAR로 배포하려면 AbstractReactiveWebInitializer을 확장하여 WAR에 포함 할 수 있다. 그 클래스는 HttpHandler
를 ServletHttpHandlerAdapter
으로 감싸서, 그것을 Servlet으로 등록한다.
1.2.2. WebHandler API
org.springframework.web.server
패키지는 HttpHandler
을 기반으로 여러 WebExceptionHandler
와 여러 WebFilter
, 단일 WebHandler 컨포넌트의 체인을 통해 요청을 처리하기 위한 범용 Web API를 제공한다. 체인 컨포넌트를 자동으로 감지하는 Spring ApplicationContext
를 지정하거나 컨포넌트 빌더에 등록하는 것만으로 WebHttpHandlerBuilder
와 결합 할 수 있다.
HttpHandler
에는 다양한 HTTP 서버의 사용을 추상화한다는 목적이 있지만, WebHandler
API는 다음과 같은 Web 응용 프로그램에서 일반적으로 사용되는 광범위한 기능 세트를 제공하는 것을 목적으로 한다.
- 특성을 가진 사용자 세션.
- 요청 속성.
- 요청의
Locale
또는Principal
를 해결됨. - 분석 및 캐시 된 폼 데이터에 액세스.
- 다중 데이터 추상화.
- 기타 등등..
특별한 Bean 형
다음 표는 WebHttpHandlerBuilder
가 Spring ApplicationContext에서 자동 검출 할 수있는 컨포넌트 또는 직접 등록 할 수 있는 컨포넌트의 목록이다.
Bean 이름 | Bean 형 | 카운트 | 설명 |
---|---|---|---|
<any> | WebExceptionHandler |
0..N | WebFilter 인스턴스의 체인 및 대상의 WebHandler 에서 예외 처리를 제공한다. 자세한 내용은 예외를 참조해라. |
<any> | WebFilter |
0..N | 필터 체인의 나머지와 대상의 WebHandler 전후에 인터셉트 스타일 로직를 적용한다. 자세한 내용은 필터를 참조해라. |
webHandler |
WebHandler |
1 | 요청 핸들러. |
webSessionManager |
WebSessionManager |
0..1 | ServerWebExchange 의 방법을 통해 공개되는 WebSession 인스턴스 매니저. 디폴트는 DefaultWebSessionManager . |
serverCodecConfigurer |
ServerCodecConfigurer |
0..1 | 폼 데이터와 멀티 파트 데이터를 분석하기 위한 HttpMessageReader 인스턴스에 액세스. 이 데이터는 ServerWebExchange 의 메서드를 통해 공개된다. 디폴트는 ServerCodecConfigurer.create() . |
localeContextResolver |
LocaleContextResolver |
0..1 | ServerWebExchange 의 방법을 통해 공개된다 LocaleContext의 확인자. 디폴트는 AcceptHeaderLocaleContextResolver . |
forwardedHeaderTransformer |
ForwardedHeaderTransformer |
0..1 | 전송 된 유형 헤더를 처리하려면 추출하여 제거하거나 삭제하면 된다. 디폴트는 사용되지 않는다. |
폼 데이터(Form Data)
ServerWebExchange
폼 데이터에 액세스하기 위해 다음과 같은 메서드을 제공한다.
Java
Mono<MultiValueMap<String, String>> getFormData();
Kotin
suspend fun getFormData(): MultiValueMap<String, String>
DefaultServerWebExchange
는 구성된 HttpMessageReader
를 사용하여 폼 데이터 (application/x-www-form-urlencoded
)을 분석하고 MultiValueMap
한다. 디폴트로 FormHttpMessageReader
는 ServerCodecConfigurer
Bean을 사용하도록 구성되어 있다 (Web 핸들러 API 참조).
멀티파트 데이터(Multipart Data)
ServerWebExchange는 다중 데이터에 액세스하기 위해 다음과 같은 메서드을 제공한다.
Java
Mono<MultiValueMap<String, Part>> getMultipartData();
Kotin
suspend fun getMultipartData(): MultiValueMap<String, Part>
DefaultServerWebExchange
는 구성된 HttpMessageReader<MultiValueMap<String, Part>>
를 사용하여 multipart/form-data
컨텐츠를 MultiValueMap
로 파싱한다. 디폴트는 DefaultPartHttpMessageReader
이며, 서드파티 의존성은 없다. 또는 Synchronoss NIO Multipart 라이브러리에 기반에 SynchronossPartHttpMessageReader
을 사용할 수 있다. 모두 ServerCodecConfigurer
Bean을 통해 구성된다(WebHandler API 참조).
스트리밍 형식으로 멀티파트 데이터를 파싱하려면 HttpMessageReader<Part>
에서 반환되는 Flux<Part>
를 사용하면 된다. 예를 들어, 어노테이션이 선언된 컨트롤러에서 @RequestPart
사용하면 Map
와 같이 이름으로 각 파트에 접근하는 것을 의미하기 때문에 멀티 파트 데이터를 완벽하게 파싱해야 한다. 대조적으로, @RequestBody
를 사용하여 MultiValueMap
수집하지 않고 컨텐츠를 Flux<Part>
로 디코딩 할 수 있다.
Forwarded 헤더
요청이 프록시(로드 밸런서 등)을 통할 때, 호스트, 포트, 스키마가 변경 될 수 있다. 따라서 클라이언트의 관점에서 올바른 호스트, 포트, 스키마를 가리키는 링크를 만들기 어렵다.
RFC 7239에 따르면 프록시가 원래의 요청에 대한 정보를 제공하는데 사용할 수 있는 Forwarded HTTP 헤더를 정의한다. X-Forwarded-Host
, X-Forwarded-Port
, X-Forwarded-Proto
, X-Forwarded-Ssl
, X-Forwarded-Prefix
등 다른 비표준 헤더도 있다.
ForwardedHeaderTransformer
는 전송 된 헤더에 따라 요청 호스트, 포트, 스키마를 변경하고, 해당 헤더를 제거하는 컨포넌트이다. forwardedHeaderTransformer
라는 Bean으로 선언하면 자동 감지되어 사용할 수 있다.
전송 된 헤더에는 의도 한대로 프록시는 헤더가 추가되었는지, 악의적인 클라이언트에 의해 헤더가 추가되었는지를 응용 프로그램이 인식 할 수 없기 때문에 보안 고려 사항이 있다. 이것이 외부에서 신뢰할 수 없는 전송 트래픽을 삭제하도록 신뢰의 경계에있 는 프록시를 구성해야 하는 이유이다. ForwardedHeaderTransformer
을 removeOnly=true
로 구성 할 수 있다. 이 경우 헤더는 사용되지 않고 제거될 수 있다.
5.1 버전에서는 교환이 생성되기 전에 전송 된 헤더를 더 빨리 처리 할 수 있도록
ForwardedHeaderFilter
는 사용되지 않으며,ForwardedHeaderTransforme
r로 대체되었다. 필터가 구성되어 있는 경우 필터 목록에서 제외되고 대신ForwardedHeaderTransformer
가 사용된다.
5.1 버전에서는
ForwardedHeaderFilter
는 더 이상 사용되지 않도록 deprecated가 선언 되었고, forwarded 헤더를 더 빨리 처리 할 수 있도록ForwardedHeaderTransformer
으로 대체되었다. 필터가 구성되어 있는 경우 필터 목록에서 제외되고 대신ForwardedHeaderTransformer
가 사용된다.
1.2.3. 필터(Filters)
WebHandler
API는 WebFilter
를 사용하여 필터의 나머지 부분 체인 및 대상이 되는 WebHandler
전후에 인터셉션 로직을 적용 할 수 있다. WebFlux 구성을 사용하는 경우, WebFilter
등록은 Spring Bean으로 선언하거나, (옵션으로) Bean 선언에 @Order
를 사용하거나 Ordered
을 구현하여 우선 순위를 간단히 정할 수도 있다.
CORS
Spring WebFlux는 컨트롤러의 어노테이션을 통해 CORS 구성의 상세한 지원을 제공한다. 단, Spring Security에서 사용하는 경우는 Spring Security 체인 필터보다 먼저 정렬해야 하는 내장 CorsFilter
을 사용하는 것이 좋다.
자세한 내용은 CORS 및 webflux-cors.html 섹션을 참조해라.
1.2.4. 예외(Exceptions)
WebHandler
API는 WebExceptionHandler
를 사용하여 WebFilter
인스턴스 체인 및 대상이 되는 WebHandler
에 예외를 처리 할 수 있다. WebFlux 구성을 사용하는 경우, WebExceptionHandler
등록은 Spring Bean으로 선언하거나, (옵션으로) Bean 선언 @Order
를 사용하거나 Ordered
을 구현하여 우선 순위를 간단히 정할 수도 있다.
다음 표에서는 사용 가능한 WebExceptionHandler
구현을 설명하고 있다.
예외 핸들러 | 설명 |
---|---|
ResponseStatusExceptionHandler |
예외의 HTTP 상태 코드에 대한 응답을 설정하여 ResponseStatusException 예외 처리를 제공한다. |
WebFluxResponseStatusExceptionHandler |
@ResponseStatus 어노테이션의 HTTP 상태 코드도 예외 결정할 수 있는 ResponseStatusExceptionHandler 확장. 이 핸들러는 WebFlux 구성에 선언할 수 있다. |
1.2.5 코덱(Codecs)
spring-web
및 spring-core
모듈은 Reactive Streams 역 압력을 사용한 non-blocking I/O를 통해, 높은 수준의 객체 사이의 바이트 컨텐츠를 직렬화 및 역 직렬화하는 지원을 제공한다. 이 지원은 아래에서 설명한다.
Encoder
와Decoder
는 HTTP에 관련 없이 컨텐츠를 인코딩 및 디코딩한다.HttpMessageReader
와HttpMessageWriter
는 HTTP 메시지 컨텐츠를 인코딩 및 디코딩한다.Encoder
는EncoderHttpMessageWriter
를Decoder
는DecoderHttpMessageReader
를 감싸고 있어 Web 응용 프로그램의 사용에 적용 할 수 있다.DataBuffer
는 다양한 바이트 버퍼 표현 (NettyByteBuf
,java.nio.ByteBuffer
등)을 추상화하고 모든 코덱이 작동하는 것이다. 이 주제에 대한 자세한 내용은 “Spring 코어"섹션의 데이터 버퍼 및 코덱을 참조해라.
spring-core
모듈은 byte[]
, ByteBuffer
, DataBuffer
, Resource
, String
인코더 및 디코더의 구현을 제공한다. spring-web
모듈은 Jackson JSON, Jackson Smile, JAXB2, Protocol Buffers 다른 인코더와 디코더, 폼 데이터, 멀티 파트 컨텐츠, 서버 전송 이벤트 등의 Web 전용 HTTP 메시지 Reader와 Writer 구현을 제공한다 .
ClientCodecConfigurer
및 ServerCodecConfigurer
는 일반적으로 응용 프로그램에서 사용하는 코덱 구성 및 사용자 지정하는데 사용된다. HTTP 메시지 코덱 설정 섹션을 참조해라.
Jackson JSON
JSON과 바이너리 JSON (Smile)는 Jackson 라이브러리가 존재하는 경우에 지원된다.
Jackson2Decoder
는 다음과 같이 작동한다.
- Jackson의 비동기 논 블로킹 파서는 바이트 청크 스트림을 각각 JSON 객체를 나타내는
TokenBuffer
에 통합하는데 사용된다. - 각
TokenBuffer
는 Jackson의ObjectMapper
에 전달되어, 보다 더 높은 레벨의 객체를 만든다. - 단일 값 게시자 (예 :
Mono
)로 디코딩하는 경우 하나의TokenBuffer
가 있다. - 다중 값 퍼블리셔 (
Flux
등)으로 디코딩하는 경우, 완전히 형성된 객체에 대해 충분한 바이트가 수신 되자마자 각TokenBuffer
이ObjectMapper
에 전달된다. 입력 내용은 JSON 배열 또는 NDJSON, JSON 행, JSON 텍스트 시퀀스 등의 줄 바꿈(line-delimited) JSON 형식으로 할 수 있다.
Jackson2Encoder
는 다음과 같이 작동한다.
- 단일 값 게시자 (예 :
Mono
)의 경우, 간단히ObjectMapper
를 통해 직렬화한다. application/json
을 사용하는 다중 값 퍼블리셔의 경우는 디폴트로Flux#collectToList()
를 사용하여 값을 수집하고 결과의 컬렉션을 직렬화한다.application/x-ndjson
이나application/stream+x-jackson-smile
등의 스트리밍 미디어 유형을 가진 여러 값 퍼블리셔의 경우는 줄 바꿈(line-delimited) JSON 형식을 사용하여, 값을 개별적으로 인코딩 쓰기 플러시(flush)한다. 다른 스트리밍 미디어 타입은 엔코더에 등록 될 수 있다.- SSE의 경우
Jackson2Encoder
는 이벤트마다 불려 출력은 플러시되고 지체없이 전달된다.
기본적으로
Jackson2Encoder
과Jackson2Decoder
모두 유형String
의 요소를 지원하지 않는다. 대신 기본으로CharSequenceEncoder
의해 렌더링되는 직렬화 된 JSON 컨텐츠를 나타내는 문자열 또는 문자열의 시퀀스이다.Flux<String>
에서 JSON 배열을 렌더링 할 필요가 있는 경우에는Flux#collectToList()
를 사용하여Mono<List<String>>
를 인코딩한다.
폼 데이터(Form Data)
FormHttpMessageReader
와 FormHttpMessageWriter
는 application/x-www-form-urlencoded
컨텐츠의 디코딩 및 인코딩을 지원한다.
여러 곳에서 폼 컨텐츠에 자주 접근해 하는 서버에서는 ServerWebExchange
는 FormHttpMessageReader
를 통해 컨텐츠를 분석하고 반복 사용하기 위해 결과를 캐시 전용 getFormData()
메서드를 제공한다. WebHandler
API 섹션의 폼 데이터를 참조해라.
getFormData()
를 사용하면 원본 컨텐츠를 더 이상 요청 본문에서 읽을 수 없다. 따라서 응용 프로그램은 원시 요청 본문(request body)에서 읽는 대신 캐시 된 양식 데이터에 액세스하기 위해서는 ServerWebExchange
를 통해야 한다.
멀티 파트(Multipart)
MultipartHttpMessageReader
와 MultipartHttpMessageWriter
는"multipart/form-data” 컨텐츠의 디코딩과 인코딩을 지원한다. 다음은 MultipartHttpMessageReader
와 다른 HttpMessageReader
에 위임하여 Flux<Part>
에 파싱하고 그 결과를 MultiValueMap
으로 수집한다. 디폴트는 DefaultPartHttpMessageReader
가 사용되지만, 이것은 ServerCodecConfigurer
을 통해 변경할 수 있다. DefaultPartHttpMessageReader
에 대한 자세한 내용은 DefaultPartHttpMessageReader
의 javadoc를 참조해라.
멀티 파트 폼 컨텐츠에 여러 위치에서 액세스 할 수 있는 서버 사이드에서 ServerWebExchange
는 MultipartHttpMessageReader
를 통해 컨텐츠를 분석하고 반복 사용하기 위해 결과를 캐시 전용 getMultipartData()
메서드를 제공한다. WebHandler
API 섹션의 멀티파트 데이터를 참조해라.
getMultipartData()
를 사용하면 원래의 원시 컨텐츠를 요청 본문에서 읽을 수 없다. 따라서 응용 프로그램은 map과 같이 반복적으로 파트에 접근하기 위해서 getMultipartData()
을 일관되게 사용하거나, Flux<Part>
에 한번의 액세스를 위해 SynchronossPartHttpMessageReader
에 의존해야만 한다 .
제한(Limits)
입력 스트림의 일부 또는 전부를 버퍼링하는 Decoder
와 HttpMessageReader
구현은 메모리에 버퍼링 할 최대 바이트 수의 제한을 설정할 수 있다. 입력이 모인 하나의 객체(예 : @RequestBody byte[]
와 x-www-form-urlencoded
데이터 등의 컨트롤러 메서드)로 표현되기 때문에 버퍼링이 발생할 수 있다. 버퍼링 입력 스트림을 분할 할 때 스트리밍에서도 발생할 수 있다. 예를 들어, 구분 된 텍스트, JSON 객체의 스트림 등이다. 이러한 스트리밍의 경우 제한 스트림에서 하나의 객체와 연결된 바이트에 적용된다.
버퍼 크기를 설정하려면 특정 Decoder
또는 HttpMessageReader
이 maxInMemorySize
속성을 공개하고 있는지 여부를 확인하고, 공개하고 있다면, Javadoc에 기본값 세부 정보가 포함되어 있는지 확인한다. 서버 사이드에서 ServerCodecConfigurer
은 모든 코덱을 설정하기 위한 한군데의 위치를 제공한다. HTTP 메시지 코덱을 참조해라. 클라이언트에서는 모든 코덱의 제한을 WebClient.Builder에서 변경할 수 있다.
멀티파트 파싱의 경우 maxInMemorySize
속성은 비 파일 파트파트의 크기를 제한한다. 파일 파트의 경우는 파트가 디스크에 기록되는 임계 값을 결정한다. 디스크에 기록되는 파일 파트의 경우는 파트 당 디스크 공간의 양을 제한하는 추가 maxDiskUsagePerPart
속성이 있다. 멀티 파트 요청의 파트 수를 제한하는 maxParts
속성도 있다. WebFlux에서 세 가지를 모두 구성하려면 구성된 MultipartHttpMessageReader
에서 ServerCodecConfigurer
의 인스턴스를 제공해야 한다.
스트리밍(Streaming)
HTTP 응답 (예를 들어, text/event-stream
, application/x-ndjson
)에 스트리밍하는 경우, 연결이 끊어진 클라이언트를 가능한 빠르게 감지하기 위해 주기적으로 데이터를 전송하는 것이 중요하다 . 이러한 전송은 짧은 문자열이나 버어 있는 SSE 이벤트 또는 하트 비트로서 효과적으로 기능하는 기타 “no-op” 데이터 일 수 있다.
DataBuffer
DataBuffer
는 WebFlux 바이트 버퍼의 표현이다. 이 레퍼런스 Spring 코어 부분은 데이터 버퍼 및 코덱 섹션에서 자세히 설명하고 있다. 이해해야 할 중요한 점은 Netty와 같은 일부 서버에서는 바이트 버퍼가 풀링 된 참조를 계산 메모리 누수를 방지하기 위해 소비(consume)되고, 반드시 릴리즈를 해야 한다.
WebFlux 응용 프로그램은 코덱에 따라 높은 수준의 객체 사이의 변환하지 않고 데이터 버퍼를 직접 소비하거나 생성하지 않는 한 또는 사용자 지정 코덱을 만드는 것을 선택하지 않는 한 일반적으로 그런 과제를 고려할 필요는 없다. 그런 경우는 데이터 버퍼 및 코덱 정보, 특히 DataBuffer
의 사용 섹션을 확인해라.
1.2.6. 로깅
Spring에서 DEBUG
레벨의 로그 WebFlux는 가볍고, 최소한으로, 사람이 보기 편하게 설계되어 있다. 이것은 특정 문제를 디버깅 할 경우에만 유용한 다른 정보와 비교하여 지속적으로 도움이 되는 정보의 중점을 두고 있다.
TRACE
레벨의 로그는 일반적으로 DEBUG
와 같은 원칙을 따른다 (예를 들어, 소방 호스(firehose)가 되어서는 안된다). 다만 문제의 디버깅에 사용될 수 있다. 또한 일부 로그 메시지는 TRACE
와 DEBUG
에서 상세한 레벨이 다를 수도 있다.
해당 로그는 로그를 사용한 경험에서 얻을 수 있다. 기재되어 있는 목적와 다른 것을 발견하면 알려 주시기 바란다.
로그 ID
WebFlux는 단일 요청을 여러 스레드에서 실행할 수 있다. 스레드 ID는 특정 요청에 속하는 로그 메시지 상관 관계에 찾기 힘들다. 그래서 기본적으로 WebFlux 로그 메시지의 시작 부분에 요청 고유한 ID가 붙는다.
서버에서 로그 ID는 ServerWebExchange
특성 (LOG_ID_ATTRIBUTE)에 저장되지만, 그 ID를 기반으로 완전히 포맷 된 접두사는 ServerWebExchange#getLogPrefix()
에서 사용할 수 있다. WebClient
에서 로그 ID는 ClientRequest
특성 (LOG_ID_ATTRIBUTE)에 저장되지만 완전히 포맷 된 접두사는 ClientRequest#logPrefix()
에서 구할 수 있다.
민감한 데이터
DEBUG
및 TRACE
로깅은 기밀 정보를 기록 할 수 있다. 그래서 폼 매개 변수와 헤더가 기본적으로 마스크되어 있으며, 이에 대한 로깅을 완전히 명시적으로 활성화해야 한다.
다음 예제는 서버 사이드 요청에 대해 이렇게 하는 방법을 보여준다.
Java
@Configuration
@EnableWebFlux
class MyConfig implements WebFluxConfigurer {
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().enableLoggingRequestDetails(true);
}
}
Kotlin
@Configuration
@EnableWebFlux
class MyConfig : WebFluxConfigurer {
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
configurer.defaultCodecs().enableLoggingRequestDetails(true)
}
}
다음 예제는 클라이언트 요청에 대해 이를 수행하는 방법 보여준다.
Java
Consumer<ClientCodecConfigurer> consumer = configurer ->
configurer.defaultCodecs().enableLoggingRequestDetails(true);
WebClient webClient = WebClient.builder()
.exchangeStrategies(strategies -> strategies.codecs(consumer))
.build();
Kotlin
val consumer: (ClientCodecConfigurer) -> Unit = { configurer -> configurer.defaultCodecs().enableLoggingRequestDetails(true) }
val webClient = WebClient.builder()
.exchangeStrategies({ strategies -> strategies.codecs(consumer) })
.build()
Appenders
SLF4J과 Log4J2 등의 로깅 라이브러리는 블로킹을 피하기 비동기 로거를 제공한다. 로깅을 위해 대기열에 넣을 수 없는 메시지를 삭제하는 것과 같은 자체 단점들이 있지만, 현재 리액티브, 논블로킹 응용 프로그램에서 사용할 수있는 최선의 선택이다.
사용자 정의 코덱
응용 프로그램은 추가의 미디어 타입을 지원하는 커스텀 코덱, 또는 기본 코덱에서 지원되지 않는 특정 동작을 등록 할 수 있다.
개발자에 의해 표현 된 일부 구성 옵션은 기본 코덱에 적용된다. 사용자 정의 코덱은 버퍼링 제한의 강제하거나 기밀 데이터 로깅 등이 설정에 맞게 조정할 기회를 얻고 싶은 경우가 있다.
다음 예제는 클라이언트 측의 요청에 대해 커스텀 코텍을 설정하는 방법을 보여준다.
Java
WebClient webClient = WebClient.builder()
.codecs(configurer -> {
CustomDecoder decoder = new CustomDecoder();
configurer.customCodecs().registerWithDefaultConfig(decoder);
})
.build();
Kotlin
val webClient = WebClient.builder()
.codecs({ configurer ->
val decoder = CustomDecoder()
configurer.customCodecs().registerWithDefaultConfig(decoder)
})
.build()