songining
article thumbnail

Spring WebFlux๊ฐ€ ํฐ ํ‹€์—์„œ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ณ ์ž ํ•œ๋‹ค.

Spring MVC ์˜ ๊ฒฝ์šฐ์—๋Š” DispatcherServlet ๊ธฐ๋ฐ˜์œผ๋กœ ์š”์ฒญ์„ ๋ฐ›์•„ ๋’ท๋‹จ์˜ ์ฒ˜๋ฆฌ๊ฐ€ ์ด๋ฃจ์–ด์ง„๋‹ค. Spring WebFlux์˜ ๊ฒฝ์šฐ์—๋„ DispatcherServlet๊ณผ ๊ฐ™์ด front controller ์˜ ์—ญํ• ์„ ํ•ด์ฃผ๋Š” ์นœ๊ตฌ๊ฐ€ ์กด์žฌํ•œ๋‹ค. 

 

์ผ๋‹จ ๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜๋ฉด ๋Œ€์ถฉ ๋ถ„์„ํ•ด๋ณธ ๊ฒƒ๋“ค์„ ์š”์•ฝํ–ˆ์„ ๋•Œ ์•„๋ž˜ ๊ฐ™์€ ๊ทธ๋ฆผ์ด๋‹ค. (๊ฐ„๋‹จํ•˜๊ฒŒ ํ‘œํ˜„ํ•ด์„œ ์ƒ๋žต์ด ๋งŽ์„ ์ˆ˜ ์žˆ๋‹ค.)

 

 

1. DispatcherHandler (WebHandler์˜ ๊ตฌํ˜„์ฒด)

DispatcherHandler ๋Š” WebHandler ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋กœ Spring Webflux์—์„œ์˜ front controller ์—ญํ• ์„ ํ•œ๋‹ค. 

 

์•„๋ž˜๋Š” DispatchHandler ํด๋ž˜์Šค์ด๋‹ค. Spring MVC์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ DispatcherHandler๋Š” HandlerMapping, HandlerAdapter, ๊ทธ๋ฆฌ๊ณ  ์ถ”๊ฐ€์ ์œผ๋กœ ResultHandler ์™€์˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

  • handlerMapping : ์š”์ฒญ์„ ์–ด๋–ค controller๊ฐ€ ์ฒ˜๋ฆฌํ•  ์ง€ ๊ฒฐ์ •
  • handlerAdapter : HandlerMapping ์œผ๋กœ ์„ ํƒ๋œ controller ๋˜๋Š” router ๋ฅผ ํ˜ธ์ถœ
  • handlerResultHandler : ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‘๋‹ต
<java />
public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware { @Nullable private List<HandlerMapping> handlerMappings; @Nullable private List<HandlerAdapter> handlerAdapters; @Nullable private List<HandlerResultHandler> resultHandlers; public DispatcherHandler() { } public DispatcherHandler(ApplicationContext applicationContext) { initStrategies(applicationContext); } @Nullable public final List<HandlerMapping> getHandlerMappings() { return this.handlerMappings; } @Override public void setApplicationContext(ApplicationContext applicationContext) { initStrategies(applicationContext); } protected void initStrategies(ApplicationContext context) { Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( context, HandlerMapping.class, true, false); ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values()); AnnotationAwareOrderComparator.sort(mappings); this.handlerMappings = Collections.unmodifiableList(mappings); Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors( context, HandlerAdapter.class, true, false); this.handlerAdapters = new ArrayList<>(adapterBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerAdapters); Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors( context, HandlerResultHandler.class, true, false); this.resultHandlers = new ArrayList<>(beans.values()); AnnotationAwareOrderComparator.sort(this.resultHandlers); } @Override public Mono<Void> handle(ServerWebExchange exchange) { if (this.handlerMappings == null) { return createNotFoundError(); } if (CorsUtils.isPreFlightRequest(exchange.getRequest())) { return handlePreFlight(exchange); } return Flux.fromIterable(this.handlerMappings) .concatMap(mapping -> mapping.getHandler(exchange)) .next() .switchIfEmpty(createNotFoundError()) .flatMap(handler -> invokeHandler(exchange, handler)) .flatMap(result -> handleResult(exchange, result)); } ... }

 

1. handle (ServerWebExchange serverWebExchange)

  • request ๋ฅผ ๋ฐ›์•„ handlerMapping ์„ ํ†ตํ•ด ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งคํ•‘ํ•˜๊ณ  handlerAdapter๋ฅผ ํ†ตํ•ด ๋ผ์šฐํ„ฐ๋ฅผ ํ˜ธ์ถœ ํ›„ response๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ชจ๋“  ์ผ๋ จ์˜ ๊ณผ์ •์„ ๋‹ด์€ ๋ฉ”์„œ๋“œ์ด๋‹ค.
  • serverWebExchange๋Š” ์š”์ฒญ(ServerHttpRequest)๊ณผ ์‘๋‹ต(ServerHttpResponse) ๋ชจ๋‘์— ๋Œ€ํ•œ ์ปจํ…์ŠคํŠธ๋ฅผ ์ œ๊ณต ๋ฐ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค.

