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 HttpClientApache 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에 포함 할 수 있다. 그 클래스는 HttpHandlerServletHttpHandlerAdapter으로 감싸서, 그것을 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한다. 디폴트로 FormHttpMessageReaderServerCodecConfigurer Bean을 사용하도록 구성되어 있다 (Web 핸들러 API 참조).

멀티파트 데이터(Multipart Data)

Web MVC

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 헤더

Web MVC

요청이 프록시(로드 밸런서 등)을 통할 때, 호스트, 포트, 스키마가 변경 될 수 있다. 따라서 클라이언트의 관점에서 올바른 호스트, 포트, 스키마를 가리키는 링크를 만들기 어렵다.

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는 사용되지 않으며, ForwardedHeaderTransformer로 대체되었다. 필터가 구성되어 있는 경우 필터 목록에서 제외되고 대신 ForwardedHeaderTransformer가 사용된다.

5.1 버전에서는 ForwardedHeaderFilter는 더 이상 사용되지 않도록 deprecated가 선언 되었고, forwarded 헤더를 더 빨리 처리 할 수 있도록 ForwardedHeaderTransformer으로 대체되었다. 필터가 구성되어 있는 경우 필터 목록에서 제외되고 대신 ForwardedHeaderTransformer가 사용된다.

1.2.3. 필터(Filters)

Web MVC

WebHandler APIWebFilter를 사용하여 필터의 나머지 부분 체인 및 대상이 되는 WebHandler 전후에 인터셉션 로직을 적용 할 수 있다. WebFlux 구성을 사용하는 경우, WebFilter 등록은 Spring Bean으로 선언하거나, (옵션으로) Bean 선언에 @Order를 사용하거나 Ordered을 구현하여 우선 순위를 간단히 정할 수도 있다.

CORS

Web MVC

Spring WebFlux는 컨트롤러의 어노테이션을 통해 CORS 구성의 상세한 지원을 제공한다. 단, Spring Security에서 사용하는 경우는 Spring Security 체인 필터보다 먼저 정렬해야 하는 내장 CorsFilter을 사용하는 것이 좋다.

자세한 내용은 CORS 및 webflux-cors.html 섹션을 참조해라.

1.2.4. 예외(Exceptions)

Web MVC

WebHandler APIWebExceptionHandler를 사용하여 WebFilter 인스턴스 체인 및 대상이 되는 WebHandler에 예외를 처리 할 수 있다. WebFlux 구성을 사용하는 경우, WebExceptionHandler 등록은 Spring Bean으로 선언하거나, (옵션으로) Bean 선언 @Order를 사용하거나 Ordered을 구현하여 우선 순위를 간단히 정할 수도 있다.

다음 표에서는 사용 가능한 WebExceptionHandler 구현을 설명하고 있다.

예외 핸들러 설명
ResponseStatusExceptionHandler 예외의 HTTP 상태 코드에 대한 응답을 설정하여 ResponseStatusException 예외 처리를 제공한다.
WebFluxResponseStatusExceptionHandler @ResponseStatus 어노테이션의 HTTP 상태 코드도 예외 결정할 수 있는 ResponseStatusExceptionHandler 확장. 이 핸들러는 WebFlux 구성에 선언할 수 있다.

1.2.5 코덱(Codecs)

Web MVC

spring-web spring-core 모듈은 Reactive Streams 역 압력을 사용한 non-blocking I/O를 통해, 높은 수준의 객체 사이의 바이트 컨텐츠를 직렬화 및 역 직렬화하는 지원을 제공한다. 이 지원은 아래에서 설명한다.

  • Encoder Decoder는 HTTP에 관련 없이 컨텐츠를 인코딩 및 디코딩한다.
  • HttpMessageReader HttpMessageWriter는 HTTP 메시지 컨텐츠를 인코딩 및 디코딩한다.
  • EncoderEncoderHttpMessageWriterDecoderDecoderHttpMessageReader를 감싸고 있어 Web 응용 프로그램의 사용에 적용 할 수 있다.
  • DataBuffer는 다양한 바이트 버퍼 표현 (Netty ByteBuf, java.nio.ByteBuffer 등)을 추상화하고 모든 코덱이 작동하는 것이다. 이 주제에 대한 자세한 내용은 “Spring 코어"섹션의 데이터 버퍼 및 코덱을 참조해라.

spring-core 모듈은 byte[], ByteBuffer, DataBuffer, Resource, String 인코더 및 디코더의 구현을 제공한다. spring-web 모듈은 Jackson JSON, Jackson Smile, JAXB2, Protocol Buffers 다른 인코더와 디코더, 폼 데이터, 멀티 파트 컨텐츠, 서버 전송 이벤트 등의 Web 전용 HTTP 메시지 Reader와 Writer 구현을 제공한다 .

ClientCodecConfigurerServerCodecConfigurer는 일반적으로 응용 프로그램에서 사용하는 코덱 구성 및 사용자 지정하는데 사용된다. HTTP 메시지 코덱 설정 섹션을 참조해라.

Jackson JSON

JSON과 바이너리 JSON (Smile)는 Jackson 라이브러리가 존재하는 경우에 지원된다.

Jackson2Decoder는 다음과 같이 작동한다.

  • Jackson의 비동기 논 블로킹 파서는 바이트 청크 스트림을 각각 JSON 객체를 나타내는 TokenBuffer에 통합하는데 사용된다.
  • TokenBuffer는 Jackson의 ObjectMapper에 전달되어, 보다 더 높은 레벨의 객체를 만든다.
  • 단일 값 게시자 (예 : Mono)로 디코딩하는 경우 하나의 TokenBuffer가 있다.
  • 다중 값 퍼블리셔 (Flux 등)으로 디코딩하는 경우, 완전히 형성된 객체에 대해 충분한 바이트가 수신 되자마자 각 TokenBufferObjectMapper에 전달된다. 입력 내용은 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는 이벤트마다 불려 출력은 플러시되고 지체없이 전달된다.

기본적으로 Jackson2EncoderJackson2Decoder 모두 유형 String의 요소를 지원하지 않는다. 대신 기본으로 CharSequenceEncoder 의해 렌더링되는 직렬화 된 JSON 컨텐츠를 나타내는 문자열 또는 문자열의 시퀀스이다. Flux<String>에서 JSON 배열을 렌더링 할 필요가 있는 경우에는 Flux#collectToList()를 사용하여 Mono<List<String>>를 인코딩한다.

폼 데이터(Form Data)

FormHttpMessageReaderFormHttpMessageWriterapplication/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를 참조해라.

멀티 파트 폼 컨텐츠에 여러 위치에서 액세스 할 수 있는 서버 사이드에서 ServerWebExchangeMultipartHttpMessageReader를 통해 컨텐츠를 분석하고 반복 사용하기 위해 결과를 캐시 전용 getMultipartData() 메서드를 제공한다. WebHandler API 섹션의 멀티파트 데이터를 참조해라.

getMultipartData()를 사용하면 원래의 원시 컨텐츠를 요청 본문에서 읽을 수 없다. 따라서 응용 프로그램은 map과 같이 반복적으로 파트에 접근하기 위해서 getMultipartData()을 일관되게 사용하거나, Flux<Part>에 한번의 액세스를 위해 SynchronossPartHttpMessageReader에 의존해야만 한다 .

제한(Limits)

입력 스트림의 일부 또는 전부를 버퍼링하는 DecoderHttpMessageReader 구현은 메모리에 버퍼링 할 최대 바이트 수의 제한을 설정할 수 있다. 입력이 모인 하나의 객체(예 : @RequestBody byte[]x-www-form-urlencoded 데이터 등의 컨트롤러 메서드)로 표현되기 때문에 버퍼링이 발생할 수 있다. 버퍼링 입력 스트림을 분할 할 때 스트리밍에서도 발생할 수 있다. 예를 들어, 구분 된 텍스트, JSON 객체의 스트림 등이다. 이러한 스트리밍의 경우 제한 스트림에서 하나의 객체와 연결된 바이트에 적용된다.

버퍼 크기를 설정하려면 특정 Decoder 또는 HttpMessageReadermaxInMemorySize 속성을 공개하고 있는지 여부를 확인하고, 공개하고 있다면, Javadoc에 기본값 세부 정보가 포함되어 있는지 확인한다. 서버 사이드에서 ServerCodecConfigurer은 모든 코덱을 설정하기 위한 한군데의 위치를 제공한다. HTTP 메시지 코덱을 참조해라. 클라이언트에서는 모든 코덱의 제한을 WebClient.Builder에서 변경할 수 있다.

멀티파트 파싱의 경우 maxInMemorySize 속성은 비 파일 파트파트의 크기를 제한한다. 파일 파트의 경우는 파트가 디스크에 기록되는 임계 값을 결정한다. 디스크에 기록되는 파일 파트의 경우는 파트 당 디스크 공간의 양을 제한하는 추가 maxDiskUsagePerPart 속성이 있다. 멀티 파트 요청의 파트 수를 제한하는 maxParts 속성도 있다. WebFlux에서 세 가지를 모두 구성하려면 구성된 MultipartHttpMessageReader에서 ServerCodecConfigurer의 인스턴스를 제공해야 한다.

스트리밍(Streaming)

Web MVC

HTTP 응답 (예를 들어, text/event-stream, application/x-ndjson)에 스트리밍하는 경우, 연결이 끊어진 클라이언트를 가능한 빠르게 감지하기 위해 주기적으로 데이터를 전송하는 것이 중요하다 . 이러한 전송은 짧은 문자열이나 버어 있는 SSE 이벤트 또는 하트 비트로서 효과적으로 기능하는 기타 “no-op” 데이터 일 수 있다.

DataBuffer

DataBuffer는 WebFlux 바이트 버퍼의 표현이다. 이 레퍼런스 Spring 코어 부분은 데이터 버퍼 및 코덱 섹션에서 자세히 설명하고 있다. 이해해야 할 중요한 점은 Netty와 같은 일부 서버에서는 바이트 버퍼가 풀링 된 참조를 계산 메모리 누수를 방지하기 위해 소비(consume)되고, 반드시 릴리즈를 해야 한다.

WebFlux 응용 프로그램은 코덱에 따라 높은 수준의 객체 사이의 변환하지 않고 데이터 버퍼를 직접 소비하거나 생성하지 않는 한 또는 사용자 지정 코덱을 만드는 것을 선택하지 않는 한 일반적으로 그런 과제를 고려할 필요는 없다. 그런 경우는 데이터 버퍼 및 코덱 정보, 특히 DataBuffer의 사용 섹션을 확인해라.

1.2.6. 로깅

Web MVC

Spring에서 DEBUG 레벨의 로그 WebFlux는 가볍고, 최소한으로, 사람이 보기 편하게 설계되어 있다. 이것은 특정 문제를 디버깅 할 경우에만 유용한 다른 정보와 비교하여 지속적으로 도움이 되는 정보의 중점을 두고 있다.

TRACE 레벨의 로그는 일반적으로 DEBUG와 같은 원칙을 따른다 (예를 들어, 소방 호스(firehose)가 되어서는 안된다). 다만 문제의 디버깅에 사용될 수 있다. 또한 일부 로그 메시지는 TRACEDEBUG에서 상세한 레벨이 다를 수도 있다.

해당 로그는 로그를 사용한 경험에서 얻을 수 있다. 기재되어 있는 목적와 다른 것을 발견하면 알려 주시기 바란다.

로그 ID

WebFlux는 단일 요청을 여러 스레드에서 실행할 수 있다. 스레드 ID는 특정 요청에 속하는 로그 메시지 상관 관계에 찾기 힘들다. 그래서 기본적으로 WebFlux 로그 메시지의 시작 부분에 요청 고유한 ID가 붙는다.

서버에서 로그 ID는 ServerWebExchange 특성 (LOG_ID_ATTRIBUTE)에 저장되지만, 그 ID를 기반으로 완전히 포맷 된 접두사는 ServerWebExchange#getLogPrefix()에서 사용할 수 있다. WebClient에서 로그 ID는 ClientRequest 특성 (LOG_ID_ATTRIBUTE)에 저장되지만 완전히 포맷 된 접두사는 ClientRequest#logPrefix()에서 구할 수 있다.

민감한 데이터

Web MVC

DEBUGTRACE 로깅은 기밀 정보를 기록 할 수 있다. 그래서 폼 매개 변수와 헤더가 기본적으로 마스크되어 있으며, 이에 대한 로깅을 완전히 명시적으로 활성화해야 한다.

다음 예제는 서버 사이드 요청에 대해 이렇게 하는 방법을 보여준다.

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()



최종 수정 : 2021-04-12