Reactive Core

설명

스프링-웹 모듈에는 반응형 웹 애플리케이션을 위한 다음과 같은 기본 지원이 포함되어 있다.

  • 서버 요청 처리에는 두 가지 수준의 지원한다.
    • HttpHandler: 논블로킹 I/O 및 리액티브 스트림 Back Pressure을 사용하는 HTTP 요청을 처리하며, Reactor Netty, Undertow, Tomcat, Jetty 및 모든 Servlet 3.1+ 컨테이너용 어댑터와 함께 사용한다.
    • WebHandler API: 요청 처리를 위한 약간 더 높은 수준의 범용 웹 API로, 주석이 달린 컨트롤러 및 기능적 엔드포인트와 같은 구체적인 프로그래밍 모델로 작성되어 있다.
  • 클라이언트 사이드에서는 논블로킹 I/O 및 리액티브 스트림 Back Pressure으로 HTTP 요청을 수행하는 기본 ClientHttpConnector 계약과 함께 Reactor Netty, 리액티브 Jetty HttpClient 및 Apache HttpComponents용 어댑터가 있다. 애플리케이션에 사용되는 상위 수준의 WebClient는 이를 기반으로 구축된다.
  • 클라이언트와 서버 모두 코덱으로 HTTP 요청 및 응답 콘텐츠의 직렬화 및 역직렬화를 한다.

HttpHandler

HttpHandler는 요청과 응답을 처리하는 메소드를 하나만 가지고 있다. 의도적으로 최소한의 기능을 제공하며, 주된 목적은 다양한 HTTP 서버 API에 대한 최소한의 추상화이다.
지원하는 서버의 API는 아래 표와 같다.

서버 이름 사용하는 Servlet API 리액티브 스트림 지원
NettyNetty APIReactor Netty
UndertowUndertow API APIspring-web: Undertow to 리액티브 스트림 브릿지
Tomcat서블릿 3.1 논블로킹 I/O; ByteBuffers로 byte[]를 읽고 쓰는 Tomcat APIspring-web: 서블릿 3.1 논블로킹 I/O to 리액티브 스트림 브릿지
Jetty서블릿 3.1 논블로킹 I/O; ByteBuffers로 byte[]를 읽고 쓰는 Jetty APIspring-web: 서블릿 3.1 논블로킹 I/O to 리액티브 스트림 브릿지
서블릿 3.1 컨테이너서블릿 3.1 논블로킹 I/Ospring-web: 서블릿 3.1 논블로킹 I/O to 리액티브 스트림 브릿지


서버 Dependency는 아래 표와 같다(지원 버전 참고)

서버 이름 Group ID Artifact Name
Reactor Nettyio.projectreactor.nettyreactor-netty
Undertowio.undertowundertow-core
Tomcatorg.apache.tomcat.embedtomcat-embed-core
Jettyorg.eclipse.jettyjetty-server, jetty-servlet


다음은 각 서버의 API 어댑터를 활용하는 HttpHandler이다.

리액터 Netty

HttpHandler handler = ...
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(handler);
HttpServer.create().host(host).port(port).handle(adapter).bind().block();

Undertow

HttpHandler handler = ...
UndertowHttpHandlerAdapter adapter = new UndertowHttpHandlerAdapter(handler);
Undertow server = Undertow.builder().addHttpListener(port, host).setHandler(adapter).build();
server.start();

Tomcat

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

Jetty

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

서블릿 3.1+ 컨테이너

서블릿 3.1+ 컨테이너에 WAR를 배포하려면 WAR에 AbstractReactiveWebInitializer를 확장하여 추가하면 된다. 이 클래스는 HttpHandler, ServletHttpHandlerAdapter를 감싸고 있으며, 이 핸들러를 서블릿으로 등록한다.

WebHandler API

org.springframework.web.server 패키지를 보면 HttpHandler가 WebHandler와 여러 WebExceptionHandler, WebFilter로 체인을 형성해 요청을 처리하는 범용 웹 API를 제공한다.
WebHttpHandlerBuilder에 컴포넌트를 등록하거나 스프링 ApplicationContext 위치만 알려주면 자동으로 컴포넌트 체인에 추가한다.
HttpHandler는 서로 다른 HTTP 서버를 쓰기 위한 추상화가 전부인 반면, WebHandler API는 아래와 같이 웹 애플리케이션에서 흔히 쓰는 광범위한 기능을 제공한다.

  • User session과 Session attributes
  • Request attributes.
  • Locale, Principal 리졸브
  • form 데이터 파싱, 캐싱 조회
  • multipart 데이터 추상화
  • 기타 등등

Form Data

ServerWebExchange는 form 데이터에 접근할 수 있는 메소드를 제공한다.

Mono<MultiValueMap<String, String>> getFormData();

DefaultServerWebExchange는 설정에 있는 HttpMessageReader를 사용해 form 데이터를 MultiValueMap으로 파싱한다. 디폴트로 사용하는 리더는 ServerCodecConfigurer 빈에 있는 FormHttpMessageReader이다.

Multipart Data

ServerWebExchange는 multipart 데이터에 접근할 수 있는 메소드를 제공한다.

Mono<MultiValueMap<String, Part>> getMultipartData();