2. initStrategies(ApplicationContext context)

  • ApplicationContext์˜ Bean(HandlerMapping, HandlerAdapter, HandlerResultHandler) ์„ ์ฐพ์•„ ์ฃผ์ž…ํ•ด์ค€๋‹ค.

 

2. HttpWebHandlerAdapter (HttpHandler ์˜ ๊ตฌํ˜„์ฒด)

 

์š”์ฒญ์— ๋Œ€ํ•œ lowest level ๋™์ž‘์„ ๋งก๊ณ  ์žˆ๋Š” HttpHandler ์˜ ๊ตฌํ˜„์ฒด๋กœ, WebHandler๋กœ ์š”์ฒญ์„ ๋„˜๊ธฐ๊ธฐ ์ „์— ๋‚ด๋ถ€์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค. 

์ €์ˆ˜์ค€ ๋ ˆ๋ฒจ์˜ ๊ตฌํ˜„์ฒด์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ธ”๋ž™๋ฐ•์Šค ์˜์—ญ์œผ๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ ‘๊ทผํ•˜๊ณ  ์ˆ˜์ •ํ•  ์ผ์ด ์žˆ๋Š” ํด๋ž˜์Šค๋Š” ์•„๋‹ˆ๋‹ค.

์ฆ‰, HttpWebHandlerAdapter ๋Š” HTTP ์š”์ฒญ์„ ๋ฐ›์•„ WebHandler(DispatcherHandler) ๋กœ ์ „๋‹ฌํ•˜๊ณ  HTTP ์‘๋‹ต์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ด ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์–ธ๊ธ‰์ด ํ•„์š”ํ•œ ์ด์œ ๋Š” DispatcherHandler๋กœ ์ „๋‹ฌ ๋˜๊ธฐ ์ „ FilteringWebHandler๋ฅผ ํ†ตํ•ด WebFilter ํด๋ž˜์Šค๋“ค์˜ ํ•„ํ„ฐ ์ฒด์ธ์ด ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

<java />
public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHandler { ... @Override public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { if (this.forwardedHeaderTransformer != null) { try { request = this.forwardedHeaderTransformer.apply(request); } catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to apply forwarded headers to " + formatRequest(request), ex); } response.setStatusCode(HttpStatus.BAD_REQUEST); return response.setComplete(); } } ServerWebExchange exchange = createExchange(request, response); LogFormatUtils.traceDebug(logger, traceOn -> exchange.getLogPrefix() + formatRequest(exchange.getRequest()) + (traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : "")); return getDelegate().handle(exchange) .doOnSuccess(aVoid -> logResponse(exchange)) .onErrorResume(ex -> handleUnresolvedError(exchange, ex)) .then(Mono.defer(response::setComplete)); } ... }

์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด getDelegate().handle(exchange) ๋ถ€๋ถ„์—์„œ WebHandler ์˜ ๊ตฌํ˜„์ฒด์˜ handle ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

WebHandler์˜ ๊ตฌํ˜„์ฒด๋กœ๋Š” DispatcherHandler ์ด์™ธ์—๋„ FilteringWebHandler, ExceptionHandlingWebHandler ๋“ฑ ์—ฌ๋Ÿฌ ๊ตฌํ˜„์ฒด๊ฐ€ ์กด์žฌํ•œ๋‹ค.

 

์•„๋ž˜ ์ฝ”๋“œ๋Š” ํ•„ํ„ฐ์ฒด์ธ์˜ ํ•„ํ„ฐ ์‹คํ–‰ ํ•จ์ˆ˜์ธ๋ฐ, ํ•„ํ„ฐ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๋” ์ด์ƒ ์‹คํ–‰ํ•  ํ•„ํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ๋‹ค๋ฅธ webHandler์˜ handler ๋ฉ”์„œ๋“œ๋กœ ์œ„์ž„ํ•ด์ฃผ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

<java />
public Mono<Void> filter(ServerWebExchange exchange) { return Mono.defer(() -> this.currentFilter != null && this.chain != null ? invokeFilter(this.currentFilter, this.chain, exchange) : this.handler.handle(exchange)); }

๋งŒ์•ฝ ์„œ๋ฒ„์— ์š”์ฒญ ์ „์— ์ถ”๊ฐ€ ๋กœ์ง์„ ๋ง๋ถ™์ด๊ณ  ์‹ถ๋‹ค๋ฉด WebFilter ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ตฌํ˜„์ฒด๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

 

 

์ฐธ๊ณ 

https://docs.spring.io/spring-framework/reference/web/webflux/dispatcher-handler.html

https://blog.naver.com/PostView.naver?blogId=gngh0101&logNo=221538537388