songining
article thumbnail

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

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

 

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

 

 

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

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

 

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

  • handlerMapping : ์š”์ฒญ์„ ์–ด๋–ค controller๊ฐ€ ์ฒ˜๋ฆฌํ•  ์ง€ ๊ฒฐ์ •
  • handlerAdapter : HandlerMapping ์œผ๋กœ ์„ ํƒ๋œ controller ๋˜๋Š” router ๋ฅผ ํ˜ธ์ถœ
  • handlerResultHandler : ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‘๋‹ต
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) ์„ ์ฐพ์•„ ์ฃผ์ž…ํ•ด์ค€๋‹ค.

 

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

 

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

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

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

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

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 ๋ฉ”์„œ๋“œ๋กœ ์œ„์ž„ํ•ด์ฃผ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

	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