DefaultServerWebExchange는 설정에 있는 HttpMessageReader<MultiValueMap<String, Part»를 사용해 multipart/form-data 컨텐츠를 MultiValueMap으로 파싱한다. 현재로써는 Synchronoss NIO Multipart가 유일하게 지원하는 서브파티 라이브러리이며, 논블로킹으로 multipart 요청을 파싱하는 유일한 라이브러리이다. ServerCodecConfigurer 빈으로 활성화할 수 있다.
스트리밍 방식으로 multipart 데이터를 파싱하려면 HttpMessageReader<Part>가 리턴하는 Flux<Part>를 사용한다. 예를 들어 컨트롤러에서 @RequestPart를 선언하면 Map처럼 이름으로 각 파트에 접근하겠다는 뜻이므로 multipart 데이터를 한번에 파싱해야 한다. 반대로 Flux<Part> 타입에 @RequestPart를 사용허면 컨텐츠를 디코딩할 때 MultiValueMap에 수집하지 않는다.

Forwarded Headers

Load Balancers와 같이 프톡시를 경유한 요청은 호스트, 포트, URL 스킴ㅇ이 변경될 수 있어서 클라이언트 입장에서는 원래의 URL 정보를 알아내기 어렵다.
RFC 7239 정의에 따르면 Forwarded HTTP 헤더는 프록시가 원래 요청에 대한 정보를 추가하는 헤다이다. X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Proto, X-Forwarded-Ssl, and X-Forwarded-Prefix 같은 비표준 헤더도 존재한다.
ForwardedHeaderTransformer는 forwarded 헤더를 보고 요청의 호스트, 포트, 스킴을 바꿔준 다음, 헤더를 제거하는 컴포넌트이다. ForwardedHeaderTransformer라는 이름으로 빈을 정의하면 자동으로 체인에 추가된다.
forwarded 헤더는 보안에 신경써야 할 요소가 있는데 프록시가 헤더를 추가한건지, 클라이언트가 악의적으로 추가한 것인지 애플리케이션에서는 알 수 없기 때문이다.
따라서 외부에서 들어오는 신뢰할 수 없는 프록시 요청을 제거할 때 ForwardedHeaderTransformer를 removeOnly=true로 설정하여 헤더 정보를 사용하지 않고 제거할 수 있다.

5.1 버전부터 ForwardedHeaderFilter는 제거대상(deprecated)이 되어 ForwardedHeaderTransformer롤 대신한다. 따라서 exchange(http 요청/응답과 세션 정보 등의 컨테이너)를 만들기 전에 forwarded 헤더를 처리할 수 있다. 필터를 사용하더라도 이 필터는 전체 필터 리스트에서 제외되며 그 대신 ForwardedHeaderTransformer를 사용한다.

Filters

WebHandler API에서는 WebFilter를 사용하면 다른 필터 체인과 WebHandler 전후에 요청을 가로채서 원하는 로직을 넣을 수 있다. WebFilter를 등록하려면 스프링 빈으로 만들어 빈 위에 @Order를 선언아거나 Ordered를 구현해 순서를 정해도 되고, WebFlux Config를 사용해도 된다.

CORS

CORS는 컨트롤러에 어노테이션을 선언하는 것으로 잘 동작한다. Spring Security와 사용하면 CorsFilter를 사용해서 Spring Security 필터 체인보다 먼저 처리하도록 해야 한다.

Exceptions

WebHandler API에서는 WebFilter 체인과 WebHandler에서 발생한 예외를 WebExceptionHandler로 처리한다. WebExceptionHandler를 등록하려면 스프링 빈으로 빈 위에 @Order를 선언아거나 Ordered를 구현해 순서를 정해도 되고, WebFlux Config를 사용해도 된다.
아래 표는 바로 사용할 수 있는 WebExceptionHandler 구현체이다.

Exception Handler Description
ResponseStatusExceptionHandlerHttp status code를 지정할 수 있는 ResponseStatusException을 처리한다.
WebFluxResponseStatusExceptionHandlerResponseStatusExceptionHandler를 확장하는 것으로 다른 exception 타입도 @ResponseStatus를 선언해서 HTTP status code를 정할 수 있다.
이 핸들러는 WebFlux Config 안에 선언되어 있다.

참고자료

 
egovframework/rte4.2/ptl/web_reactive_core.txt · 마지막 수정: 2024/02/26 01:22 (외부 편집기)
 
이 위키의 내용은 다음의 라이센스에 따릅니다 :CC Attribution-Noncommercial-Share Alike 3.0 Unported
전자정부 표준프레임워크 라이센스(바로가기)

전자정부 표준프레임워크 활용의 안정성 보장을 위해 위험성을 지속적으로 모니터링하고 있으나, 오픈소스의 특성상 문제가 발생할 수 있습니다.
전자정부 표준프레임워크는 Apache 2.0 라이선스를 따르고 있는 오픈소스 프로그램입니다. Apache 2.0 라이선스에 따라 표준프레임워크를 활용하여 발생된 업무중단, 컴퓨터 고장 또는 오동작으로 인한 손해 등에 대해서 책임이 없습니다.
Recent changes RSS feed CC Attribution-Noncommercial-Share Alike 3.0 Unported Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki