[Spring] Web on Reactive Stack(๋ฐ˜์‘ํ˜• ์Šคํƒ์˜ ์›น)

2023. 2. 26. 20:46ยทFramework/spring

1. Spring WebFlux

์Šคํ”„๋ง ํ”„๋ ˆ์ž„ ์›Œํฌ์— ํฌํ•จ ๋œ ์›๋ž˜ ์›น ํ”„๋ ˆ์ž„ ์›Œํฌ ์ธ ์Šคํ”„๋ง ์›น MVC๋Š” ์„œ๋ธ”๋ฆฟ API ๋ฐ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ ์šฉ์œผ๋กœ ํŠน๋ณ„ํžˆ ์ œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ์Šคํƒ ์›น ํ”„๋ ˆ์ž„์›Œํฌ์ธ Spring WebFlux๋Š” ๋ฒ„์ „ 5.0 ํ›„๋ฐ˜์— ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์™„์ „ํžˆ ์ฐจ๋‹จ๋˜์ง€ ์•Š๊ณ  ๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ ์—ญ ์••๋ ฅ์„ ์ง€์› ํ•˜๋ฉฐ Netty, Undertow ๋ฐ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ์™€ ๊ฐ™์€ ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

๋‘ ์›น ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ์†Œ์Šค ๋ชจ๋“ˆ (spring-webmvc ๋ฐ spring-webflux)์˜ ์ด๋ฆ„์„ ๋ฏธ๋Ÿฌ๋งํ•˜๊ณ  Spring ํ”„๋ ˆ์ž„ ์›Œํฌ์—์„œ ๋‚˜๋ž€ํžˆ ๊ณต์กดํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ชจ๋“ˆ์€ ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ํ•˜๋‚˜ ๋˜๋Š” ๋‹ค๋ฅธ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๋‘˜ ๋‹ค ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (์˜ˆ : ๋ฐ˜์‘ํ˜• WebClient๊ฐ€์žˆ๋Š” Spring MVC ์ปจํŠธ๋กค๋Ÿฌ).

1.1. Overview

Why was Spring WebFlux created?

ํ•ด๋‹ต์˜ ์ผ๋ถ€๋Š” ์ ์€ ์ˆ˜์˜ ์Šค๋ ˆ๋“œ๋กœ ๋™์‹œ์„ฑ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋” ์ ์€ ํ•˜๋“œ์›จ์–ด ๋ฆฌ์†Œ์Šค๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๋น„์ฐจ๋‹จ ์›น ์Šคํƒ์˜ ํ•„์š”์„ฑ์ž…๋‹ˆ๋‹ค. ์„œ๋ธ”๋ฆฟ ๋น„์ฐจ๋‹จ I/O๋Š” ๊ณ„์•ฝ์ด ๋™๊ธฐ์‹(ํ•„ํ„ฐ, ์„œ๋ธ”๋ฆฟ) ๋˜๋Š” ์ฐจ๋‹จ(getParameter, getPart)์ธ ์„œ๋ธ”๋ฆฟ API์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์—์„œ ๋ฉ€์–ด์ง‘๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์ƒˆ๋กœ์šด ๊ณตํ†ต API๊ฐ€ ๋ชจ๋“  ๋น„์ฐจ๋‹จ ๋Ÿฐํƒ€์ž„์—์„œ ๊ธฐ์ดˆ ์—ญํ• ์„ ํ•˜๊ฒŒ ๋œ ๋™๊ธฐ์˜€์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋น„๋™๊ธฐ, ๋น„ ์ฐจ๋‹จ ๊ณต๊ฐ„์— ์ž˜ ์„ค์ •๋œ ์„œ๋ฒ„ (์˜ˆ : Netty) ๋•Œ๋ฌธ์— ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋Œ€๋‹ต์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์€ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ž…๋‹ˆ๋‹ค. Java 5์—์„œ ์ฃผ์„์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๊ธฐํšŒ(์˜ˆ: ์ฃผ์„์ด ์ถ”๊ฐ€๋œ REST ์ปจํŠธ๋กค๋Ÿฌ ๋˜๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ)๊ฐ€ ์ƒ์„ฑ๋จ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Java 8์— ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์ถ”๊ฐ€ํ•˜๋ฉด Java์—์„œ ๊ธฐ๋Šฅ API์— ๋Œ€ํ•œ ๊ธฐํšŒ๊ฐ€ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋น„๋™๊ธฐ ๋…ผ๋ฆฌ์˜ ์„ ์–ธ์  ๊ตฌ์„ฑ์„ ํ—ˆ์šฉํ•˜๋Š” ๋น„์ฐจ๋‹จ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐ ์—ฐ์† ์Šคํƒ€์ผ API(CompletableFuture ๋ฐ ReactiveX์—์„œ ๋Œ€์ค‘ํ™”๋จ)์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ ์ˆ˜์ค€์—์„œ Java 8์„ ํ†ตํ•ด Spring WebFlux๋Š” ์ฃผ์„์ด ๋‹ฌ๋ฆฐ ์ปจํŠธ๋กค๋Ÿฌ์™€ ํ•จ๊ป˜ ๊ธฐ๋Šฅ์ ์ธ ์›น ์—”๋“œ ํฌ์ธํŠธ๋ฅผ ์ œ๊ณต ํ•  ์ˆ˜์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

1.1.1. Define “Reactive”

์šฐ๋ฆฌ๋Š” "๋น„ ์ฐจ๋‹จ"๊ณผ "๊ธฐ๋Šฅ์ "์— ๋Œ€ํ•ด ๋‹ค๋ฃจ์—ˆ์ง€๋งŒ ๋ฐ˜์‘ ์„ฑ์€ ๋ฌด์—‡์„ ์˜๋ฏธํ•ฉ๋‹ˆ๊นŒ?

"๋ฐ˜์‘ํ˜•"์ด๋ผ๋Š” ์šฉ์–ด๋Š” I/O ์ด๋ฒคํŠธ์— ๋ฐ˜์‘ํ•˜๋Š” ๋„คํŠธ์›Œํฌ ๊ตฌ์„ฑ ์š”์†Œ, ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ์— ๋ฐ˜์‘ํ•˜๋Š” UI ์ปจํŠธ๋กค๋Ÿฌ ๋“ฑ ๋ณ€ํ™”์— ๋ฐ˜์‘ํ•˜๋Š” ๊ฒƒ์„ ์ค‘์‹ฌ์œผ๋กœ ๊ตฌ์ถ•๋œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ์˜๋ฏธ์—์„œ ๋น„์ฐจ๋‹จ์€ ์ฐจ๋‹จ๋˜๋Š” ๋Œ€์‹  ์ด์ œ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋  ๋•Œ ์•Œ๋ฆผ์— ๋ฐ˜์‘ํ•˜๋Š” ๋ชจ๋“œ์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜์‘์ ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ Spring ํŒ€์—์„œ "๋ฐ˜์‘์„ฑ"๊ณผ ์—ฐ๊ด€์‹œํ‚ค๋Š” ๋˜ ๋‹ค๋ฅธ ์ค‘์š”ํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ์žˆ๋Š”๋ฐ, ๊ทธ๊ฒƒ์€ ๋น„ ์ฐจ๋‹จ ๋ฐฐ์••์ž…๋‹ˆ๋‹ค. ๋™๊ธฐ์‹ ๋ช…๋ นํ˜• ์ฝ”๋“œ์—์„œ ํ˜ธ์ถœ ์ฐจ๋‹จ์€ ํ˜ธ์ถœ์ž๊ฐ€ ๋Œ€๊ธฐํ•˜๋„๋ก ํ•˜๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ํ˜•ํƒœ์˜ ์—ญ ์••๋ ฅ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋น„์ฐจ๋‹จ ์ฝ”๋“œ์—์„œ๋Š” ๋น ๋ฅธ ์ƒ์‚ฐ์ž๊ฐ€ ๋Œ€์ƒ์„ ์••๋„ํ•˜์ง€ ์•Š๋„๋ก ์ด๋ฒคํŠธ ์†๋„๋ฅผ ์ œ์–ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ์€ ๋น„๋™๊ธฐ ๊ตฌ์„ฑ ์š”์†Œ์™€ ์—ญ ์••๋ ฅ ๊ฐ„์˜ ์ƒํ˜ธ ์ž‘์šฉ์„ ์ •์˜ํ•˜๋Š” ์ž‘์€ ์‚ฌ์–‘ (Java 9์—์„œ๋„ ์ฑ„ํƒ ๋จ)์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ฒŒ์‹œ์ž ์—ญํ• ์„ ํ•˜๋Š” ๋ฐ์ดํ„ฐ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋Š” HTTP ์„œ๋ฒ„(๊ตฌ๋…์ž ์—ญํ• )๊ฐ€ ์‘๋‹ต์— ์“ธ ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ์˜ ์ฃผ์š” ๋ชฉ์ ์€ ๊ตฌ๋…์ž๊ฐ€ ๊ฒŒ์‹œ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์†๋„ ๋˜๋Š” ์†๋„๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Common question: what if a publisher cannot slow down?

๋ฐ˜์‘์  ์ŠคํŠธ๋ฆผ์˜ ๋ชฉ์ ์€ ๋ฉ”์ปค๋‹ˆ์ฆ˜๊ณผ ๊ฒฝ๊ณ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฒŒ์‹œ์ž๊ฐ€ ์†๋„๋ฅผ ๋Šฆ์ถœ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ ๋ฒ„ํผ๋ง, ์‚ญ์ œ ๋˜๋Š” ์‹คํŒจ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
 

1.1.2. Reactive API

๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ์€ ์ƒํ˜ธ ์šด์šฉ์„ฑ์— ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐ ์ธํ”„๋ผ ๊ตฌ์„ฑ ์š”์†Œ์— ๊ด€์‹ฌ์ด ์žˆ์ง€๋งŒ ๋„ˆ๋ฌด ๋‚ฎ์€ ์ˆ˜์ค€์ด๊ธฐ ๋•Œ๋ฌธ์— ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ API๋กœ๋Š” ๋œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋น„๋™๊ธฐ ๋กœ์ง์„ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด ๋” ๋†’์€ ์ˆ˜์ค€์˜ ํ’๋ถ€ํ•œ ๊ธฐ๋Šฅ์˜ API๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค — Java 8 Stream API์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ ์ปฌ๋ ‰์…˜์—๋งŒ ํ•ด๋‹น๋˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ˜์‘ํ˜• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ์—ญํ• ์ž…๋‹ˆ๋‹ค.

Reactor๋Š” Spring WebFlux์— ๋Œ€ํ•ด ์„ ํƒ๋˜๋Š” ๋ฐ˜์‘์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์—ฐ์‚ฐ์ž์˜ ReactiveX ์–ดํœ˜์™€ ์ผ์น˜ํ•˜๋Š” ํ’๋ถ€ํ•œ ์—ฐ์‚ฐ์ž ์ง‘ํ•ฉ์„ ํ†ตํ•ด 0..1(๋ชจ๋…ธ) ๋ฐ 0..N(ํ”Œ๋Ÿญ์Šค)์˜ ๋ฐ์ดํ„ฐ ์‹œํ€€์Šค์—์„œ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๋Š” Mono ๋ฐ Flux API ์œ ํ˜•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Reactor๋Š” ๋ฐ˜์‘์„ฑ ์ŠคํŠธ๋ฆผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฏ€๋กœ ๋ชจ๋“  ์ž‘์—…์ž๊ฐ€ ๋น„์ฐจ๋‹จ ๋ฐฐ์••์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Reactor๋Š” ์„œ๋ฒ„ ์ธก Java์— ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ Spring๊ณผ์˜ ๊ธด๋ฐ€ํ•œ ํ˜‘๋ ฅ์„ ํ†ตํ•ด ๊ฐœ๋ฐœ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

WebFlux๋Š” ํ•ต์‹ฌ ์ข…์†์„ฑ์œผ๋กœ Reactor๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ ๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ์„ ํ†ตํ•ด ๋‹ค๋ฅธ ๋ฐ˜์‘ํ˜• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์ƒํ˜ธ ์šด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ WebFlux API๋Š” ์ผ๋ฐ˜ ๊ฒŒ์‹œ์ž๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„๋“ค์ด๊ณ , ๋‚ด๋ถ€์ ์œผ๋กœ Reactor ์œ ํ˜•์— ๋งž๊ฒŒ ์กฐ์ •ํ•˜๊ณ , ์ด๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , Flux ๋˜๋Š” Mono๋ฅผ ์ถœ๋ ฅ ์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋ชจ๋“  ๊ฒŒ์‹œ์ž๋ฅผ ์ž…๋ ฅ์œผ๋กœ ์ „๋‹ฌํ•˜๊ณ  ์ถœ๋ ฅ์— ์ž‘์—…์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹ค๋ฅธ ๋ฐ˜์‘ํ˜• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ถœ๋ ฅ์„ ์กฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•  ๋•Œ๋งˆ๋‹ค(์˜ˆ: ์ฃผ์„์ด ๋‹ฌ๋ฆฐ ์ปจํŠธ๋กค๋Ÿฌ) WebFlux๋Š” RxJava ๋˜๋Š” ๋‹ค๋ฅธ ๋ฐ˜์‘ํ˜• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์‚ฌ์šฉ์— ํˆฌ๋ช…ํ•˜๊ฒŒ ์ ์‘ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ฐ˜์‘ํ˜• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

 

๋ฐ˜์‘ํ˜• API ์™ธ์—๋„ WebFlux๋Š” Kotlin์˜ ์ฝ”๋ฃจํ‹ด API์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด ๋ณด๋‹ค ๋ช…๋ น์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์Šคํƒ€์ผ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ Kotlin ์ฝ”๋“œ ์ƒ˜ํ”Œ์€ ์ฝ”๋ฃจํ‹ด API์™€ ํ•จ๊ป˜ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

 

1.1.3. Programming Models

์Šคํ”„๋ง ์›น ๋ชจ๋“ˆ์—๋Š” HTTP ์ถ”์ƒํ™”, ์ง€์›๋˜๋Š” ์„œ๋ฒ„์šฉ ๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ ์–ด๋Œ‘ํ„ฐ, ์ฝ”๋ฑ ๋ฐ ์„œ๋ธ”๋ฆฟ API์™€ ๋น„์Šทํ•˜์ง€๋งŒ ๋น„์ฐจ๋‹จ ๊ณ„์•ฝ์ด ์žˆ๋Š” ํ•ต์‹ฌ ์›น ํ•ธ๋“ค๋Ÿฌ API๋ฅผ ํฌํ•จํ•˜์—ฌ ์Šคํ”„๋ง ์›นํ”Œ๋Ÿญ์Šค์˜ ๊ธฐ์ดˆ๊ฐ€ ๋˜๋Š” ๋ฐ˜์‘ํ˜• ๊ธฐ๋ฐ˜์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ Spring WebFlux๋Š” ๋‘ ๊ฐ€์ง€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Annotated Controllers: Spring MVC์™€ ์ผ์น˜ํ•˜๋ฉฐ ์Šคํ”„๋ง ์›น ๋ชจ๋“ˆ์˜ ๋™์ผํ•œ ์ฃผ์„์„ ๊ธฐ๋ฐ˜์œผ๋กœํ•ฉ๋‹ˆ๋‹ค. Spring MVC ๋ฐ WebFlux ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋ชจ๋‘ ๋ฐ˜์‘ ํ˜• (Reactor ๋ฐ RxJava) ๋ฐ˜ํ™˜ ์œ ํ˜•์„ ์ง€์›ํ•˜๋ฏ€๋กœ ๊ฒฐ๊ณผ์ ์œผ๋กœ ๊ตฌ๋ถ„ํ•˜๊ธฐ๊ฐ€ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•œ ๊ฐ€์ง€ ์ฃผ๋ชฉํ• ๋งŒํ•œ ์ฐจ์ด์ ์€ WebFlux๊ฐ€ ๋ฐ˜์‘ํ˜• @RequestBody ์ธ์ˆ˜๋„ ์ง€์›ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • Functional Endpoints: Lambda ๊ธฐ๋ฐ˜, ๊ฒฝ๋Ÿ‰ ๋ฐ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ. ์ด๋ฅผ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ์š”์ฒญ์„ ๋ผ์šฐํŒ…ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ์ง‘ํ•ฉ์œผ๋กœ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฃผ์„์ด ๋‹ฌ๋ฆฐ ์ปจํŠธ๋กค๋Ÿฌ์™€์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด์ ์€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์š”์ฒญ ์ฒ˜๋ฆฌ๋ฅผ ๋‹ด๋‹นํ•˜๊ณ  ์ฃผ์„์„ ํ†ตํ•ด ์˜๋„๋ฅผ ์„ ์–ธํ•˜๊ณ  ์ฝœ๋ฐฑ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

1.1.4. Applicability

Spring MVC or WebFlux?

์ž์—ฐ์Šค๋Ÿฌ์šด ์งˆ๋ฌธ์ด์ง€๋งŒ ๋ถˆ๊ฑด์ „ํ•œ ์ด๋ถ„๋ฒ•์„ ์„ค์ •ํ•˜๋Š” ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ๋‘˜ ๋‹ค ํ•จ๊ป˜ ์ž‘๋™ํ•˜์—ฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์˜ต์…˜์˜ ๋ฒ”์œ„๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด ๋‘˜์€ ์„œ๋กœ์˜ ์—ฐ์†์„ฑ๊ณผ ์ผ๊ด€์„ฑ์„ ์œ„ํ•ด ์„ค๊ณ„๋˜์—ˆ์œผ๋ฉฐ ๋‚˜๋ž€ํžˆ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๊ฐ ์ธก๋ฉด์˜ ํ”ผ๋“œ๋ฐฑ์€ ์–‘์ธก ๋ชจ๋‘์—๊ฒŒ ์ด์ต์ด๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ๋‹ค์ด์–ด๊ทธ๋žจ์—์„œ๋Š” ๋‘ ๊ฐ€์ง€๊ฐ€ ์–ด๋–ป๊ฒŒ ๊ด€๋ จ๋˜์–ด ์žˆ๋Š”์ง€, ๊ณตํ†ต์ ์ด ๋ฌด์—‡์ธ์ง€, ๊ฐ๊ฐ์ด ๊ณ ์œ ํ•˜๊ฒŒ ์ง€์›ํ•˜๋Š” ๊ฒƒ์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

We suggest that you consider the following specific points:

  • ์ž˜ ์ž‘๋™ํ•˜๋Š” Spring MVC ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด์žˆ๋Š” ๊ฒฝ์šฐ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋ช…๋ นํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ, ์ดํ•ด ๋ฐ ๋””๋ฒ„๊ทธํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์—ญ์‚ฌ์ ์œผ๋กœ ๋Œ€๋ถ€๋ถ„์ด ์ฐจ๋‹จ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ตœ๋Œ€ํ•œ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฏธ ๋น„ ์ฐจ๋‹จ ์›น ์Šคํƒ์„ ๊ตฌ์ž…ํ•˜๊ณ ์žˆ๋Š” ๊ฒฝ์šฐ Spring WebFlux๋Š”์ด ๊ณต๊ฐ„์˜ ๋‹ค๋ฅธ ์›น ์Šคํƒ๊ณผ ๋™์ผํ•œ ์‹คํ–‰ ๋ชจ๋ธ ์ด์ ์„ ์ œ๊ณตํ•˜๋ฉฐ ์„œ๋ฒ„ (Netty, Tomcat, Jetty, Undertow ๋ฐ Servlet ์ปจํ…Œ์ด๋„ˆ), ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ ์„ ํƒ (์ฃผ์„์ด ๋‹ฌ๋ฆฐ ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ ๊ธฐ๋Šฅ ์›น ์—”๋“œ ํฌ์ธํŠธ) ๋ฐ ๋ฐ˜์‘ ํ˜• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ ํƒ (Reactor, RxJava, ๋˜๋Š” ๊ธฐํƒ€).
  • Java 8 ๋žŒ๋‹ค ๋˜๋Š” Kotlin๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ๋Ÿ‰์˜ ๊ธฐ๋Šฅ์  ์›น ํ”„๋ ˆ์ž„์›Œํฌ์— ๊ด€์‹ฌ์ด ์žˆ๋Š” ๊ฒฝ์šฐ Spring WebFlux ๊ธฐ๋Šฅ ์›น ์—”๋“œํฌ์ธํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋” ํฐ ํˆฌ๋ช…์„ฑ๊ณผ ์ œ์–ด์˜ ์ด์ ์„ ๋ˆ„๋ฆด ์ˆ˜ ์žˆ๋Š” ๋œ ๋ณต์žกํ•œ ์š”๊ตฌ ์‚ฌํ•ญ์ด ์žˆ๋Š” ์†Œ๊ทœ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋˜๋Š” ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค์—๋„ ์ข‹์€ ์„ ํƒ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜์—์„œ๋Š” Spring MVC ๋˜๋Š” Spring WebFlux ์ปจํŠธ๋กค๋Ÿฌ ๋˜๋Š” Spring WebFlux ๊ธฐ๋Šฅ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ˜ผํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ํ”„๋ ˆ์ž„์›Œํฌ ๋ชจ๋‘์—์„œ ๋™์ผํ•œ ์ฃผ์„ ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์„ ์ง€์›ํ•˜๋ฉด ์ง€์‹์„ ๋” ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๋™์‹œ์— ์˜ฌ๋ฐ”๋ฅธ ์ž‘์—…์— ์ ํ•ฉํ•œ ๋„๊ตฌ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ํ‰๊ฐ€ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ์ข…์†์„ฑ์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•  ์ฐจ๋‹จ ์ง€์†์„ฑ API (JPA, JDBC) ๋˜๋Š” ๋„คํŠธ์›Œํ‚น API๊ฐ€์žˆ๋Š” ๊ฒฝ์šฐ Spring MVC๋Š” ์ ์–ด๋„ ์ผ๋ฐ˜์ ์ธ ์•„ํ‚คํ…์ฒ˜์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์„ ํƒ์ž…๋‹ˆ๋‹ค. Reactor์™€ RxJava ๋ชจ๋‘์—์„œ ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ์ฐจ๋‹จ ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์€ ๊ธฐ์ˆ ์ ์œผ๋กœ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋น„์ฐจ๋‹จ ์›น ์Šคํƒ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
  • ์›๊ฒฉ ์„œ๋น„์Šค์— ๋Œ€ํ•œ ํ˜ธ์ถœ์ด์žˆ๋Š” Spring MVC ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด์žˆ๋Š” ๊ฒฝ์šฐ ๋ฐ˜์‘ํ˜• ์›น ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์‹ญ์‹œ์˜ค. ๋ฐ˜์‘ ํ˜• ์œ ํ˜• (๋ฆฌ์•กํ„ฐ, RxJava ๋˜๋Š” ๊ธฐํƒ€)์„ Spring MVC ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์—์„œ ์ง์ ‘ ๋ฐ˜ํ™˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ†ตํ™”๋‹น ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ๊ธธ๊ฑฐ๋‚˜ ํ†ตํ™” ๊ฐ„์˜ ์ƒํ˜ธ ์ข…์†์„ฑ์ด ํด์ˆ˜๋ก ์ด์ ์ด ๋” ์ปค์ง‘๋‹ˆ๋‹ค. Spring MVC ์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋‹ค๋ฅธ ๋ฐ˜์‘ ํ˜• ๊ตฌ์„ฑ ์š”์†Œ๋„ ํ˜ธ์ถœ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋Œ€๊ทœ๋ชจ ํŒ€์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋น„์ฐจ๋‹จ, ๊ธฐ๋Šฅ ๋ฐ ์„ ์–ธ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ์˜ ์ „ํ™˜์—์„œ ๊ฐ€ํŒŒ๋ฅธ ํ•™์Šต ๊ณก์„ ์„ ์—ผ๋‘์— ๋‘์‹ญ์‹œ์˜ค. ์ „์ฒด ์Šค์œ„์น˜ ์—†์ด ์‹œ์ž‘ํ•˜๋Š” ์‹ค์šฉ์ ์ธ ๋ฐฉ๋ฒ•์€ ๋ฐ˜์‘ํ˜• WebClient๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ์™ธ์—๋„ ์ž‘๊ฒŒ ์‹œ์ž‘ํ•˜์—ฌ ์ด์ ์„ ์ธก์ •ํ•˜์‹ญ์‹œ์˜ค. ์šฐ๋ฆฌ๋Š” ๊ด‘๋ฒ”์œ„ํ•œ ์‘์šฉ ๋ถ„์•ผ์—์„œ ์ด๋Ÿฌํ•œ ๋ณ€ํ™”๊ฐ€ ๋ถˆํ•„์š”ํ•˜๋‹ค๊ณ  ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ์ด์ ์„ ์ฐพ์•„์•ผ ํ• ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์œผ๋ฉด ๋จผ์ € ๋น„์ฐจ๋‹จ I/O์˜ ์ž‘๋™ ๋ฐฉ์‹(์˜ˆ: ๋‹จ์ผ ์Šค๋ ˆ๋“œ ๋…ธ๋“œ์˜ ๋™์‹œ์„ฑ.js ๋ฐ ๊ทธ ํšจ๊ณผ์— ๋Œ€ํ•ด ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

1.1.5. Servers

Spring WebFlux๋Š” Tomcat, Jetty, Servlet ์ปจํ…Œ์ด๋„ˆ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ Netty ๋ฐ Undertow์™€ ๊ฐ™์€ ๋น„ ์„œ๋ธ”๋ฆฟ ๋Ÿฐํƒ€์ž„์—์„œ๋„ ์ง€์›๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์„œ๋ฒ„๋Š” ๋‚ฎ์€ ์ˆ˜์ค€์˜ ๊ณตํ†ต API์— ๋งž๊ฒŒ ์กฐ์ •๋˜๋ฏ€๋กœ ์„œ๋ฒ„ ๊ฐ„์— ์ƒ์œ„ ์ˆ˜์ค€์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์„ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šคํ”„๋ง WebFlux์—๋Š” ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ค‘์ง€ํ•˜๋Š” ๊ธฐ๋ณธ ์ œ๊ณต ์ง€์›์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Spring ๊ตฌ์„ฑ ๋ฐ WebFlux ์ธํ”„๋ผ์—์„œ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ์กฐ๋ฆฝํ•˜๊ณ  ๋ช‡ ์ค„์˜ ์ฝ”๋“œ๋กœ ์‰ฝ๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ์—๋Š” ์ด๋Ÿฌํ•œ ๋‹จ๊ณ„๋ฅผ ์ž๋™ํ™”ํ•˜๋Š” WebFlux ์Šคํƒ€ํ„ฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์Šคํƒ€ํ„ฐ๋Š” Netty๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ Maven ๋˜๋Š” Gradle ์ข…์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜์—ฌ Tomcat, Jetty ๋˜๋Š” Undertow๋กœ ์‰ฝ๊ฒŒ ์ „ํ™˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Spring Boot๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Netty๋กœ ์„ค์ •๋˜๋Š”๋ฐ, ์ด๋Š” ๋น„๋™๊ธฐ์‹, ๋น„์ฐจ๋‹จ ๊ณต๊ฐ„์—์„œ ๋” ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋ฉฐ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Tomcat๊ณผ Jetty๋Š” Spring MVC ๋ฐ WebFlux์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‚ฌ์šฉ ๋ฐฉ์‹์€ ๋งค์šฐ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. Spring MVC๋Š” ์„œ๋ธ”๋ฆฟ ์ฐจ๋‹จ I / O์— ์˜์กดํ•˜๋ฉฐ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์„œ๋ธ”๋ฆฟ API๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง WebFlux๋Š” ์„œ๋ธ”๋ฆฟ ๋น„ ์ฐจ๋‹จ I / O์— ์˜์กดํ•˜๋ฉฐ ์ €์ˆ˜์ค€ ์–ด๋Œ‘ํ„ฐ ๋’ค์—์„œ ์„œ๋ธ”๋ฆฟ API๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋…ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Undertow์˜ ๊ฒฝ์šฐ Spring WebFlux๋Š” Servlet API์—†์ด ์ง์ ‘ Undertow API๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

1.1.6. Performance

์„ฑ๋Šฅ์—๋Š” ๋งŽ์€ ํŠน์„ฑ๊ณผ ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ๋ฐ ๋น„์ฐจ๋‹จํ˜•์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ๋” ๋น ๋ฅด๊ฒŒ ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ์›๊ฒฉ ํ˜ธ์ถœ์„ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ์™€ ๊ฐ™์ด ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ „๋ฐ˜์ ์œผ๋กœ ๋น„ ์ฐจ๋‹จ ๋ฐฉ์‹์œผ๋กœ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด ๋” ๋งŽ์€ ์ž‘์—…์ด ํ•„์š”ํ•˜๋ฉฐ ํ•„์š”ํ•œ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ์•ฝ๊ฐ„ ๋Š˜์–ด๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘์  ๋ฐ ๋น„์ฐจ๋‹จ์˜ ์ฃผ์š” ์˜ˆ์ƒ ์ด์ ์€ ์ž‘๊ณ  ๊ณ ์ •๋œ ์ˆ˜์˜ ์Šค๋ ˆ๋“œ์™€ ๋” ์ ์€ ๋ฉ”๋ชจ๋ฆฌ๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ๋” ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ๋ฐฉ์‹์œผ๋กœ ํ™•์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถ€ํ•˜ ์ƒํƒœ์—์„œ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ๋ณต์›๋ ฅ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฌํ•œ ์ด์ ์„ ๊ด€์ฐฐํ•˜๋ ค๋ฉด ์•ฝ๊ฐ„์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„(๋А๋ฆฌ๊ณ  ์˜ˆ์ธกํ•  ์ˆ˜ ์—†๋Š” ๋„คํŠธ์›Œํฌ I/O์˜ ํ˜ผํ•ฉ ํฌํ•จ)์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ˜์‘ ์Šคํƒ์ด ๊ฐ•์ ์„ ๋ณด์—ฌ์ฃผ๊ธฐ ์‹œ์ž‘ํ•˜๋Š” ๊ณณ์ด๋ฉฐ ๊ทธ ์ฐจ์ด๋Š” ๊ทน์  ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1.1.7. Concurrency Model

Spring MVC ๋ฐ Spring WebFlux๋Š” ๋ชจ๋‘ ์ฃผ์„์ด ๋‹ฌ๋ฆฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ง€์›ํ•˜์ง€๋งŒ ๋™์‹œ์„ฑ ๋ชจ๋ธ๊ณผ ์ฐจ๋‹จ ๋ฐ ์Šค๋ ˆ๋“œ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ๊ฐ€์ •์—๋Š” ์ค‘์š”ํ•œ ์ฐจ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

Spring MVC (๋ฐ ์ผ๋ฐ˜์ ์œผ๋กœ ์„œ๋ธ”๋ฆฟ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ)์—์„œ๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ํ˜„์žฌ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค (์˜ˆ : ์›๊ฒฉ ํ˜ธ์ถœ). ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋Š” ๋Œ€ํ˜• ์Šค๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ ์ฒ˜๋ฆฌ ์ค‘์— ์ž ์žฌ์  ์ฐจ๋‹จ์„ ํก์ˆ˜ํ•ฉ๋‹ˆ๋‹ค.

Spring WebFlux (๋ฐ ์ผ๋ฐ˜์ ์œผ๋กœ ๋น„ ์ฐจ๋‹จ ์„œ๋ฒ„)์—์„œ๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ์ฐจ๋‹จ๋˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋น„์ฐจ๋‹จ ์„œ๋ฒ„๋Š” ์ž‘์€ ๊ณ ์ • ํฌ๊ธฐ ์Šค๋ ˆ๋“œ ํ’€(์ด๋ฒคํŠธ ๋ฃจํ”„ ์ž‘์—…์ž)์„ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

"ํ™•์žฅํ•˜๊ธฐ"์™€ "์ ์€ ์ˆ˜์˜ ์Šค๋ ˆ๋“œ"๋Š” ๋ชจ์ˆœ์ ์œผ๋กœ ๋“ค๋ฆด ์ˆ˜ ์žˆ์ง€๋งŒ ํ˜„์žฌ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ๋Œ€์‹  ์ฝœ๋ฐฑ์— ์˜์กดํ•œ๋‹ค๋Š” ๊ฒƒ์€ ํก์ˆ˜ ํ•  ์ฐจ๋‹จ ํ˜ธ์ถœ์ด ์—†์œผ๋ฏ€๋กœ ์ถ”๊ฐ€ ์Šค๋ ˆ๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

Invoking a Blocking API

์ฐจ๋‹จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ? Reactor์™€ RxJava๋Š” ๋ชจ๋‘ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌ๋ฅผ ๊ณ„์†ํ•  ์ˆ˜ ์žˆ๋„๋ก publishOn ์—ฐ์‚ฐ์ž๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์‰ฌ์šด ํƒˆ์ถœ ํ•ด์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ API ์ฐจ๋‹จ์€ ์ด ๋™์‹œ์„ฑ ๋ชจ๋ธ์— ์ ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Mutable State

Reactor ๋ฐ RxJava์—์„œ๋Š” ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด ๋…ผ๋ฆฌ๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ„๊ฐœ์˜ ๋‹จ๊ณ„์—์„œ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” ๋ฐ˜์‘ํ˜• ํŒŒ์ดํ”„๋ผ์ธ์ด ํ˜•์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์˜ ์ฃผ์š” ์ด์ ์€ ํ•ด๋‹น ํŒŒ์ดํ”„๋ผ์ธ ๋‚ด์˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์ฝ”๋“œ๊ฐ€ ๋™์‹œ์— ํ˜ธ์ถœ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋ฅผ ๋ณดํ˜ธํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Threading Model

Spring WebFlux๋กœ ์‹คํ–‰๋˜๋Š” ์„œ๋ฒ„์—์„œ ์–ด๋–ค ์Šค๋ ˆ๋“œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

  • "๋ฐ”๋‹๋ผ"Spring WebFlux ์„œ๋ฒ„ (์˜ˆ : ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๋˜๋Š” ๊ธฐํƒ€ ์„ ํƒ์  ์ข…์†์„ฑ ์—†์Œ)์—์„œ ์„œ๋ฒ„์— ๋Œ€ํ•ด ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์˜ˆ์ƒํ•˜๊ณ  ์š”์ฒญ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ๋‹ค๋ฅธ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ (์ผ๋ฐ˜์ ์œผ๋กœ CPU ์ฝ”์–ด ์ˆ˜๋งŒํผ)๋ฅผ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋Š” ์„œ๋ธ”๋ฆฟ(์ฐจ๋‹จ) I/O ๋ฐ ์„œ๋ธ”๋ฆฟ 10.3(๋น„์ฐจ๋‹จ) I/O ์‚ฌ์šฉ์„ ๋ชจ๋‘ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ๋” ๋งŽ์€ ์Šค๋ ˆ๋“œ(์˜ˆ: Tomcat์˜ ๊ฒฝ์šฐ 1๊ฐœ)๋กœ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐ˜์‘ํ˜• WebClient๋Š” ์ด๋ฒคํŠธ ๋ฃจํ”„ ์Šคํƒ€์ผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด์™€ ๊ด€๋ จ๋œ ์ž‘๊ณ  ๊ณ ์ •๋œ ์ˆ˜์˜ ์ฒ˜๋ฆฌ ์Šค๋ ˆ๋“œ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: Reactor Netty ์ปค๋„ฅํ„ฐ๊ฐ€ ์žˆ๋Š” reactor-http-nio-). ๊ทธ๋Ÿฌ๋‚˜ Reactor Netty๊ฐ€ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๋ชจ๋‘์— ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ ๋‘ ๊ฐœ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด๋ฒคํŠธ ๋ฃจํ”„ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.
  • Reactor ๋ฐ RxJava๋Š” ์ฒ˜๋ฆฌ๋ฅผ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ ํ’€๋กœ ์ „ํ™˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” publishOn ์—ฐ์‚ฐ์ž์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์Šค์ผ€์ค„๋Ÿฌ๋ผ๋Š” ์Šค๋ ˆ๋“œ ํ’€ ์ถ”์ƒํ™”๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์Šค์ผ€์ค„๋Ÿฌ์—๋Š” ํŠน์ • ๋™์‹œ์„ฑ ์ „๋žต์„ ์ œ์•ˆํ•˜๋Š” ์ด๋ฆ„(์˜ˆ: "๋ณ‘๋ ฌ"(์Šค๋ ˆ๋“œ ์ˆ˜๊ฐ€ ์ œํ•œ๋œ CPU ๋ฐ”์ธ๋”ฉ ์ž‘์—…์˜ ๊ฒฝ์šฐ) ๋˜๋Š” "ํƒ„๋ ฅ์ "(์Šค๋ ˆ๋“œ ์ˆ˜๊ฐ€ ๋งŽ์€ I/O ๋ฐ”์ธ๋”ฉ๋œ ์ž‘์—…์˜ ๊ฒฝ์šฐ)์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ํ‘œ์‹œ๋˜๋ฉด ์ผ๋ถ€ ์ฝ”๋“œ๊ฐ€ ํŠน์ • ์Šค๋ ˆ๋“œ ํ’€ ์Šค์ผ€์ค„๋Ÿฌ ์ „๋žต์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐ ๊ธฐํƒ€ ํƒ€์‚ฌ ์ข…์†์„ฑ๋„ ์ž์ฒด ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Configuring

์Šคํ”„๋ง ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ์„œ๋ฒ„ ์‹œ์ž‘ ๋ฐ ์ค‘์ง€ ์— ๋Œ€ํ•œ ์ง€์›์„ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„์— ๋Œ€ํ•œ ์Šค๋ ˆ๋”ฉ ๋ชจ๋ธ์„ ๊ตฌ์„ฑํ•˜๋ ค๋ฉด ์„œ๋ฒ„๋ณ„ ๊ตฌ์„ฑ API๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, Spring Boot๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๊ฐ ์„œ๋ฒ„์— ๋Œ€ํ•œ Spring Boot ๊ตฌ์„ฑ ์˜ต์…˜์„ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์›น ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ง์ ‘ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ชจ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ•ด๋‹น ์„ค๋ช…์„œ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

1.2. Reactive Core

spring-web ๋ชจ๋“ˆ์—๋Š” ๋ฐ˜์‘ํ˜• ์›น ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ๋Œ€ํ•œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธ ์ง€์›์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์„œ๋ฒ„ ์š”์ฒญ ์ฒ˜๋ฆฌ์—๋Š” ๋‘ ๊ฐ€์ง€ ์ˆ˜์ค€์˜ ์ง€์›์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • HttpHandler: ๋น„์ฐจ๋‹จ I/O ๋ฐ ๋ฐ˜์‘์„ฑ ์ŠคํŠธ๋ฆผ ๋ฐฐ์••์„ ์‚ฌ์šฉํ•œ HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ๊ณ„์•ฝ๊ณผ Reactor Netty, Undertow, Tomcat, Jetty ๋ฐ ๋ชจ๋“  ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ์šฉ ์–ด๋Œ‘ํ„ฐ.
  • WebHandler API: ์š”์ฒญ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์•ฝ๊ฐ„ ๋” ๋†’์€ ์ˆ˜์ค€์˜ ๋ฒ”์šฉ ์›น API๋กœ, ๊ทธ ์œ„์— ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ์ปจํŠธ๋กค๋Ÿฌ ๋ฐ ๊ธฐ๋Šฅ ๋์ ๊ณผ ๊ฐ™์€ ๊ตฌ์ฒด์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์ด ๋นŒ๋“œ๋ฉ๋‹ˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ์ธก์˜ ๊ฒฝ์šฐ Reactor Netty, Reactive Jetty HttpClient ๋ฐ Apache HttpComponents์šฉ ์–ด๋Œ‘ํ„ฐ์™€ ํ•จ๊ป˜ ๋น„์ฐจ๋‹จ I/O ๋ฐ ๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ ์—ญ ์••๋ ฅ์œผ๋กœ HTTP ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ธฐ๋ณธ ClientHttpConnector ๊ณ„์•ฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ์‚ฌ์šฉ๋˜๋Š” ์ƒ์œ„ ์ˆ˜์ค€ WebClient๋Š” ์ด ๊ธฐ๋ณธ ๊ณ„์•ฝ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ๋ฐ ์„œ๋ฒ„์˜ ๊ฒฝ์šฐ HTTP ์š”์ฒญ ๋ฐ ์‘๋‹ต ์ฝ˜ํ…์ธ ์˜ ์ง๋ ฌํ™” ๋ฐ ์—ญ์ง๋ ฌํ™”๋ฅผ ์œ„ํ•œ ์ฝ”๋ฑ์ž…๋‹ˆ๋‹ค.

1.2.1. HttpHandler

HttpHandler๋Š” ์š”์ฒญ ๋ฐ ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋‹จ์ผ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ๊ณ„์•ฝ์ž…๋‹ˆ๋‹ค. ์˜๋„์ ์œผ๋กœ ์ตœ์†Œํ•œ์ด๋ฉฐ, ์ฃผ์š” ๋ชฉ์ ์€ ๋‹ค๋ฅธ HTTP ์„œ๋ฒ„ API์— ๋Œ€ํ•œ ์ตœ์†Œํ•œ์˜ ์ถ”์ƒํ™”์ž…๋‹ˆ๋‹ค.

๋‹ค์Œ ํ‘œ์—์„œ๋Š” ์ง€์›๋˜๋Š” ์„œ๋ฒ„ API์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„ ์ด๋ฆ„์„œ๋ฒ„ API ์‚ฌ์šฉ๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ ์ง€์›
Netty Netty API Reactor Netty
Undertow Undertow API spring-web: Undertow to Reactive Streams bridge
Tomcat Servlet non-blocking I/O; Tomcat API to read and write ByteBuffers vs byte[] spring-web: Servlet non-blocking I/O to Reactive Streams bridge
Jetty Servlet non-blocking I/O; Jetty API to write ByteBuffers vs byte[] spring-web: Servlet non-blocking I/O to Reactive Streams bridge
Servlet container Servlet non-blocking I/O spring-web: Servlet non-blocking I/O to Reactive Streams bridge

๋‹ค์Œ ํ‘œ์—์„œ๋Š” ์„œ๋ฒ„ ์ข…์†์„ฑ์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค(์ง€์›๋˜๋Š” ๋ฒ„์ „๋„ ์ฐธ์กฐ).


Server name Group id Artifact name
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
val handler: HttpHandler = ...
val adapter = ReactorHttpHandlerAdapter(handler)
HttpServer.create().host(host).port(port).handle(adapter).bindNow()
 

Undertow

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

Tomcat

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

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 Container

์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ์— WAR๋กœ ๋ฐฐํฌํ•˜๋ ค๋ฉด WAR์—์„œ AbstractReactiveWebInitializer๋ฅผ ํ™•์žฅํ•˜๊ณ  ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ServletHttpHandlerAdapter๋กœ HttpHandler๋ฅผ ๋ž˜ํ•‘ํ•˜๊ณ  ์„œ๋ธ”๋ฆฟ์œผ๋กœ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

1.2.2. WebHandler API

org.springframework.web.server ํŒจํ‚ค์ง€๋Š” HttpHandler ๊ณ„์•ฝ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์—ฌ๋Ÿฌ WebExceptionHandler, ์—ฌ๋Ÿฌ WebFilter ๋ฐ ๋‹จ์ผ WebHandler ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ฒด์ธ์„ ํ†ตํ•ด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฒ”์šฉ ์›น API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ฒด์ธ์€ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ž๋™ ๊ฐ์ง€๋˜๋Š” Spring ApplicationContext๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฑฐ๋‚˜ ๋นŒ๋”์— ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋“ฑ๋กํ•˜์—ฌ WebHttpHandlerBuilder์™€ ํ•จ๊ป˜ ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

HttpHandler๋Š” ๋‹ค์–‘ํ•œ HTTP ์„œ๋ฒ„์˜ ์‚ฌ์šฉ์„ ์ถ”์ƒํ™”ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋ชฉํ‘œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ WebHandler API๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์›น ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ด‘๋ฒ”์œ„ํ•œ ๊ธฐ๋Šฅ ์ง‘ํ•ฉ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค.

  • User session with attributes.
  • Request attributes.
  • Resolved Locale or Principal for the request.
  • Access to parsed and cached form data.
  • Abstractions for multipart data.
  • and more..

Special bean types

์•„๋ž˜ ํ‘œ์—๋Š” WebHttpHandlerBuilder๊ฐ€ Spring ApplicationContext์—์„œ ์ž๋™ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๊ฑฐ๋‚˜ ์ง์ ‘ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋‚˜์—ด๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.


Bean name Bean type Count Description
<any> WebExceptionHandler 0..N WebFilter ์ธ์Šคํ„ด์Šค ์ฒด์ธ๊ณผ ๋Œ€์ƒ ์›น ์ฒ˜๋ฆฌ๊ธฐ์˜ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์˜ˆ์™ธ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
<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 exposes the following method for accessing form data:

suspend fun getFormData(): MultiValueMap<String, String>
 

DefaultServerWebExchange๋Š” ๊ตฌ์„ฑ๋œ HttpMessageReader๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์–‘์‹ ๋ฐ์ดํ„ฐ(application/x-www-form-urlencoded)๋ฅผ MultiValueMap์œผ๋กœ ๊ตฌ๋ฌธ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ, FormHttpMessageReader๋Š” ServerCodecConfigurer Bean์—์„œ ์‚ฌ์šฉํ•˜๋„๋ก ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค(์›น ํ•ธ๋“ค๋Ÿฌ API ์ฐธ์กฐ).

Multipart Data

See equivalent in the Servlet stack

ServerWebExchange exposes the following method for accessing multipart data:

suspend fun getMultipartData(): MultiValueMap<String, Part>
 

DefaultServerWebExchange๋Š” ๊ตฌ์„ฑ๋œ HttpMessageReader<MultiValueMap<String, Part>>๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ multipart/form data, multipart/mixed ๋ฐ multipart/related ์ฝ˜ํ…์ธ ๋ฅผ MultiValueMap์œผ๋กœ ๊ตฌ๋ฌธ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด ๋ฉ”์„œ๋“œ๋Š” ํƒ€์‚ฌ ์ข…์†์„ฑ์ด ์—†๋Š” DefaultPartHttpMessageReader์ž…๋‹ˆ๋‹ค. ๋˜๋Š” Synchronosss NIO Multipart ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” SynchronossPartHttpMessageReader๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ServerCodecConfigurer Bean์„ ํ†ตํ•ด ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค(์›น ํ•ธ๋“ค๋Ÿฌ API ์ฐธ์กฐ).

์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋ฉ€ํ‹ฐํŒŒํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๋ ค๋ฉด @RequestPart ๋Œ€์‹  PartEventHttpMessageReader์—์„œ ๋ฐ˜ํ™˜๋œ Flux<PartEvent>๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋Š” ์ด๋ฆ„๋ณ„๋กœ ๊ฐœ๋ณ„ ํŒŒํŠธ์— ๋Œ€ํ•œ ๋งต๊ณผ ๊ฐ™์€ ์•ก์„ธ์Šค๋ฅผ ์˜๋ฏธํ•˜๋ฏ€๋กœ ๋ฉ€ํ‹ฐํŒŒํŠธ ๋ฐ์ดํ„ฐ ์ „์ฒด๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด, @RequestBody ์‚ฌ์šฉํ•˜๋ฉด MultiValueMap์— ์ˆ˜์ง‘ํ•˜์ง€ ์•Š๊ณ  Flux<PartEvent>๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ๋””์ฝ”๋”ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


Forwarded Headers

See equivalent in the Servlet stack

์š”์ฒญ์ด ํ”„๋ก์‹œ(์˜ˆ: ๋ถ€ํ•˜ ๋ถ„์‚ฐ ์žฅ์น˜)๋ฅผ ํ†ต๊ณผํ•˜๋ฉด ํ˜ธ์ŠคํŠธ, ํฌํŠธ ๋ฐ ์ฒด๊ณ„๊ฐ€ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํด๋ผ์ด์–ธํŠธ ๊ด€์ ์—์„œ ์˜ฌ๋ฐ”๋ฅธ ํ˜ธ์ŠคํŠธ, ํฌํŠธ ๋ฐ ์ฒด๊ณ„๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋งํฌ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

RFC 7239๋Š” ํ”„๋ก์‹œ๊ฐ€ ์›๋ž˜ ์š”์ฒญ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ „๋‹ฌ๋œ HTTP ํ—ค๋”๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Proto, X-Forwarded-SSL ๋ฐ X-Forwarded-Prefix๋ฅผ ํฌํ•จํ•œ ๋‹ค๋ฅธ ๋น„ํ‘œ์ค€ ํ—ค๋”๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

ForwardedHeaderTransformer๋Š” ์ „๋‹ฌ๋œ ํ—ค๋”๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์š”์ฒญ์˜ ํ˜ธ์ŠคํŠธ, ํฌํŠธ ๋ฐ ์ฒด๊ณ„๋ฅผ ์ˆ˜์ •ํ•œ ๋‹ค์Œ ํ•ด๋‹น ํ—ค๋”๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ์ž…๋‹ˆ๋‹ค. forwardedHeaderTransformer ๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง„ Bean์œผ๋กœ ์„ ์–ธํ•˜๋ฉด ๊ฐ์ง€๋˜์–ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

์ „๋‹ฌ๋œ ํ—ค๋”์— ๋Œ€ํ•œ ๋ณด์•ˆ ๊ณ ๋ ค ์‚ฌํ•ญ์ด ์žˆ๋Š”๋ฐ, ์ด๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ํ—ค๋”๊ฐ€ ์˜๋„ํ•œ ๋Œ€๋กœ ํ”„๋ก์‹œ์— ์˜ํ•ด ์ถ”๊ฐ€๋˜์—ˆ๋Š”์ง€ ๋˜๋Š” ์•…์˜์ ์ธ ํด๋ผ์ด์–ธํŠธ์— ์˜ํ•ด ์ถ”๊ฐ€๋˜์—ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์™ธ๋ถ€์—์„œ ๋“ค์–ด์˜ค๋Š” ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ์ „๋‹ฌ๋œ ํŠธ๋ž˜ํ”ฝ์„ ์ œ๊ฑฐํ•˜๋„๋ก ์‹ ๋ขฐ ๊ฒฝ๊ณ„์˜ ํ”„๋ก์‹œ๋ฅผ ๊ตฌ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. removeOnly=true๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ForwardedHeaderTransformer๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, ์ด ๊ฒฝ์šฐ ํ—ค๋”๋Š” ์ œ๊ฑฐ๋˜์ง€๋งŒ ํ—ค๋”๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

5.1์—์„œ ForwardedHeaderFilter๋Š” ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š๊ณ  ForwardedHeaderTransformer๋กœ ๋Œ€์ฒด๋˜์—ˆ์œผ๋ฏ€๋กœ ๊ตํ™˜์ด ์ƒ์„ฑ๋˜๊ธฐ ์ „์— ์ „๋‹ฌ๋œ ํ—ค๋”๋ฅผ ๋” ์ผ์ฐ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ํ•„ํ„ฐ๊ฐ€ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์œผ๋ฉด ํ•„ํ„ฐ ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐ๋˜๊ณ  ๋Œ€์‹  ForwardedHeaderTransformer๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

 

 

1.2.3. Filters

See equivalent in the Servlet stack

์›น ์ฒ˜๋ฆฌ๊ธฐ API์—์„œ WebFilter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•„ํ„ฐ์˜ ๋‚˜๋จธ์ง€ ์ฒ˜๋ฆฌ ์ฒด์ธ๊ณผ ๋Œ€์ƒ ์›น ์ฒ˜๋ฆฌ๊ธฐ ์•ž๋’ค์— ๊ฐ€๋กœ์ฑ„๊ธฐ ์Šคํƒ€์ผ ๋…ผ๋ฆฌ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. WebFlux ๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•  ๋•Œ WebFilter๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์€ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ์„ ์–ธํ•˜๊ณ  (์„ ํƒ์ ์œผ๋กœ) ๋นˆ ์„ ์–ธ์— @Order ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ Ordered๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์šฐ์„  ์ˆœ์œ„๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ๋งŒ ํผ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

 

CORS

See equivalent in the Servlet stack

์Šคํ”„๋ง WebFlux๋Š” ์ปจํŠธ๋กค๋Ÿฌ์˜ ์ฃผ์„์„ ํ†ตํ•ด CORS ๊ตฌ์„ฑ์— ๋Œ€ํ•œ ์„ธ๋ถ„ํ™”๋œ ์ง€์›์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์Šคํ”„๋ง ๋ณด์•ˆ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์Šคํ”„๋ง ๋ณด์•ˆ์˜ ํ•„ํ„ฐ ์ฒด์ธ๋ณด๋‹ค ๋จผ์ € ์ฃผ๋ฌธํ•ด์•ผํ•˜๋Š” ๋‚ด์žฅ CorsFilter์— ์˜์กดํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ CORS ๋ฐ CORS ์›น ํ•„ํ„ฐ ์„น์…˜์„ ์ฐธ์กฐํ•˜์„ธ์š”.

1.2.4. Exceptions

See equivalent in the Servlet stack

์›น ์ฒ˜๋ฆฌ๊ธฐ API์—์„œ WebExceptionHandler๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ WebFilter ์ธ์Šคํ„ด์Šค ๋ฐ ๋Œ€์ƒ ์›น ์ฒ˜๋ฆฌ๊ธฐ ์ฒด์ธ์˜ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. WebFlux Config๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ WebExceptionHandler๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์€ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ์„ ์–ธํ•˜๊ณ  (์„ ํƒ์ ์œผ๋กœ) Bean ์„ ์–ธ์— @Order ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ Ordered๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์šฐ์„  ์ˆœ์œ„๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ๋งŒ ํผ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

 

The following table describes the available WebExceptionHandler implementations:

Exception Handler Description
ResponseStatusExceptionHandler ์˜ˆ์™ธ์˜ HTTP ์ƒํƒœ ์ฝ”๋“œ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ์„ค์ •ํ•˜์—ฌ ResponseStatusException ํ˜•์‹์˜ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
WebFluxResponseStatusExceptionHandler ์˜ˆ์™ธ์— ๋Œ€ํ•œ @ResponseStatus ์ฃผ์„์˜ HTTP ์ƒํƒœ ์ฝ”๋“œ๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ResponseStatusExceptionHandler์˜ ํ™•์žฅ์ž…๋‹ˆ๋‹ค.
์ด ํ•ธ๋“ค๋Ÿฌ๋Š” WebFlux ๊ตฌ์„ฑ์—์„œ ์„ ์–ธ๋ฉ๋‹ˆ๋‹ค.

1.2.5. Codecs

See equivalent in the Servlet stack

์Šคํ”„๋ง ์›น ๋ฐ ์Šคํ”„๋ง ์ฝ”์–ด ๋ชจ๋“ˆ์€ Reactive Streams ์—ญ ์••๋ ฅ์ด ์žˆ๋Š” ๋น„์ฐจ๋‹จ I/O๋ฅผ ํ†ตํ•ด ์ƒ์œ„ ์ˆ˜์ค€ ๊ฐœ์ฒด์™€ ๋ฐ”์ดํŠธ ์ฝ˜ํ…์ธ ๋ฅผ ์ง๋ ฌํ™” ๋ฐ ์—ญ์ง๋ ฌํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์ด ์ง€์›์— ๋Œ€ํ•œ ์„ค๋ช…์ž…๋‹ˆ๋‹ค.

  • ์ธ์ฝ”๋” ๋ฐ ๋””์ฝ”๋”๋Š” HTTP์™€ ๋…๋ฆฝ์ ์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ธ์ฝ”๋”ฉ ๋ฐ ๋””์ฝ”๋”ฉํ•˜๋Š” ํ•˜์œ„ ์ˆ˜์ค€ ๊ณ„์•ฝ์ž…๋‹ˆ๋‹ค.
  • HttpMessageReader ๋ฐ HttpMessageWriter๋Š” HTTP ๋ฉ”์‹œ์ง€ ์ฝ˜ํ…์ธ ๋ฅผ ์ธ์ฝ”๋”ฉ ๋ฐ ๋””์ฝ”๋”ฉํ•˜๊ธฐ ์œ„ํ•œ ๊ณ„์•ฝ์ž…๋‹ˆ๋‹ค.
  • ์ธ์ฝ”๋”๋Š” EncoderHttpMessageWriter๋กœ ๋ž˜ํ•‘ํ•˜์—ฌ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋””์ฝ”๋”๋Š” DecoderHttpMessageReader๋กœ ๋ž˜ํ•‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • DataBuffer๋Š” ๋‹ค์–‘ํ•œ ๋ฐ”์ดํŠธ ๋ฒ„ํผ ํ‘œํ˜„ (์˜ˆ : Netty ByteBuf, java.nio.ByteBuffer ๋“ฑ)์„ ์ถ”์ƒํ™”ํ•˜๋ฉฐ ๋ชจ๋“  ์ฝ”๋ฑ์ด ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์ฃผ์ œ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ "Spring Core"์„น์…˜์˜ ๋ฐ์ดํ„ฐ ๋ฒ„ํผ ๋ฐ ์ฝ”๋ฑ์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์Šคํ”„๋ง ์ฝ”์–ด ๋ชจ๋“ˆ์€ byte[], ByteBuffer, DataBuffer, ๋ฆฌ์†Œ์Šค ๋ฐ ๋ฌธ์ž์—ด ์ธ์ฝ”๋” ๋ฐ ๋””์ฝ”๋” ๊ตฌํ˜„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์Šคํ”„๋ง ์›น ๋ชจ๋“ˆ์€ ์–‘์‹ ๋ฐ์ดํ„ฐ, ๋ฉ€ํ‹ฐํŒŒํŠธ ์ฝ˜ํ…์ธ , ์„œ๋ฒ„ ์ „์†ก ์ด๋ฒคํŠธ ๋“ฑ์— ๋Œ€ํ•œ ์›น ์ „์šฉ HTTP ๋ฉ”์‹œ์ง€ ํŒ๋…๊ธฐ ๋ฐ ์ž‘์„ฑ๊ธฐ ๊ตฌํ˜„๊ณผ ํ•จ๊ป˜ Jackson JSON, Jackson Smile, JAXB2, ํ”„๋กœํ† ์ฝœ ๋ฒ„ํผ ๋ฐ ๊ธฐํƒ€ ์ธ์ฝ”๋” ๋ฐ ๋””์ฝ”๋”๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋ฐ ServerCodecConfigurer๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์‚ฌ์šฉํ•  ์ฝ”๋ฑ์„ ๊ตฌ์„ฑํ•˜๊ณ  ์‚ฌ์šฉ์ž ์ง€์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. HTTP ๋ฉ”์‹œ์ง€ ์ฝ”๋ฑ ๊ตฌ์„ฑ์— ๋Œ€ํ•œ ์„น์…˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

 

Jackson JSON

JSON and binary JSON (Smile) Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ์„ ๋•Œ ๋‘˜ ๋‹ค ์ง€์›๋ฉ๋‹ˆ๋‹ค.

The Jackson2Decoder works as follows:

  • ์žญ์Šจ์˜ ๋น„๋™๊ธฐ์‹, ๋น„์ฐจ๋‹จ ํŒŒ์„œ๋Š” ๋ฐ”์ดํŠธ ์ฒญํฌ ์ŠคํŠธ๋ฆผ์„ ๊ฐ๊ฐ JSON ๊ฐ์ฒด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” TokenBuffer๋กœ ์ง‘๊ณ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ๊ฐ TokenBuffer๋Š” ์žญ์Šจ์˜ ObjectMapper์— ์ „๋‹ฌ๋˜์–ด ๋” ๋†’์€ ์ˆ˜์ค€์˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • ๋‹จ์ผ ๊ฐ’ ๊ฒŒ์‹œ์ž(์˜ˆ: Mono)๋กœ ๋””์ฝ”๋”ฉํ•  ๋•Œ ํ•˜๋‚˜์˜ TokenBuffer๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹ค์ค‘ ๊ฐ’ ๊ฒŒ์‹œ์ž(์˜ˆ: Flux)๋กœ ๋””์ฝ”๋”ฉํ•  ๋•Œ ๊ฐ TokenBuffer๋Š” ์™„์ „ํžˆ ํ˜•์„ฑ๋œ ๊ฐœ์ฒด์— ๋Œ€ํ•ด ์ถฉ๋ถ„ํ•œ ๋ฐ”์ดํŠธ๊ฐ€ ์ˆ˜์‹ ๋˜๋Š” ์ฆ‰์‹œ ObjectMapper์— ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ์ž…๋ ฅ ์ฝ˜ํ…์ธ ๋Š” JSON ๋ฐฐ์—ด์ด๊ฑฐ๋‚˜ ์ค„๋กœ ๊ตฌ๋ถ„๋œ JSON ํ˜•์‹(์˜ˆ: NDJSON, JSON ์ค„ ๋˜๋Š” JSON ํ…์ŠคํŠธ ์‹œํ€€์Šค)์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

The Jackson2Encoder works as follows:

  • ๋‹จ์ผ ๊ฐ’ ๊ฒŒ์‹œ์ž(์˜ˆ: Mono)์˜ ๊ฒฝ์šฐ ObjectMapper๋ฅผ ํ†ตํ•ด ์ง๋ ฌํ™”ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • application/json์ด ์žˆ๋Š” ๋‹ค์ค‘ ๊ฐ’ ๊ฒŒ์‹œ์ž์˜ ๊ฒฝ์šฐ ๊ธฐ๋ณธ์ ์œผ๋กœ Flux#collectToList()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’์„ ์ˆ˜์ง‘ํ•œ ๋‹ค์Œ ๊ฒฐ๊ณผ ์ปฌ๋ ‰์…˜์„ ์ง๋ ฌํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜/x-ndjson ๋˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜/์ŠคํŠธ๋ฆผ+x-์žญ์Šจ-์Šค๋งˆ์ผ๊ณผ ๊ฐ™์€ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฏธ๋””์–ด ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋Š” ๋‹ค์ค‘๊ฐ’ ๊ฒŒ์‹œ์ž์˜ ๊ฒฝ์šฐ ์ค„๋กœ ๊ตฌ๋ถ„๋œ JSON ํ˜•์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ๊ฐ’์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ธ์ฝ”๋”ฉ, ์“ฐ๊ธฐ ๋ฐ ํ”Œ๋Ÿฌ์‹œํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฏธ๋””์–ด ์œ ํ˜•์ด ์ธ์ฝ”๋”์— ๋“ฑ๋ก๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • SSE์˜ ๊ฒฝ์šฐ Jackson2Encoder๊ฐ€ ์ด๋ฒคํŠธ๋ณ„๋กœ ํ˜ธ์ถœ๋˜๊ณ  ์ง€์—ฐ์—†์ด ์ „๋‹ฌ๋˜๋„๋ก ์ถœ๋ ฅ์ด ํ”Œ๋Ÿฌ์‹œ๋ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ Jackson2Encoder์™€ Jackson2Decoder๋Š” ๋ชจ๋‘ ๋ฌธ์ž์—ด ํ˜•์‹์˜ ์š”์†Œ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ๊ธฐ๋ณธ ๊ฐ€์ •์€ ๋ฌธ์ž์—ด ๋˜๋Š” ๋ฌธ์ž์—ด ์‹œํ€€์Šค๊ฐ€ CharSequenceEncoder์— ์˜ํ•ด ๋ Œ๋”๋ง๋  ์ง๋ ฌํ™”๋œ JSON ์ฝ˜ํ…์ธ ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฒƒ์ด Flux<String>์—์„œ JSON ๋ฐฐ์—ด์„ ๋ Œ๋”๋งํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด Flux#collectToList()๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  Mono<List<String>>์„ ์ธ์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค.

 
 

Form Data

FormHttpMessageReader ๋ฐ FormHttpMessageWriter๋Š” ๋””์ฝ”๋”ฉ ๋ฐ ์ธ์ฝ”๋”ฉ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ/x-www-form-urlencoded ์ฝ˜ํ…์ธ ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

์–‘์‹ ์ฝ˜ํ…์ธ ์— ์—ฌ๋Ÿฌ ์œ„์น˜์—์„œ ์•ก์„ธ์Šคํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์€ ์„œ๋ฒ„ ์ชฝ์—์„œ ServerWebExchange๋Š” FormHttpMessageReader๋ฅผ ํ†ตํ•ด ์ฝ˜ํ…์ธ ๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•œ ๋‹ค์Œ ๋ฐ˜๋ณต์ ์ธ ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•ด ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œํ•˜๋Š” ์ „์šฉ getFormData() ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์›น ์ฒ˜๋ฆฌ๊ธฐ API ์„น์…˜์˜ ์–‘์‹ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

getFormData()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์š”์ฒญ ๋ณธ๋ฌธ์—์„œ ์›๋ณธ ์›์‹œ ์ฝ˜ํ…์ธ ๋ฅผ ๋” ์ด์ƒ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์€ ์›์‹œ ์š”์ฒญ ๋ณธ๋ฌธ์—์„œ ์ฝ๋Š” ๋Œ€์‹  ์บ์‹œ๋œ ์–‘์‹ ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•ด ์ผ๊ด€๋˜๊ฒŒ ServerWebExchange๋ฅผ ๊ฑฐ์ณ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

Multipart

๋ฐ MultipartHttpMessageWriter๋Š” "multipart/form-data", "multipart/mixed" ๋ฐ "multipart/related" ์ฝ˜ํ…์ธ ์˜ ๋””์ฝ”๋”ฉ ๋ฐ ์ธ์ฝ”๋”ฉ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด MultipartHttpMessageReader๋Š” Flux<Part>์— ๋Œ€ํ•œ ์‹ค์ œ ๊ตฌ๋ฌธ ๋ถ„์„์„ ์œ„ํ•ด ๋‹ค๋ฅธ HttpMessageReader์— ์œ„์ž„ํ•œ ๋‹ค์Œ ๋‹จ์ˆœํžˆ ํŒŒํŠธ๋ฅผ MultiValueMap์œผ๋กœ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ DefaultPartHttpMessageReader๊ฐ€ ์‚ฌ์šฉ๋˜์ง€๋งŒ ServerCodecConfigurer๋ฅผ ํ†ตํ•ด ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. DefaultPartHttpMessageReader์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ DefaultPartHttpMessageReader์˜ javadoc์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์—ฌ๋Ÿฌ ์œ„์น˜์—์„œ ๋‹ค์ค‘ ํŒŒํŠธ ์–‘์‹ ์ฝ˜ํ…์ธ ์— ์•ก์„ธ์Šคํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋ฒ„ ์ชฝ์—์„œ ServerWebExchange๋Š” MultipartHttpMessageReader๋ฅผ ํ†ตํ•ด ์ฝ˜ํ…์ธ ๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•œ ๋‹ค์Œ ๋ฐ˜๋ณต ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•ด ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œํ•˜๋Š” ์ „์šฉ getMultipartData() ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์›น ์ฒ˜๋ฆฌ๊ธฐ API ์„น์…˜์˜ ๋‹ค์ค‘ ํŒŒํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

getMultipartData()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์š”์ฒญ ๋ณธ๋ฌธ์—์„œ ์›๋ณธ ์›์‹œ ์ฝ˜ํ…์ธ ๋ฅผ ๋” ์ด์ƒ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ํŒŒํŠธ์— ๋Œ€ํ•œ ๋ฐ˜๋ณต์ ์ธ ๋งต ์œ ์‚ฌ ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•ด getMultipartData()๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ Flux<Part>์— ๋Œ€ํ•œ ์ผํšŒ์„ฑ ์•ก์„ธ์Šค๋ฅผ ์œ„ํ•ด SynchronossPartHttpMessageReader์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Limits

์ž…๋ ฅ ์ŠคํŠธ๋ฆผ์˜ ์ผ๋ถ€ ๋˜๋Š” ์ „๋ถ€๋ฅผ ๋ฒ„ํผ๋งํ•˜๋Š” ๋””์ฝ”๋” ๋ฐ HttpMessageReader ๊ตฌํ˜„์€ ๋ฉ”๋ชจ๋ฆฌ์— ๋ฒ„ํผ๋งํ•  ์ตœ๋Œ€ ๋ฐ”์ดํŠธ ์ˆ˜์— ๋Œ€ํ•œ ์ œํ•œ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ์ž…๋ ฅ์ด ์ง‘๊ณ„๋˜๊ณ  ๋‹จ์ผ ๊ฐœ์ฒด(์˜ˆ: @RequestBody๋ฐ”์ดํŠธ[], x-www-form-url์ธ์ฝ”๋”ฉ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ)๋กœ ํ‘œํ˜„๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ„ํผ๋ง์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„ํผ๋ง์€ ์ž…๋ ฅ ์ŠคํŠธ๋ฆผ(์˜ˆ: ๊ตฌ๋ถ„๋œ ํ…์ŠคํŠธ, JSON ๊ฐœ์ฒด์˜ ์ŠคํŠธ๋ฆผ ๋“ฑ)์„ ๋ถ„ํ• ํ•  ๋•Œ ์ŠคํŠธ๋ฆฌ๋ฐ์—์„œ๋„ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ŠคํŠธ๋ฆฌ๋ฐ์˜ ๊ฒฝ์šฐ ์ œํ•œ์€ ์ŠคํŠธ๋ฆผ์˜ ํ•œ ๊ฐœ์ฒด์™€ ์—ฐ๊ฒฐ๋œ ๋ฐ”์ดํŠธ ์ˆ˜์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

๋ฒ„ํผ ํฌ๊ธฐ๋ฅผ ๊ตฌ์„ฑํ•˜๋ ค๋ฉด ์ฃผ์–ด์ง„ ๋””์ฝ”๋” ๋˜๋Š” HttpMessageReader๊ฐ€ maxInMemorySize ์†์„ฑ์„ ๋…ธ์ถœํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ๊ทธ๋ ‡๋‹ค๋ฉด Javadoc์— ๊ธฐ๋ณธ๊ฐ’์— ๋Œ€ํ•œ ์„ธ๋ถ€ ์ •๋ณด๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ชฝ์—์„œ ServerCodecConfigurer๋Š” ๋ชจ๋“  ์ฝ”๋ฑ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹จ์ผ ์œ„์น˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค(HTTP ๋ฉ”์‹œ์ง€ ์ฝ”๋ฑ ์ฐธ์กฐ). ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ๋Š” WebClient.Builder์—์„œ ๋ชจ๋“  ์ฝ”๋ฑ์— ๋Œ€ํ•œ ์ œํ•œ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์ค‘ ํŒŒํŠธ ๊ตฌ๋ฌธ ๋ถ„์„์˜ ๊ฒฝ์šฐ maxInMemorySize ์†์„ฑ์€ ํŒŒ์ผ์ด ์•„๋‹Œ ํŒŒํŠธ์˜ ํฌ๊ธฐ๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ํŒŒํŠธ์˜ ๊ฒฝ์šฐ ํŒŒํŠธ๊ฐ€ ๋””์Šคํฌ์— ๊ธฐ๋ก๋˜๋Š” ์ž„๊ณ„๊ฐ’์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ๋””์Šคํฌ์— ๊ธฐ๋ก๋œ ํŒŒ์ผ ํŒŒํŠธ์˜ ๊ฒฝ์šฐ ํŒŒํŠธ๋‹น ๋””์Šคํฌ ๊ณต๊ฐ„์„ ์ œํ•œํ•˜๋Š” ์ถ”๊ฐ€ maxDiskUsagePerPart ์†์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์ค‘ ํŒŒํŠธ ์š”์ฒญ์—์„œ ์ „์ฒด ํŒŒํŠธ ์ˆ˜๋ฅผ ์ œํ•œํ•˜๋Š” maxParts ์†์„ฑ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. WebFlux์—์„œ ์„ธ ๊ฐ€์ง€๋ฅผ ๋ชจ๋‘ ๊ตฌ์„ฑํ•˜๋ ค๋ฉด MultipartHttpMessageReader์˜ ๋ฏธ๋ฆฌ ๊ตฌ์„ฑ๋œ ์ธ์Šคํ„ด์Šค๋ฅผ ServerCodecConfigurer์— ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

Streaming

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

HTTP ์‘๋‹ต(์˜ˆ: ํ…์ŠคํŠธ/์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ, application/x-ndjson)์œผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐํ•  ๋•Œ ์—ฐ๊ฒฐ์ด ๋Š๊ธด ํด๋ผ์ด์–ธํŠธ๋ฅผ ์กฐ๋งŒ๊ฐ„ ์•ˆ์ •์ ์œผ๋กœ ๊ฐ์ง€ํ•˜๋ ค๋ฉด ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ „์†ก์€ ์ฃผ์„ ์ „์šฉ, ๋นˆ SSE ์ด๋ฒคํŠธ ๋˜๋Š” ํ•˜ํŠธ๋น„ํŠธ ์—ญํ• ์„ ํšจ๊ณผ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐํƒ€ "์ž‘๋™ ๊ธˆ์ง€" ๋ฐ์ดํ„ฐ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

DataBuffer

DataBuffer๋Š” WebFlux์˜ ๋ฐ”์ดํŠธ ๋ฒ„ํผ์— ๋Œ€ํ•œ ํ‘œํ˜„์ž…๋‹ˆ๋‹ค. ์ด ์ฐธ์กฐ์˜ Spring Core ๋ถ€๋ถ„์—๋Š” ๋ฐ์ดํ„ฐ ๋ฒ„ํผ ๋ฐ ์ฝ”๋ฑ ์„น์…˜์—์„œ ์ž์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์ดํ•ดํ•ด์•ผ ํ•  ์š”์ ์€ Netty์™€ ๊ฐ™์€ ์ผ๋ถ€ ์„œ๋ฒ„์—์„œ๋Š” ๋ฐ”์ดํŠธ ๋ฒ„ํผ๊ฐ€ ํ’€๋ง๋˜๊ณ  ์ฐธ์กฐ ํšŸ์ˆ˜๊ฐ€ ๊ณ„์‚ฐ๋˜๋ฉฐ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์†Œ๋น„ ๋  ๋•Œ ํ•ด์ œ๋˜์–ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

WebFlux ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ฝ”๋ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์œ„ ์ˆ˜์ค€ ๊ฐœ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž ์ง€์ • ์ฝ”๋ฑ์„ ๋งŒ๋“ค์ง€ ์•Š๋Š” ํ•œ ๋ฐ์ดํ„ฐ ๋ฒ„ํผ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š” ํ•œ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ์— ๊ด€์‹ฌ์„ ๊ฐ€์งˆ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ ๋ฒ„ํผ ๋ฐ ์ฝ”๋ฑ์˜ ์ •๋ณด, ํŠนํžˆ DataBuffer ์‚ฌ์šฉ ์„น์…˜์„ ๊ฒ€ํ† ํ•˜์‹ญ์‹œ์˜ค.

1.2.6. Logging

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

์Šคํ”„๋ง WebFlux์˜ ๋””๋ฒ„๊ทธ ๋ ˆ๋ฒจ ๋กœ๊น…์€ ์ž‘๊ณ  ์ตœ์†Œ์ด๋ฉฐ ์ธ๊ฐ„ ์นœํ™”์ ์œผ๋กœ ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ณตํ•ด์„œ ์œ ์šฉํ•œ ์ •๋ณด์˜ ๋†’์€ ๊ฐ€์น˜์™€ ํŠน์ • ๋ฌธ์ œ๋ฅผ ๋””๋ฒ„๊น… ํ•  ๋•Œ๋งŒ ์œ ์šฉํ•œ ์ •๋ณด์— ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค.

TRACE ๋ ˆ๋ฒจ ๋กœ๊น…์€ ์ผ๋ฐ˜์ ์œผ๋กœ DEBUG์™€ ๋™์ผํ•œ ์›์น™์„ ๋”ฐ๋ฅด์ง€๋งŒ(์˜ˆ๋ฅผ ๋“ค์–ด ํŒŒ์ด์–ดํ˜ธ์Šค๊ฐ€ ์•„๋‹ˆ์–ด์•ผ ํ•จ) ๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ๋””๋ฒ„๊น…ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ผ๋ถ€ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋Š” TRACE ๋ฐ DEBUG์—์„œ ๋‹ค๋ฅธ ์ˆ˜์ค€์˜ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ข‹์€ ๋กœ๊น…์€ ๋กœ๊ทธ ์‚ฌ์šฉ ๊ฒฝํ—˜์—์„œ ๋น„๋กฏ๋ฉ๋‹ˆ๋‹ค. ๋ช…์‹œ๋œ ๋ชฉํ‘œ๋ฅผ ์ถฉ์กฑํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ•˜๋ฉด ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค.

 

Log Id

WebFlux์—์„œ๋Š” ๋‹จ์ผ ์š”์ฒญ์„ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์Šค๋ ˆ๋“œ ID๋Š” ํŠน์ • ์š”์ฒญ์— ์†ํ•˜๋Š” ๋กœ๊ทธ ๋ฉ”์‹œ์ง€์˜ ์ƒ๊ด€ ๊ด€๊ณ„๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด WebFlux ๋กœ๊ทธ ๋ฉ”์‹œ์ง€์— ๊ธฐ๋ณธ์ ์œผ๋กœ ์š”์ฒญ๋ณ„ ID๊ฐ€ ์ ‘๋‘์‚ฌ๋กœ ๋ถ™๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.

์„œ๋ฒ„ ์ธก์—์„œ ๋กœ๊ทธ ID๋Š” ServerWebExchange ํŠน์„ฑ(LOG_ID_ATTRIBUTE)์— ์ €์žฅ๋˜์ง€๋งŒ ํ•ด๋‹น ID๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ์™„์ „ํ•œ ํ˜•์‹์˜ ์ ‘๋‘์‚ฌ๋Š” ServerWebExchange#getLogPrefix()์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. WebClient ์ธก์—์„œ ๋กœ๊ทธ ID๋Š” ClientRequest ์†์„ฑ(LOG_ID_ATTRIBUTE)์— ์ €์žฅ๋˜์ง€๋งŒ ์™„์ „ํ•œ ํ˜•์‹์˜ ์ ‘๋‘์‚ฌ๋Š” ClientRequest#logPrefix()์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Sensitive Data

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๋””๋ฒ„๊ทธ ๋ฐ ์ถ”์  ๋กœ๊น…์€ ์ค‘์š”ํ•œ ์ •๋ณด๋ฅผ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์–‘์‹ ๋งค๊ฐœ ๋ณ€์ˆ˜์™€ ํ—ค๋”๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋งˆ์Šคํ‚น๋˜๋Š” ์ด์œ ์ด๋ฉฐ ์ „์ฒด ๋กœ๊น…์„ ๋ช…์‹œ์ ์œผ๋กœ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” ์„œ๋ฒ„ ์ชฝ ์š”์ฒญ์— ๋Œ€ํ•ด ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

@Configuration
@EnableWebFlux
class MyConfig : WebFluxConfigurer {

    override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
        configurer.defaultCodecs().enableLoggingRequestDetails(true)
    }
}
 

๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” ํด๋ผ์ด์–ธํŠธ ์ชฝ ์š”์ฒญ์— ๋Œ€ํ•ด ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

val consumer: (ClientCodecConfigurer) -> Unit  = { configurer -> configurer.defaultCodecs().enableLoggingRequestDetails(true) }

val webClient = WebClient.builder()
        .exchangeStrategies({ strategies -> strategies.codecs(consumer) })
        .build()
 
 

Appenders

SLF4J ๋ฐ Log4J 2์™€ ๊ฐ™์€ ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ฐจ๋‹จ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋น„๋™๊ธฐ ๋กœ๊ฑฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊น…์„ ์œ„ํ•ด ํ์— ๋Œ€๊ธฐํ•  ์ˆ˜ ์—†๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ž ์žฌ์ ์œผ๋กœ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ณ ์œ ํ•œ ๋‹จ์ ์ด ์žˆ์ง€๋งŒ ํ˜„์žฌ ์‚ฌํ›„ ๋Œ€์‘์ ์ด๊ณ  ๋น„์ฐจ๋‹จ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์ƒ์˜ ์˜ต์…˜์ž…๋‹ˆ๋‹ค.

 

Custom codecs

์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์€ ์ถ”๊ฐ€ ๋ฏธ๋””์–ด ์œ ํ˜• ๋˜๋Š” ๊ธฐ๋ณธ ์ฝ”๋ฑ์—์„œ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŠน์ • ๋™์ž‘์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž ์ง€์ • ์ฝ”๋ฑ์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์ž๊ฐ€ ํ‘œํ˜„ํ•œ ์ผ๋ถ€ ๊ตฌ์„ฑ ์˜ต์…˜์€ ๊ธฐ๋ณธ ์ฝ”๋ฑ์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ์ง€์ • ์ฝ”๋ฑ์€ ๋ฒ„ํผ๋ง ์ œํ•œ ์ ์šฉ ๋˜๋Š” ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ ๋กœ๊น…๊ณผ ๊ฐ™์€ ๊ธฐ๋ณธ ์„ค์ •์— ๋งž์ถœ ๊ธฐํšŒ๋ฅผ ์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” ํด๋ผ์ด์–ธํŠธ ์ชฝ ์š”์ฒญ์— ๋Œ€ํ•ด ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

val webClient = WebClient.builder()
        .codecs({ configurer ->
                val decoder = CustomDecoder()
                configurer.customCodecs().registerWithDefaultConfig(decoder)
         })
        .build()
 

1.3. DispatcherHandler

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

Spring MVC์™€ ์œ ์‚ฌํ•˜๊ฒŒ Spring WebFlux๋Š” ์ค‘์•™ ์›น ํ•ธ๋“ค๋Ÿฌ ์ธ DispatcherHandler๊ฐ€ ์š”์ฒญ ์ฒ˜๋ฆฌ๋ฅผ์œ„ํ•œ ๊ณต์œ  ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ˜๋ฉด ์‹ค์ œ ์ž‘์—…์€ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•œ ๋Œ€๋ฆฌ์ž ๊ตฌ์„ฑ ์š”์†Œ์— ์˜ํ•ด ์ˆ˜ํ–‰๋˜๋Š” ์ „๋ฉด ์ปจํŠธ๋กค๋Ÿฌ ํŒจํ„ด์„ ์ค‘์‹ฌ์œผ๋กœ ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ชจ๋ธ์€ ์œ ์—ฐํ•˜๋ฉฐ ๋‹ค์–‘ํ•œ ์›Œํฌํ”Œ๋กœ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

๋””์ŠคํŒจ์ฒ˜ ํ•ธ๋“ค๋Ÿฌ๋Š” ์Šคํ”„๋ง ๊ตฌ์„ฑ์—์„œ ํ•„์š”ํ•œ ๋Œ€๋ฆฌ์ž ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์Šคํ”„๋ง ๋นˆ ์ž์ฒด๋กœ ์„ค๊ณ„๋˜์—ˆ์œผ๋ฉฐ ์‹คํ–‰๋˜๋Š” ์ปจํ…์ŠคํŠธ์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•ด ApplicationContextAware๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. DispatcherHandler๊ฐ€ webHandler์˜ Bean ์ด๋ฆ„์œผ๋กœ ์„ ์–ธ๋˜๋ฉด, WebHandler API์— ์„ค๋ช…๋œ ๋Œ€๋กœ ์š”์ฒญ ์ฒ˜๋ฆฌ ์ฒด์ธ์„ ์กฐํ•ฉํ•˜๋Š” WebHttpHandlerBuilder์— ์˜ํ•ด ์ฐจ๋ก€๋กœ ๋ฐœ๊ฒฌ๋ฉ๋‹ˆ๋‹ค.

WebFlux ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์Šคํ”„๋ง ๊ตฌ์„ฑ์—๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹ค์Œ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

  • Bean ์ด๋ฆ„์„ ๊ฐ€์ง„ DispatcherHandler webHandler
  • WebFilter and WebExceptionHandler beans
  • DispatcherHandler special beans
  • Others

๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด ์ฒ˜๋ฆฌ ์ฒด์ธ์„ ๋นŒ๋“œํ•˜๊ธฐ ์œ„ํ•ด WebHttpHandlerBuilder์— ๊ตฌ์„ฑ์ด ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

val context: ApplicationContext = ...
val handler = WebHttpHandlerBuilder.applicationContext(context).build()
 

๊ฒฐ๊ณผ HttpHandler๋Š” ์„œ๋ฒ„ ์–ด๋Œ‘ํ„ฐ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

1.3.1. Special Bean Types

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๋””์ŠคํŒจ์ฒ˜ํ•ธ๋“ค๋Ÿฌ๋Š” ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ ์ ˆํ•œ ์‘๋‹ต์„ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด ํŠน์ˆ˜ Bean์— ์œ„์ž„ํ•ฉ๋‹ˆ๋‹ค. "ํŠน์ˆ˜ ๋นˆ"์ด๋ž€ WebFlux ํ”„๋ ˆ์ž„ ์›Œํฌ ๊ณ„์•ฝ์„ ๊ตฌํ˜„ํ•˜๋Š” ์Šคํ”„๋ง ๊ด€๋ฆฌ ๊ฐœ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ๊ธฐ๋ณธ ์ œ๊ณต ๊ณ„์•ฝ๊ณผ ํ•จ๊ป˜ ์ œ๊ณต๋˜์ง€๋งŒ ์†์„ฑ์„ ์‚ฌ์šฉ์ž ์ง€์ •ํ•˜๊ฑฐ๋‚˜ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ํ‘œ์—๋Š” ๋””์ŠคํŒจ์ฒ˜ ํ•ธ๋“ค๋Ÿฌ์—์„œ ๋ฐœ๊ฒฌํ•œ ํŠน์ˆ˜ Bean์ด ๋‚˜์—ด๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์œ„ ๋ ˆ๋ฒจ์—์„œ ๋ฐœ๊ฒฌ๋œ ๋‹ค๋ฅธ Bean๋„ ์žˆ์Šต๋‹ˆ๋‹ค(์›น ํ•ธ๋“ค๋Ÿฌ API์˜ ํŠน์ˆ˜ Bean ์œ ํ˜• ์ฐธ์กฐ).

Bean type Explanation
HandlerMapping ์š”์ฒญ์„ ์ฒ˜๋ฆฌ๊ธฐ์— ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค. ๋งคํ•‘์€ ๋ช‡ ๊ฐ€์ง€ ๊ธฐ์ค€์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฉฐ, ์„ธ๋ถ€ ์ •๋ณด๋Š” HandlerMapping ๊ตฌํ˜„(์ฃผ์„์ด ๋‹ฌ๋ฆฐ ์ปจํŠธ๋กค๋Ÿฌ, ๋‹จ์ˆœ URL ํŒจํ„ด ๋งคํ•‘ ๋“ฑ)์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.
์ฃผ์š” HandlerMapping ๊ตฌํ˜„์€ @RequestMapping ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ RequestMappingHandlerMapping, ๊ธฐ๋Šฅ์  ๋์  ๊ฒฝ๋กœ์— ๋Œ€ํ•œ RouterFunctionMapping, URI ๊ฒฝ๋กœ ํŒจํ„ด ๋ฐ WebHandler ์ธ์Šคํ„ด์Šค์˜ ๋ช…์‹œ์  ๋“ฑ๋ก์„ ์œ„ํ•œ SimpleUrlHandlerMapping์ž…๋‹ˆ๋‹ค.
HandlerAdapter DispatcherHandler๊ฐ€ ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ์‹ค์ œ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๋ฐฉ์‹์— ๊ด€๊ณ„์—†์ด ์š”์ฒญ์— ๋งคํ•‘๋œ ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•˜๋ ค๋ฉด ์ฃผ์„์„ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ์˜ ์ฃผ์š” ๋ชฉ์ ์€ ๋””์ŠคํŒจ์ฒ˜ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ด๋Ÿฌํ•œ ์„ธ๋ถ€ ์ •๋ณด๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
HandlerResultHandler ์ฒ˜๋ฆฌ๊ธฐ ํ˜ธ์ถœ์˜ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‘๋‹ต์„ ์™„๋ฃŒํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

1.3.2. WebFlux Config

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ธํ”„๋ผ Bean(์›น ํ•ธ๋“ค๋Ÿฌ API ๋ฐ DispatcherHandler ์•„๋ž˜์— ๋‚˜์—ด๋จ)์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ WebFlux ๊ตฌ์„ฑ์ด ๊ฐ€์žฅ ์ข‹์€ ์‹œ์ž‘์ ์ž…๋‹ˆ๋‹ค. ํ•„์š”ํ•œ Bean์„ ์„ ์–ธํ•˜๊ณ  ์ด๋ฅผ ์‚ฌ์šฉ์ž ์ •์˜ํ•˜๊ธฐ ์œ„ํ•œ ์ƒ์œ„ ๋ ˆ๋ฒจ ๊ตฌ์„ฑ ์ฝœ๋ฐฑ API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” WebFlux ๊ตฌ์„ฑ์— ์˜์กดํ•˜์—ฌ ์Šคํ”„๋ง WebFlux๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ๋งŽ์€ ํŽธ๋ฆฌํ•œ ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

1.3.3. Processing

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๋””์ŠคํŒจ์ฒ˜ ํ•ธ๋“ค๋Ÿฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

  • ๊ฐ HandlerMapping์€ ์ผ์น˜ํ•˜๋Š” ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ฐพ๋„๋ก ์š”์ฒญ๋˜๊ณ  ์ฒซ ๋ฒˆ์งธ ์ผ์น˜ ํ•ญ๋ชฉ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ๋ฐœ๊ฒฌ๋˜๋ฉด ์ ์ ˆํ•œ HandlerAdapter๋ฅผ ํ†ตํ•ด ์‹คํ–‰๋˜๋ฉฐ, ์ด ์–ด๋Œ‘ํ„ฐ๋Š” ์‹คํ–‰์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ HandlerResult๋กœ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  • ๋Š” ์‘๋‹ต์— ์ง์ ‘ ์“ฐ๊ฑฐ๋‚˜ ๋ Œ๋”๋งํ•  ๋ทฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌ๋ฅผ ์™„๋ฃŒํ•˜๊ธฐ ์œ„ํ•ด ์ ์ ˆํ•œ HandlerResultHandler์— ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

1.3.4. Result Handling

๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’์€ ๋ช‡ ๊ฐ€์ง€ ์ถ”๊ฐ€ ์ปจํ…์ŠคํŠธ์™€ ํ•จ๊ป˜ ์ฒ˜๋ฆฌ๊ธฐ ๊ฒฐ๊ณผ๋กœ ๋ž˜ํ•‘๋˜๊ณ  ํ•ด๋‹น ์ฒ˜๋ฆฌ๊ธฐ์— ๋Œ€ํ•œ ์ง€์›์„ ํด๋ ˆ์ž„ํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ์ฒ˜๋ฆฌ๊ธฐ ๊ฒฐ๊ณผ์ฒ˜๋ฆฌ๊ธฐ์— ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํ‘œ์—์„œ๋Š” ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ HandlerResultHandler ๊ตฌํ˜„์„ ๋ณด์—ฌ ์ฃผ๋ฉฐ, ๋ชจ๋‘ WebFlux ๊ตฌ์„ฑ์—์„œ ์„ ์–ธ๋ฉ๋‹ˆ๋‹ค.

Result Handler Type Return Values Default Order
ResponseEntityResultHandler ResponseEntity, typically from @Controller instances. 0
ServerResponseResultHandler ServerResponse, typically from functional endpoints. 0
ResponseBodyResultHandler Handle return values from @ResponseBody methods or @RestController classes. 100
ViewResolutionResultHandler CharSequence, View, Model, Map, Rendering, or any other Object is treated as a model attribute.
See also View Resolution.
Integer.MAX_VALUE

1.3.5. Exceptions

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

HandlerAdapter ๊ตฌํ˜„์€ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์™€ ๊ฐ™์€ ์š”์ฒญ ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋‚ด๋ถ€ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์š”์ฒญ ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ๋น„๋™๊ธฐ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ๊ฐ€ ์ง€์—ฐ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Š” ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋ฐ˜ํ™˜ํ•˜๋Š” HandlerResult์— ์„ค์ •๋œ DispatchExceptionHandler๋กœ ๋…ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์„ค์ •์ด ์™„๋ฃŒ๋˜๋ฉด DispatcherHandler๋Š” ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ์—๋„ ์ด๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

HandlerAdapter๋Š” DispatchExceptionHandler๋ฅผ ๊ตฌํ˜„ํ•˜๋„๋ก ์„ ํƒํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ DispatcherHandler๋Š” ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ๋งคํ•‘๋˜๊ธฐ ์ „์— ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ(์˜ˆ: ์ฒ˜๋ฆฌ๊ธฐ ๋งคํ•‘ ์ค‘ ๋˜๋Š” WebFilter ์ด์ „)์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

"์ฃผ์„ ์ด ์ถ”๊ฐ€๋œ ์ปจํŠธ๋กค๋Ÿฌ" ์„น์…˜์˜ ์˜ˆ์™ธ ๋˜๋Š” WebHandler API ์„น์…˜์˜ ์˜ˆ์™ธ๋„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

1.3.6. View Resolution

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๋ทฐ ํ•ด์ƒ๋„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ๋ทฐ ๊ธฐ์ˆ ์— ์–ฝ๋งค์ด์ง€ ์•Š๊ณ  HTML ํ…œํ”Œ๋ฆฟ๊ณผ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์— ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์Šคํ”„๋ง WebFlux์—์„œ ๋ทฐ ํ•ด์ƒ๋„๋Š” ViewResolver ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ž์—ด(๋…ผ๋ฆฌ์  ๋ทฐ ์ด๋ฆ„์„ ๋‚˜ํƒ€๋ƒ„)์„ View ์ธ์Šคํ„ด์Šค์— ๋งคํ•‘ํ•˜๋Š” ์ „์šฉ HandlerResultHandler๋ฅผ ํ†ตํ•ด ์ง€์›๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ทฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‘๋‹ต์„ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

 

Handling

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

์— ์ „๋‹ฌ๋œ ์ฒ˜๋ฆฌ๊ธฐ ๊ฒฐ๊ณผ์—๋Š” ์ฒ˜๋ฆฌ๊ธฐ์˜ ๋ฐ˜ํ™˜ ๊ฐ’๊ณผ ์š”์ฒญ ์ฒ˜๋ฆฌ ์ค‘์— ์ถ”๊ฐ€๋œ ํŠน์„ฑ์ด ํฌํ•จ๋œ ๋ชจ๋ธ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜ ๊ฐ’์€ ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

  • String, CharSequence: ๊ตฌ์„ฑ๋œ ViewResolver ๊ตฌํ˜„ ๋ชฉ๋ก์„ ํ†ตํ•ด View๋กœ ํ™•์ธ๋  ๋…ผ๋ฆฌ์  ๋ทฐ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.
  • void: ์š”์ฒญ ๊ฒฝ๋กœ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์„ ํ–‰ ๋ฐ ํ›„ํ–‰ ์Šฌ๋ž˜์‹œ๋ฅผ ๋บ€ ๊ธฐ๋ณธ ๋ณด๊ธฐ ์ด๋ฆ„์„ ์„ ํƒํ•˜๊ณ  View๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋ทฐ ์ด๋ฆ„์ด ์ œ๊ณต๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ(์˜ˆ: ๋ชจ๋ธ ํŠน์„ฑ์ด ๋ฐ˜ํ™˜๋จ) ๋น„๋™๊ธฐ ๋ฐ˜ํ™˜ ๊ฐ’(์˜ˆ: Mono๊ฐ€ ๋น„์–ด ์žˆ์Œ)์ธ ๊ฒฝ์šฐ์—๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค.
  • Rendering: ๋ทฐ ํ™•์ธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์œ„ํ•œ API. ์ฝ”๋“œ ์™„์„ฑ์„ ํ†ตํ•ด IDE์˜ ์˜ต์…˜์„ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.
  • Model, Map: ์š”์ฒญ์— ๋Œ€ํ•ด ๋ชจ๋ธ์— ์ถ”๊ฐ€ํ•  ์ถ”๊ฐ€ ๋ชจ๋ธ ํŠน์„ฑ์ž…๋‹ˆ๋‹ค.
  • Any other: ๋‹ค๋ฅธ ๋ชจ๋“  ๋ฆฌํ„ด ๊ฐ’(BeanUtils#isSimpleProperty์— ์˜ํ•ด ํŒ๋ณ„๋˜๋Š” ๋‹จ์ˆœ ์œ ํ˜• ์ œ์™ธ)์€ ๋ชจ๋ธ์— ์ถ”๊ฐ€๋  ๋ชจ๋ธ ์†์„ฑ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ํŠน์„ฑ ์ด๋ฆ„์€ ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ @ModelAttribute ์ฃผ์„์ด ์—†๋Š” ํ•œ ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ž˜์Šค ์ด๋ฆ„์—์„œ ํŒŒ์ƒ๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋ธ์—๋Š” ๋น„๋™๊ธฐ์‹ ๋ฐ˜์‘ํ˜• ์œ ํ˜•(์˜ˆ: Reactor ๋˜๋Š” RxJava)์ด ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ Œ๋”๋งํ•˜๊ธฐ ์ „์— AbstractView๋Š” ์ด๋Ÿฌํ•œ ๋ชจ๋ธ ํŠน์„ฑ์„ ๊ตฌ์ฒด์ ์ธ ๊ฐ’์œผ๋กœ ํ™•์ธํ•˜๊ณ  ๋ชจ๋ธ์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ผ ๊ฐ’ ๋ฐ˜์‘ํ˜• ์œ ํ˜•์€ ๋‹จ์ผ ๊ฐ’ ๋˜๋Š” ๊ฐ’ ์—†์Œ(๋น„์–ด ์žˆ๋Š” ๊ฒฝ์šฐ)์œผ๋กœ ํ™•์ธ๋˜๋Š” ๋ฐ˜๋ฉด, ๋‹ค์ค‘ ๊ฐ’ ๋ฐ˜์‘ํ˜• ์œ ํ˜•(์˜ˆ: Flux<T>)์€ ์ˆ˜์ง‘๋˜์–ด List<T>๋กœ ํ™•์ธ๋ฉ๋‹ˆ๋‹ค.

๋ทฐ ํ•ด์ƒ๋„๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์€ ์Šคํ”„๋ง ๊ตฌ์„ฑ์— ViewResolutionResultHandler Bean์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ๋งŒ ํผ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. WebFlux ๊ตฌ์„ฑ์€ ๋ณด๊ธฐ ํ™•์ธ์„ ์œ„ํ•œ ์ „์šฉ ๊ตฌ์„ฑ API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Spring WebFlux์™€ ํ†ตํ•ฉ๋œ ๋ณด๊ธฐ ๊ธฐ์ˆ ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ View Technologies๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

 

Redirecting

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๋ทฐ ์ด๋ฆ„์˜ ํŠน์ˆ˜ ๋ฆฌ๋””๋ ‰์…˜: ์ ‘๋‘์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฆฌ๋””๋ ‰์…˜์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UrlBasedViewResolver(๋ฐ ํ•˜์œ„ ํด๋ž˜์Šค)๋Š” ์ด๋ฅผ ๋ฆฌ๋””๋ ‰์…˜์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๋ช…๋ น์œผ๋กœ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋จธ์ง€ ๋ณด๊ธฐ ์ด๋ฆ„์€ ๋ฆฌ๋””๋ ‰์…˜ URL์ž…๋‹ˆ๋‹ค.

์ตœ์ข… ํšจ๊ณผ๋Š” ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ RedirectView ๋˜๋Š” Rendering.redirectTo("abc").build()๋ฅผ ๋ฐ˜ํ™˜ํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•˜์ง€๋งŒ ์ด์ œ ์ปจํŠธ๋กค๋Ÿฌ ์ž์ฒด๊ฐ€ ๋…ผ๋ฆฌ์  ๋ทฐ ์ด๋ฆ„์œผ๋กœ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. redirect:/some/resource์™€ ๊ฐ™์€ ๋ณด๊ธฐ ์ด๋ฆ„์€ ํ˜„์žฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ƒ๋Œ€์ ์ด์ง€๋งŒ redirect:https://example.com/arbitrary/path ์™€ ๊ฐ™์€ ๋ณด๊ธฐ ์ด๋ฆ„์€ ์ ˆ๋Œ€ URL๋กœ ๋ฆฌ๋””๋ ‰์…˜๋ฉ๋‹ˆ๋‹ค.

 

Content Negotiation

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

ViewResolutionResultHandler๋Š” ์ฝ˜ํ…์ธ  ํ˜‘์ƒ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์š”์ฒญ ๋ฏธ๋””์–ด ์œ ํ˜•์„ ์„ ํƒํ•œ ๊ฐ ๋ณด๊ธฐ์—์„œ ์ง€์›ํ•˜๋Š” ๋ฏธ๋””์–ด ์œ ํ˜•๊ณผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. ์š”์ฒญ๋œ ๋ฏธ๋””์–ด ์œ ํ˜•์„ ์ง€์›ํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ๋ทฐ๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

JSON ๋ฐ XML๊ณผ ๊ฐ™์€ ๋ฏธ๋””์–ด ์œ ํ˜•์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด Spring WebFlux๋Š” HttpMessageWriter๋ฅผ ํ†ตํ•ด ๋ Œ๋”๋ง๋˜๋Š” ํŠน์ˆ˜ ๋ทฐ์ธ HttpMessageWriterView๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ WebFlux ๊ตฌ์„ฑ์„ ํ†ตํ•ด ๊ธฐ๋ณธ ๋ณด๊ธฐ๋กœ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๋ณด๊ธฐ๋Š” ์š”์ฒญ๋œ ๋ฏธ๋””์–ด ์œ ํ˜•๊ณผ ์ผ์น˜ํ•˜๋Š” ๊ฒฝ์šฐ ํ•ญ์ƒ ์„ ํƒ๋˜์–ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

1.4. Annotated Controllers

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

Spring WebFlux๋Š” @Controller ๋ฐ @RestController ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ฃผ์„์„ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ ๋งคํ•‘์„ ํ‘œํ˜„ํ•˜๊ณ , ์ž…๋ ฅ์„ ์š”์ฒญํ•˜๊ณ , ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋“ฑ์˜ ์ฃผ์„ ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ์ปจํŠธ๋กค๋Ÿฌ์—๋Š” ์œ ์—ฐํ•œ ๋ฉ”์„œ๋“œ ์„œ๋ช…์ด ์žˆ์œผ๋ฉฐ ๊ธฐ๋ณธ ํด๋ž˜์Šค๋ฅผ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ํŠน์ • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ชฉ๋ก์—์„œ๋Š” ๊ธฐ๋ณธ ์˜ˆ์ œ๋ฅผ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

@RestController
class HelloController {

    @GetMapping("/hello")
    fun handle() = "Hello WebFlux"
}
 

์•ž์˜ ์˜ˆ์ œ์—์„œ ๋ฉ”์„œ๋“œ๋Š” ์‘๋‹ต ๋ณธ๋ฌธ์— ์“ธ String์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

1.4.1. @Controller

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

ํ‘œ์ค€ Spring Bean ์ •์˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ œ์–ด๊ธฐ Bean์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. @Controller ์Šคํ…Œ๋ ˆ์˜คํƒ€์ž…์€ ์ž๋™ ๊ฐ์ง€๋ฅผ ํ—ˆ์šฉํ•˜๋ฉฐ ํด๋ž˜์Šค ๊ฒฝ๋กœ์—์„œ @Component ํด๋ž˜์Šค๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ์ด์— ๋Œ€ํ•œ Bean ์ •์˜๋ฅผ ์ž๋™ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•œ Spring ์ผ๋ฐ˜ ์ง€์›๊ณผ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ฃผ์„์ด ๋‹ฌ๋ฆฐ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์Šคํ…Œ๋ ˆ์˜คํƒ€์ž… ์—ญํ• ์„ ํ•˜์—ฌ ์›น ๊ตฌ์„ฑ ์š”์†Œ๋กœ์„œ์˜ ์—ญํ• ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ @Controller Bean์˜ ์ž๋™ ๊ฐ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด Java ๊ตฌ์„ฑ์— ์ปดํฌ๋„ŒํŠธ ์Šค์บ๋‹์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Configuration
@ComponentScan("org.example.web") 
class WebConfig {

    // ...
}
 
 

@RestController๋Š” ์ž์ฒด์ ์œผ๋กœ @Controller ๋ฐ @ResponseBody๋กœ ๋ฉ”ํƒ€ ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ๊ตฌ์„ฑ๋œ ์ฃผ์„์œผ๋กœ, ๋ชจ๋“  ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜•์‹ ์ˆ˜์ค€ @ResponseBody ์ฃผ์„์„ ์ƒ์†ํ•˜๋ฏ€๋กœ ์‘๋‹ต ๋ณธ๋ฌธ์— ์ง์ ‘ ์“ฐ๋Š” ์ปจํŠธ๋กค๋Ÿฌ์™€ ๋ทฐ ํ•ด์ƒ๋„ ๋ฐ HTML ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•œ ๋ Œ๋”๋ง์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

 

AOP Proxies

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๊ฒฝ์šฐ์— ๋”ฐ๋ผ ๋Ÿฐํƒ€์ž„์— AOP ํ”„๋ก์‹œ๋กœ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ฐ์ฝ”๋ ˆ์ดํŒ…ํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•œ ๊ฐ€์ง€ ์˜ˆ๋Š” ์ปจํŠธ๋กค๋Ÿฌ์— ์ง์ ‘ @Transactional ์ฃผ์„์„ ํฌํ•จํ•˜๋„๋ก ์„ ํƒํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ํŠนํžˆ ์ปจํŠธ๋กค๋Ÿฌ์˜ ๊ฒฝ์šฐ ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ง์ ‘ ์ด๋Ÿฌํ•œ ์ฃผ์„์ด ์žˆ๋Š” ๊ฒฝ์šฐ์— ์ž๋™์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  AOP ํ”„๋ก์‹œ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๊ตฌ์„ฑํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด @EnableTransactionManagement๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด @EnableTransactionManagement(proxyTargetClass = true)๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ณ  <tx:annotation-driven/>๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด <tx:annotation-driven proxy-target-class="true"/>๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

6.0๋ถ€ํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค ํ”„๋ก์‹ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด Spring WebFlux๋Š” ๋” ์ด์ƒ ์ธํ„ฐํŽ˜์ด์Šค์˜ ์œ ํ˜• ์ˆ˜์ค€ @RequestMapping ์ฃผ์„๋งŒ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ฐ์ง€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ํ”„๋ก์‹œ๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ฑฐ๋‚˜ ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ธํ„ฐํŽ˜์ด์Šค์—๋„ @Controller ์ฃผ์„์ด ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

 

1.4.2. Request Mapping

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

@RequestMapping ์ฃผ์„์€ ์š”์ฒญ์„ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์— ๋งคํ•‘ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. URL, HTTP ๋ฉ”์„œ๋“œ, ์š”์ฒญ ๋งค๊ฐœ ๋ณ€์ˆ˜, ํ—ค๋” ๋ฐ ๋ฏธ๋””์–ด ์œ ํ˜•๋ณ„๋กœ ์ผ์น˜ํ•˜๋Š” ๋‹ค์–‘ํ•œ ์†์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํด๋ž˜์Šค ์ˆ˜์ค€์—์„œ ๊ณต์œ  ๋งคํ•‘์„ ํ‘œํ˜„ํ•˜๊ฑฐ๋‚˜ ๋ฉ”์„œ๋“œ ์ˆ˜์ค€์—์„œ ํŠน์ • ๋์  ๋งคํ•‘์œผ๋กœ ๋ฒ”์œ„๋ฅผ ์ขํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@RequestMapping์˜ HTTP ๋ฉ”์„œ๋“œ๋ณ„ ๋ฐ”๋กœ ๊ฐ€๊ธฐ ๋ณ€ํ˜•๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

์•ž์˜ ์ฃผ์„์€ ๋Œ€๋ถ€๋ถ„์˜ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  HTTP ๋ฉ”์„œ๋“œ์™€ ์ผ์น˜ํ•˜๋Š” @RequestMapping ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ํŠน์ • HTTP ๋ฉ”์„œ๋“œ์— ๋งคํ•‘๋˜์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ œ๊ณต๋˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ์ฃผ์„์ž…๋‹ˆ๋‹ค. ๋™์‹œ์— ๊ณต์œ  ๋งคํ•‘์„ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ํด๋ž˜์Šค ์ˆ˜์ค€์—์„œ @RequestMapping๊ฐ€ ์—ฌ์ „ํžˆ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” ํ˜•์‹ ๋ฐ ๋ฉ”์„œ๋“œ ์ˆ˜์ค€ ๋งคํ•‘์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

@RestController
@RequestMapping("/persons")
class PersonController {

    @GetMapping("/{id}")
    fun getPerson(@PathVariable id: Long): Person {
        // ...
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    fun add(@RequestBody person: Person) {
        // ...
    }
}
 
 

URI Patterns

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

glob ํŒจํ„ด๊ณผ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ์„ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Pattern Description Example
? ํ•œ ๋ฌธ์ž์™€ ์ผ์น˜ "/pages/t?st.html" matches "/pages/test.html" and "/pages/t3st.html"
* ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ ๋‚ด์—์„œ 0๊ฐœ ์ด์ƒ์˜ ๋ฌธ์ž์™€ ์ผ์น˜ "/resources/*.png" matches "/resources/file.png"
"/projects/*/versions" matches "/projects/spring/versions" but does not match "/projects/spring/boot/versions"
** ๊ฒฝ๋กœ์˜ ๋๊นŒ์ง€ 0๊ฐœ ์ด์ƒ์˜ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์™€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค. "/resources/**" matches "/resources/file.png" and "/resources/images/file.png"
"/resources/**/file.png" is invalid as ** is only allowed at the end of the path.
{name} ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ผ์น˜์‹œํ‚ค๊ณ  "name"์ด๋ผ๋Š” ๋ณ€์ˆ˜๋กœ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค. "/projects/{project}/versions" matches "/projects/spring/versions" and captures project=spring
{name:[a-z]+} ์ •๊ทœ ํ‘œํ˜„์‹ "[a-z]+"๋ฅผ "name"์ด๋ผ๋Š” ๊ฒฝ๋กœ ๋ณ€์ˆ˜์™€ ์ผ์น˜์‹œํ‚ต๋‹ˆ๋‹ค. "/projects/{project:[a-z]+}/versions" matches "/projects/spring/versions" but not "/projects/spring1/versions"
{*path} ๊ฒฝ๋กœ์˜ ๋๊นŒ์ง€ 0๊ฐœ ์ด์ƒ์˜ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ผ์น˜์‹œํ‚ค๊ณ  "path"๋ผ๋Š” ๋ณ€์ˆ˜๋กœ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค. "/resources/{*file}" matches "/resources/images/file.png" and captures file=/images/file.png

์บก์ฒ˜๋œ URI ๋ณ€์ˆ˜๋Š” ๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด @PathVariable ์‚ฌ์šฉํ•˜์—ฌ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
    // ...
}
 

๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด ํด๋ž˜์Šค ๋ฐ ๋ฉ”์„œ๋“œ ์ˆ˜์ค€์—์„œ URI ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Controller
@RequestMapping("/owners/{ownerId}") 
class OwnerController {

    @GetMapping("/pets/{petId}") 
    fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
        // ...
    }
}
 
 

URI ๋ณ€์ˆ˜๊ฐ€ ์ž๋™์œผ๋กœ ์ ์ ˆํ•œ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜๋˜๊ฑฐ๋‚˜ TypeMismatchException์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœ ํ˜•์‹(int, long, Date ๋“ฑ)์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›๋˜๋ฉฐ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ ํ˜•์‹์— ๋Œ€ํ•œ ์ง€์›์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ˜•์‹ ๋ณ€ํ™˜ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

URI ๋ณ€์ˆ˜์˜ ์ด๋ฆ„์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ(์˜ˆ: @PathVariable("customId")) ์ด๋ฆ„์ด ๋™์ผํ•˜๊ณ  -parameters ์ปดํŒŒ์ผ๋Ÿฌ ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ปดํŒŒ์ผํ•˜๋Š” ๊ฒฝ์šฐ ํ•ด๋‹น ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌ๋ฌธ {*varName}์€ 0๊ฐœ ์ด์ƒ์˜ ๋‚˜๋จธ์ง€ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์™€ ์ผ์น˜ํ•˜๋Š” URI ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด /resources/{*path}๋Š” /resources/ ์•„๋ž˜์˜ ๋ชจ๋“  ํŒŒ์ผ๊ณผ ์ผ์น˜ํ•˜๊ณ  "path" ๋ณ€์ˆ˜๋Š” /resources์˜ ์ „์ฒด ๊ฒฝ๋กœ๋ฅผ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค.

{varName:regex} ๊ตฌ๋ฌธ์€ {varName:regex} ๊ตฌ๋ฌธ์ด ์žˆ๋Š” ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ URI ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด URL์ด /spring-web-3.0.5.jar์ธ ๊ฒฝ์šฐ ๋‹ค์Œ ๋ฉ”์„œ๋“œ๋Š” ์ด๋ฆ„, ๋ฒ„์ „ ๋ฐ ํŒŒ์ผ ํ™•์žฅ์ž๋ฅผ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.

@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
fun handle(@PathVariable version: String, @PathVariable ext: String) {
    // ...
}
 

URI ๊ฒฝ๋กœ ํŒจํ„ด์—๋Š” ๋กœ์ปฌ, ์‹œ์Šคํ…œ, ํ™˜๊ฒฝ ๋ฐ ๊ธฐํƒ€ ์†์„ฑ ์†Œ์Šค์— ๋Œ€ํ•ด PropertySourcesPlaceholderConfigurer๋ฅผ ํ†ตํ•ด ์‹œ์ž‘ ์‹œ ํ™•์ธ๋˜๋Š” ํฌํ•จ๋œ ${...} ์ž๋ฆฌ ํ‘œ์‹œ์ž๊ฐ€ ์žˆ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ผ๋ถ€ ์™ธ๋ถ€ ๊ตฌ์„ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ธฐ๋ณธ URL์„ ๋งค๊ฐœ ๋ณ€์ˆ˜ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Spring WebFlux๋Š” URI ๊ฒฝ๋กœ ์ผ์น˜ ์ง€์›์„ ์œ„ํ•ด PathPattern ๋ฐ PathPatternParser๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‘ ํด๋ž˜์Šค ๋ชจ๋‘ ์Šคํ”„๋ง ์›น์— ์žˆ์œผ๋ฉฐ ๋Ÿฐํƒ€์ž„์— ๋งŽ์€ URI ๊ฒฝ๋กœ ํŒจํ„ด์ด ์ผ์น˜ํ•˜๋Š” ์›น ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ HTTP URL ๊ฒฝ๋กœ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋„๋ก ๋ช…์‹œ ์ ์œผ๋กœ ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

Spring WebFlux๋Š” /person๊ณผ ๊ฐ™์€ ๋งคํ•‘์ด /person.*๊ณผ๋„ ์ผ์น˜ํ•˜๋Š” Spring MVC์™€ ๋‹ฌ๋ฆฌ ์ ‘๋ฏธ์‚ฌ ํŒจํ„ด ์ผ์น˜๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. URL ๊ธฐ๋ฐ˜ ์ฝ˜ํ…์ธ  ํ˜‘์ƒ์˜ ๊ฒฝ์šฐ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋” ๊ฐ„๋‹จํ•˜๊ณ  ๋ช…์‹œ์ ์ด๋ฉฐ URL ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์•…์šฉ์— ๋œ ์ทจ์•ฝํ•œ ์ฟผ๋ฆฌ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

 

Pattern Comparison

See equivalent in the Servlet stack

์—ฌ๋Ÿฌ ํŒจํ„ด์ด URL๊ณผ ์ผ์น˜ํ•˜๋Š” ๊ฒฝ์šฐ ๊ฐ€์žฅ ์ผ์น˜ํ•˜๋Š” ํŒจํ„ด์„ ์ฐพ๊ธฐ ์œ„ํ•ด ๋น„๊ตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์ž‘์—…์€ ๋ณด๋‹ค ๊ตฌ์ฒด์ ์ธ ํŒจํ„ด์„ ์ฐพ๋Š” PathPattern.SPECIFICITY_COMPARATOR๋กœ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ํŒจํ„ด์— ๋Œ€ํ•ด URI ๋ณ€์ˆ˜ ๋ฐ ์™€์ผ๋“œ์นด๋“œ ์ˆ˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ ์ˆ˜๊ฐ€ ๊ณ„์‚ฐ๋˜๋ฉฐ, URI ๋ณ€์ˆ˜์˜ ์ ์ˆ˜๋Š” ์™€์ผ๋“œ์นด๋“œ๋ณด๋‹ค ๋‚ฎ์Šต๋‹ˆ๋‹ค. ์ด์ ์ด ๋‚ฎ์€ ํŒจํ„ด์ด ์Šน๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋‘ ํŒจํ„ด์˜ ์ ์ˆ˜๊ฐ€ ๊ฐ™์œผ๋ฉด ๋” ๊ธด ํŒจํ„ด์ด ์„ ํƒ๋ฉ๋‹ˆ๋‹ค.

๋ฒ”์šฉ ํŒจํ„ด(์˜ˆ: **, {*varName})์€ ์ ์ˆ˜ ๋งค๊ธฐ๊ธฐ์—์„œ ์ œ์™ธ๋˜๋ฉฐ ํ•ญ์ƒ ๋งˆ์ง€๋ง‰์— ์ •๋ ฌ๋ฉ๋‹ˆ๋‹ค. ๋‘ ํŒจํ„ด์ด ๋ชจ๋‘ ํฌ๊ด„์ ์ด๋ฉด ๋” ๊ธด ํŒจํ„ด์ด ์„ ํƒ๋ฉ๋‹ˆ๋‹ค.

 

Consumable Media Types

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด ์š”์ฒญ์˜ Content-Type์„ ๊ธฐ์ค€์œผ๋กœ ์š”์ฒญ ๋งคํ•‘์˜ ๋ฒ”์œ„๋ฅผ ์ขํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@PostMapping("/pets", consumes = ["application/json"])
fun addPet(@RequestBody pet: Pet) {
    // ...
}
 

consumes ํŠน์„ฑ์€ ๋ถ€์ • ์‹๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: !text/plain์€ ํ…์ŠคํŠธ/์ผ๋ฐ˜ ์ด์™ธ์˜ ๋ชจ๋“  ์ฝ˜ํ…์ธ  ํ˜•์‹์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค).

ํด๋ž˜์Šค ์ˆ˜์ค€์—์„œ ๊ณต์œ  ์†Œ๋น„ ํŠน์„ฑ์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋Œ€๋ถ€๋ถ„์˜ ๋‹ค๋ฅธ ์š”์ฒญ ๋งคํ•‘ ํŠน์„ฑ๊ณผ ๋‹ฌ๋ฆฌ ํด๋ž˜์Šค ์ˆ˜์ค€์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ ๋ฉ”์„œ๋“œ ์ˆ˜์ค€์€ ํด๋ž˜์Šค ์ˆ˜์ค€ ์„ ์–ธ์„ ํ™•์žฅํ•˜๋Š” ๋Œ€์‹  ํŠน์„ฑ ์žฌ์ •์˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

MediaType์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ฏธ๋””์–ด ์œ ํ˜•(์˜ˆ: APPLICATION_JSON_VALUE ๋ฐ APPLICATION_XML_VALUE)์— ๋Œ€ํ•œ ์ƒ์ˆ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

Producible Media Types

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด Accept request ํ—ค๋”์™€ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๊ฐ€ ์ƒ์„ฑํ•˜๋Š” ์ฝ˜ํ…์ธ  ํ˜•์‹ ๋ชฉ๋ก์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์š”์ฒญ ๋งคํ•‘์˜ ๋ฒ”์œ„๋ฅผ ์ขํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@GetMapping("/pets/{petId}", produces = ["application/json"])
@ResponseBody
fun getPet(@PathVariable String petId): Pet {
    // ...
}
 

๋ฏธ๋””์–ด ์œ ํ˜•์€ ๋ฌธ์ž ์ง‘ํ•ฉ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ถ€์ • ํ‘œํ˜„์‹์ด ์ง€์›๋ฉ๋‹ˆ๋‹ค — ์˜ˆ๋ฅผ ๋“ค์–ด !text/plain์€ ํ…์ŠคํŠธ/์ผ๋ฐ˜ ์ด์™ธ์˜ ๋ชจ๋“  ์ฝ˜ํ…์ธ  ์œ ํ˜•์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

ํด๋ž˜์Šค ์ˆ˜์ค€์—์„œ ๊ณต์œ  ์ƒ์„ฑ ํŠน์„ฑ์„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋Œ€๋ถ€๋ถ„์˜ ๋‹ค๋ฅธ ์š”์ฒญ ๋งคํ•‘ ํŠน์„ฑ๊ณผ ๋‹ฌ๋ฆฌ ํด๋ž˜์Šค ์ˆ˜์ค€์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ ๋ฉ”์„œ๋“œ ์ˆ˜์ค€์€ ํด๋ž˜์Šค ์ˆ˜์ค€ ์„ ์–ธ์„ ํ™•์žฅํ•˜๋Š” ๋Œ€์‹  ํŠน์„ฑ ์žฌ์ •์˜๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

MediaType์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ฏธ๋””์–ด ์œ ํ˜•(์˜ˆ: APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE)์— ๋Œ€ํ•œ ์ƒ์ˆ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

Parameters and Headers

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

์ฟผ๋ฆฌ ๋งค๊ฐœ ๋ณ€์ˆ˜ ์กฐ๊ฑด์— ๋”ฐ๋ผ ์š”์ฒญ ๋งคํ•‘์˜ ๋ฒ”์œ„๋ฅผ ์ขํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋Š”์ง€(myParam), ์ฟผ๋ฆฌ ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ์—†๋Š”์ง€(!myParam) ๋˜๋Š” ํŠน์ • ๊ฐ’(myParam=myValue)์ด ์žˆ๋Š”์ง€ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” ๊ฐ’์ด ์žˆ๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.

@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) 
fun findPet(@PathVariable petId: String) {
    // ...
}
 
 

๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด ์š”์ฒญ ํ—ค๋” ์กฐ๊ฑด๊ณผ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

@GetMapping("/pets", headers = ["myHeader=myValue"]) 
fun findPet(@PathVariable petId: String) {
    // ...
}
 
 
 

HTTP HEAD, OPTIONS

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

@GetMapping ๋ฐ @RequestMapping(๋ฉ”์„œ๋“œ=HttpMethod.GET)์€ ์š”์ฒญ ๋งคํ•‘์„ ์œ„ํ•ด HTTP HEAD๋ฅผ ํˆฌ๋ช…ํ•˜๊ฒŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋Š” ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. HttpHandler ์„œ๋ฒ„ ์–ด๋Œ‘ํ„ฐ์— ์ ์šฉ๋˜๋Š” ์‘๋‹ต ๋ž˜ํผ๋Š” Content-Length ํ—ค๋”๊ฐ€ ์‹ค์ œ๋กœ ์‘๋‹ต์— ์“ฐ์ง€ ์•Š๊ณ  ๊ธฐ๋ก๋œ ๋ฐ”์ดํŠธ ์ˆ˜๋กœ ์„ค์ •๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ HTTP ์˜ต์…˜์€ URL ํŒจํ„ด์ด ์ผ์น˜ํ•˜๋Š” ๋ชจ๋“  @RequestMapping ๋ฉ”์„œ๋“œ์— ๋‚˜์—ด๋œ HTTP ๋ฉ”์„œ๋“œ ๋ชฉ๋ก์œผ๋กœ ์‘๋‹ต ํ—ˆ์šฉ ํ—ค๋”๋ฅผ ์„ค์ •ํ•˜์—ฌ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

HTTP ๋ฉ”์„œ๋“œ ์„ ์–ธ์ด ์—†๋Š” @RequestMapping์˜ ๊ฒฝ์šฐ ํ—ˆ์šฉ ํ—ค๋”๋Š” GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋Š” ํ•ญ์ƒ ์ง€์›๋˜๋Š” HTTP ๋ฉ”์„œ๋“œ๋ฅผ ์„ ์–ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: HTTP ๋ฉ”์„œ๋“œ๋ณ„ ๋ณ€ํ˜•(@GetMapping, @PostMapping ๋“ฑ) ์‚ฌ์šฉ).

@RequestMapping ๋ฉ”์„œ๋“œ๋ฅผ HTTP HEAD ๋ฐ HTTP ์˜ต์…˜์— ๋ช…์‹œ์ ์œผ๋กœ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ์—๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Custom Annotations

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

์Šคํ”„๋ง WebFlux๋Š” ์š”์ฒญ ๋งคํ•‘์„ ์œ„ํ•ด ๊ตฌ์„ฑ๋œ ์ฃผ์„์˜ ์‚ฌ์šฉ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ๋“ค์€ ๊ทธ ์ž์ฒด๋กœ @RequestMapping๋กœ ๋ฉ”ํƒ€ ์ฃผ์„์ด ๋‹ฌ๋ฆฌ๊ณ  ๋” ์ข๊ณ  ๊ตฌ์ฒด์ ์ธ ๋ชฉ์ ์œผ๋กœ @RequestMapping ์†์„ฑ์˜ ํ•˜์œ„ ์ง‘ํ•ฉ(๋˜๋Š” ์ „์ฒด)์„ ๋‹ค์‹œ ์„ ์–ธํ•˜๋„๋ก ๊ตฌ์„ฑ๋œ ์ฃผ์„์ž…๋‹ˆ๋‹ค.

@GetMapping, @PostMapping, @PutMapping, @DeleteMapping ๋ฐ @PatchMapping๋Š” ๊ตฌ์„ฑ๋œ ์ฃผ์„์˜ ์˜ˆ์ž…๋‹ˆ๋‹ค. ํ‹€๋ฆผ์—†์ด ๋Œ€๋ถ€๋ถ„์˜ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  HTTP ๋ฉ”์„œ๋“œ์™€ ์ผ์น˜ํ•˜๋Š” @RequestMapping ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ํŠน์ • HTTP ๋ฉ”์„œ๋“œ์— ๋งคํ•‘๋˜์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์ž‘์„ฑ๋œ ์ฃผ์„์˜ ์˜ˆ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ฃผ์„์ด ์„ ์–ธ๋˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด์‹ญ์‹œ์˜ค.

Spring WebFlux๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ์š”์ฒญ ์ผ์น˜ ๋…ผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ง€์ • ์š”์ฒญ ๋งคํ•‘ ์†์„ฑ๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด ์˜ต์…˜์€ RequestMappingHandlerMapping์„ ์„œ๋ธŒํด๋ž˜์‹ฑํ•˜๊ณ  getCustomMethodCondition ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•ด์•ผ ํ•˜๋Š” ๊ณ ๊ธ‰ ์˜ต์…˜์œผ๋กœ, ์‚ฌ์šฉ์ž ์ง€์ • ํŠน์„ฑ์„ ํ™•์ธํ•˜๊ณ  ์‚ฌ์šฉ์ž ๊ณ ์œ ์˜ RequestCondition์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Explicit Registrations

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๋™์  ๋“ฑ๋ก ๋˜๋Š” ๊ณ ๊ธ‰ ์‚ฌ๋ก€(์˜ˆ: ๋‹ค๋ฅธ URL์— ์žˆ๋Š” ๋™์ผํ•œ ์ฒ˜๋ฆฌ๊ธฐ์˜ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค)์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Handler ๋ฉ”์„œ๋“œ๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

@Configuration
class MyConfig {

    @Autowired
    fun setHandlerMapping(mapping: RequestMappingHandlerMapping, handler: UserHandler) { 

        val info = RequestMappingInfo.paths("/user/{id}").methods(RequestMethod.GET).build() 

        val method = UserHandler::class.java.getMethod("getUser", Long::class.java) 

        mapping.registerMapping(info, handler, method) 
    }
}
 
 

1.4.3. Handler Methods

See equivalent in the Servlet stack

@RequestMapping handler methods have a flexible signature and can choose from a range of supported controller method arguments and return values.

Explicit Registrations

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

๋‹ค์Œ ํ‘œ์—์„œ๋Š” ์ง€์›๋˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ์ธ์ˆ˜๋ฅผ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜• ์œ ํ˜•(Reactor, RxJava ๋˜๋Š” ๊ธฐํƒ€)์€ I/O ์ฐจ๋‹จ(์˜ˆ: ์š”์ฒญ ๋ณธ๋ฌธ ์ฝ๊ธฐ)์„ ํ™•์ธํ•ด์•ผ ํ•˜๋Š” ์ธ์ˆ˜์—์„œ ์ง€์›๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์„ค๋ช… ์—ด์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ํ˜•์‹์€ ์ฐจ๋‹จ์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ ์ธ์ˆ˜์—๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

JDK 1.8์˜ java.util.Optional์€ ํ•„์ˆ˜ ์†์„ฑ(์˜ˆ: @RequestParam, @RequestHeader ๋“ฑ)์ด ์žˆ๋Š” ์ฃผ์„๊ณผ ํ•จ๊ป˜ ๋ฉ”์„œ๋“œ ์ธ์ˆ˜๋กœ ์ง€์›๋˜๋ฉฐ required=false์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

Controller method argument Description
ServerWebExchange ์ „์ฒด ServerWebExchange์— ๋Œ€ํ•œ ์•ก์„ธ์Šค — HTTP ์š”์ฒญ ๋ฐ ์‘๋‹ต, ์š”์ฒญ ๋ฐ ์„ธ์…˜ ์†์„ฑ, checkNotModified ๋ฉ”์„œ๋“œ ๋“ฑ์— ๋Œ€ํ•œ ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค.
ServerHttpRequest, ServerHttpResponse HTTP ์š”์ฒญ ๋˜๋Š” ์‘๋‹ต์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค.
WebSession ์„ธ์…˜์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํŠน์„ฑ์ด ์ถ”๊ฐ€๋˜์ง€ ์•Š๋Š” ํ•œ ์ƒˆ ์„ธ์…˜์ด ๊ฐ•์ œ๋กœ ์‹œ์ž‘๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ์œ ํ˜•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
java.security.Principal ํ˜„์žฌ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž — ์•Œ๋ ค์ง„ ๊ฒฝ์šฐ ํŠน์ • Principal ๊ตฌํ˜„ ํด๋ž˜์Šค์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ์œ ํ˜•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
org.springframework.http.HttpMethod Requests์˜ HTTP ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
java.util.Locale ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฐ€์žฅ ๊ตฌ์ฒด์ ์ธ ๋กœ์บ˜ ํ™•์ธ์ž, ์ฆ‰ ์‹ค์ œ๋กœ ๊ตฌ์„ฑ๋œ LocaleResolver/LocaleContextResolver์— ์˜ํ•ด ๊ฒฐ์ •๋˜๋Š” ํ˜„์žฌ ์š”์ฒญ ๋กœ์บ˜์ž…๋‹ˆ๋‹ค.
java.util.TimeZone + java.time.ZoneId ํ˜„์žฌ ์š”์ฒญ๊ณผ ์—ฐ๊ฒฐ๋œ ํ‘œ์ค€ ์‹œ๊ฐ„๋Œ€๋กœ, ๋กœ์บ˜์ปจํ…์ŠคํŠธ ํ™•์ธ์ž์— ์˜ํ•ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.
@PathVariable URI ํ…œํ”Œ๋ฆฟ ๋ณ€์ˆ˜์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. URI ํŒจํ„ด์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@MatrixVariable URI ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์˜ ์ด๋ฆ„-๊ฐ’ ์Œ์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ํ–‰๋ ฌ ๋ณ€์ˆ˜๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@RequestParam ์ฟผ๋ฆฌ ๋งค๊ฐœ ๋ณ€์ˆ˜์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ๋งค๊ฐœ ๋ณ€์ˆ˜ ๊ฐ’์€ ์„ ์–ธ๋œ ๋ฉ”์„œ๋“œ ์ธ์ˆ˜ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. @RequestParam ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@RequestParam ์‚ฌ์šฉ์€ ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค(์˜ˆ: ์†์„ฑ ์„ค์ •). ์ด ํ‘œ์˜ ๋’ท๋ถ€๋ถ„์— ์žˆ๋Š” "๋‹ค๋ฅธ ์ธ์ˆ˜"๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@RequestHeader ์š”์ฒญ ํ—ค๋”์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ํ—ค๋” ๊ฐ’์€ ์„ ์–ธ๋œ ๋ฉ”์„œ๋“œ ์ธ์ˆ˜ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. @RequestHeader ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@CookieValue ์ฟ ํ‚ค์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•ด. ์ฟ ํ‚ค ๊ฐ’์€ ์„ ์–ธ๋œ ๋ฉ”์„œ๋“œ ์ธ์ˆ˜ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. @CookieValue ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@RequestBody HTTP ์š”์ฒญ ๋ณธ๋ฌธ์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ๋ณธ๋ฌธ ์ฝ˜ํ…์ธ ๋Š” HttpMessageReader ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ ์–ธ๋œ ๋ฉ”์„œ๋“œ ์ธ์ˆ˜ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ์œ ํ˜•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. @RequestBody ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
HttpEntity<B> ์š”์ฒญ ํ—ค๋” ๋ฐ ๋ณธ๋ฌธ์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ๋ณธ๋ฌธ์€ HttpMessageReader ์ธ์Šคํ„ด์Šค๋กœ ๋ณ€ํ™˜๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ์œ ํ˜•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@RequestPart ๋‹ค์ค‘ ํŒŒํŠธ/์–‘์‹ ๋ฐ์ดํ„ฐ ์š”์ฒญ์˜ ๋ถ€ํ’ˆ์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ์œ ํ˜•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์ค‘ ํŒŒํŠธ ์ฝ˜ํ…์ธ  ๋ฐ ๋‹ค์ค‘ ํŒŒํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
java.util.Map, org.springframework.ui.Model, and org.springframework.ui.ModelMap. HTML ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์‚ฌ์šฉ๋˜๊ณ  ๋ทฐ ๋ Œ๋”๋ง์˜ ์ผ๋ถ€๋กœ ํ…œํ”Œ๋ฆฟ์— ๋…ธ์ถœ๋˜๋Š” ๋ชจ๋ธ์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค.
@ModelAttribute ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ๋ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์ ์šฉ๋œ ๋ชจ๋ธ์˜ ๊ธฐ์กด ํŠน์„ฑ(์—†๋Š” ๊ฒฝ์šฐ ์ธ์Šคํ„ด์Šคํ™”๋จ)์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. @ModelAttribute์™€ ๋ชจ๋ธ ๋ฐ DataBinder๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@ModelAttribute ์‚ฌ์šฉ์€ ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค(์˜ˆ: ํ•ด๋‹น ์†์„ฑ ์„ค์ •). ์ด ํ‘œ์˜ ๋’ท๋ถ€๋ถ„์— ์žˆ๋Š” "๋‹ค๋ฅธ ์ธ์ˆ˜"๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
Errors, BindingResult ๋ช…๋ น ๊ฐ์ฒด์— ๋Œ€ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์˜ ์˜ค๋ฅ˜์— ์•ก์„ธ์Šคํ•˜๊ธฐ ์œ„ํ•ด (์˜ˆ : @ModelAttribute ์ธ์ˆ˜). ์˜ค๋ฅ˜ ๋˜๋Š” BindingResult ์ธ์ˆ˜๋Š” ์œ ํšจ์„ฑ์ด ๊ฒ€์‚ฌ๋œ ๋ฉ”์„œ๋“œ ์ธ์ˆ˜ ๋ฐ”๋กœ ๋’ค์— ์„ ์–ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
SessionStatus + class-level @SessionAttributes ์–‘์‹ ์ฒ˜๋ฆฌ๋ฅผ ์™„๋ฃŒ๋กœ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด ํด๋ž˜์Šค ์ˆ˜์ค€ @SessionAttributes ์ฃผ์„์„ ํ†ตํ•ด ์„ ์–ธ๋œ ์„ธ์…˜ ์†์„ฑ์˜ ์ •๋ฆฌ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ @SessionAttributes ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
UriComponentsBuilder ํ˜„์žฌ ์š”์ฒญ์˜ ํ˜ธ์ŠคํŠธ, ํฌํŠธ, ์ฒด๊ณ„ ๋ฐ ์ปจํ…์ŠคํŠธ ๊ฒฝ๋กœ์— ์ƒ๋Œ€์ ์ธ URL์„ ์ค€๋น„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. URI ๋งํฌ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@SessionAttribute ๋ชจ๋“  ์„ธ์…˜ ์†์„ฑ์— ๋Œ€ํ•œ ์•ก์„ธ์Šค — ํด๋ž˜์Šค ์ˆ˜์ค€ @SessionAttributes ์„ ์–ธ์˜ ๊ฒฐ๊ณผ๋กœ ์„ธ์…˜์— ์ €์žฅ๋œ ๋ชจ๋ธ ์†์„ฑ๊ณผ ๋Œ€์กฐ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ @SessionAttribute ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
@RequestAttribute ์š”์ฒญ ์†์„ฑ์— ์•ก์„ธ์Šคํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ @RequestAttribute ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
Any other argument ๋ฉ”์†Œ๋“œ ์ธ์ˆ˜๊ฐ€ ์œ„์˜ ํ•ญ๋ชฉ๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ๊ธฐ๋ณธ์ ์œผ๋กœ BeanUtils#isSimpleProperty์— ์˜ํ•ด ํŒ๋ณ„๋˜๋Š” ๋‹จ์ˆœ ์œ ํ˜•์ธ ๊ฒฝ์šฐ @RequestParam์œผ๋กœ ํ•ด์„๋˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด @ModelAttribute๋กœ ํ•ด์„๋ฉ๋‹ˆ๋‹ค.

Return Values

See equivalent in the Servlet stack

The following table shows the supported controller method return values. Note that reactive types from libraries such as Reactor, RxJava, or other are generally supported for all return values.


Controller method return value Description
@ResponseBody ๋ฐ˜ํ™˜ ๊ฐ’์€ HttpMessageWriter ์ธ์Šคํ„ด์Šค๋ฅผ ํ†ตํ•ด ์ธ์ฝ”๋”ฉ๋˜๊ณ  ์‘๋‹ต์— ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค. @ResponseBody ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
HttpEntity<B>, ResponseEntity<B> ๋ฐ˜ํ™˜ ๊ฐ’์€ HTTP ํ—ค๋”๋ฅผ ํฌํ•จํ•œ ์ „์ฒด ์‘๋‹ต์„ ์ง€์ •ํ•˜๋ฉฐ ๋ณธ๋ฌธ์€ HttpMessageWriter ์ธ์Šคํ„ด์Šค๋ฅผ ํ†ตํ•ด ์ธ์ฝ”๋”ฉ๋˜์–ด ์‘๋‹ต์— ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค. ResponseEntity๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
HttpHeaders ํ—ค๋”๊ฐ€ ์žˆ๊ณ  ๋ณธ๋ฌธ์ด ์—†๋Š” ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
ErrorResponse ๋ณธ๋ฌธ์— ์„ธ๋ถ€ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ RFC 7807 ์˜ค๋ฅ˜ ์‘๋‹ต์„ ๋ Œ๋”๋งํ•˜๋ ค๋ฉด ์˜ค๋ฅ˜ ์‘๋‹ต์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
ProblemDetail ๋ณธ๋ฌธ์— ์„ธ๋ถ€ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ RFC 7807 ์˜ค๋ฅ˜ ์‘๋‹ต์„ ๋ Œ๋”๋งํ•˜๋ ค๋ฉด ์˜ค๋ฅ˜ ์‘๋‹ต์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
String ViewResolver ์ธ์Šคํ„ด์Šค๋กœ ํ™•์ธ๋˜๊ณ  ์•”์‹œ์  ๋ชจ๋ธ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š” ๋ทฐ ์ด๋ฆ„์œผ๋กœ, ๋ช…๋ น ๊ฐœ์ฒด ๋ฐ @ModelAttribute ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค. ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๋Š” ์•ž์—์„œ ์„ค๋ช…ํ•œ Model ์ธ์ˆ˜๋ฅผ ์„ ์–ธํ•˜์—ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋ชจ๋ธ์„ ๋ณด๊ฐ•ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
View ์•”์‹œ์  ๋ชจ๋ธ๊ณผ ํ•จ๊ป˜ ๋ Œ๋”๋งํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  View ์ธ์Šคํ„ด์Šค๋กœ, ๋ช…๋ น ๊ฐœ์ฒด์™€ @ModelAttribute ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค. ์ฒ˜๋ฆฌ๊ธฐ ๋ฉ”์„œ๋“œ๋Š” ์•ž์—์„œ ์„ค๋ช…ํ•œ Model ์ธ์ˆ˜๋ฅผ ์„ ์–ธํ•˜์—ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋ชจ๋ธ์„ ๋ณด๊ฐ•ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
java.util.Map, org.springframework.ui.Model ์•”์‹œ์  ๋ชจ๋ธ์— ์ถ”๊ฐ€ํ•  ํŠน์„ฑ์œผ๋กœ, ๋ทฐ ์ด๋ฆ„์€ ์š”์ฒญ ๊ฒฝ๋กœ์— ๋”ฐ๋ผ ์•”์‹œ์ ์œผ๋กœ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.
@ModelAttribute ๋ชจ๋ธ์— ์ถ”๊ฐ€ํ•  ํŠน์„ฑ์œผ๋กœ, ๋ทฐ ์ด๋ฆ„์€ ์š”์ฒญ ๊ฒฝ๋กœ์— ๋”ฐ๋ผ ์•”์‹œ์ ์œผ๋กœ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.
@ModelAttribute ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ์ด ํ‘œ์˜ ๋’ท๋ถ€๋ถ„์— ์žˆ๋Š” "๋‹ค๋ฅธ ๋ฐ˜ํ™˜ ๊ฐ’"์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
Rendering ๋ชจ๋ธ ๋ฐ ๋ทฐ ๋ Œ๋”๋ง ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์œ„ํ•œ API์ž…๋‹ˆ๋‹ค.
void ๋น„๋™๊ธฐ์ ์ผ ์ˆ˜ ์žˆ๋Š” void(์˜ˆ: Mono<Void>), ๋ฐ˜ํ™˜ ํ˜•์‹(๋˜๋Š” null ๋ฐ˜ํ™˜ ๊ฐ’)์ด ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋Š” ServerHttpResponse, ServerWebExchange ์ธ์ˆ˜ ๋˜๋Š” @ResponseStatus ์ฃผ์„๋„ ์žˆ๋Š” ๊ฒฝ์šฐ ์‘๋‹ต์„ ์™„์ „ํžˆ ์ฒ˜๋ฆฌํ•œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๊ธ์ •์ ์ธ ETag ๋˜๋Š” lastModified ํƒ€์ž„์Šคํƒฌํ”„ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ ๊ฒฝ์šฐ์—๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.
์œ„์˜ ์–ด๋А ๊ฒƒ๋„ ์ฐธ์ด ์•„๋‹Œ ๊ฒฝ์šฐ void ๋ฐ˜ํ™˜ ์œ ํ˜•์€ REST ์ปจํŠธ๋กค๋Ÿฌ์— ๋Œ€ํ•ด "์‘๋‹ต ๋ณธ๋ฌธ ์—†์Œ" ๋˜๋Š” HTML ์ œ์–ด๊ธฐ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ๋ณด๊ธฐ ์ด๋ฆ„ ์„ ํƒ์„ ๋‚˜ํƒ€๋‚ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
Flux<ServerSentEvent>, Observable<ServerSentEvent>, or other reactive type ์„œ๋ฒ„์—์„œ ๋ณด๋‚ธ ์ด๋ฒคํŠธ๋ฅผ ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค. ServerSentEvent ๋ž˜ํผ๋Š” ๋ฐ์ดํ„ฐ๋งŒ ์จ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๋‹จ, produce ํŠน์„ฑ์„ ํ†ตํ•ด ๋งคํ•‘์—์„œ ํ…์ŠคํŠธ/์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ์„ ์š”์ฒญํ•˜๊ฑฐ๋‚˜ ์„ ์–ธํ•ด์•ผ ํ•จ).
Other return values ๋ฆฌํ„ด ๊ฐ’์ด ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ํ•ด์„๋˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ๋‚จ์•„ ์žˆ๋Š” ๊ฒฝ์šฐ, BeanUtils#isSimpleProperty์— ์˜ํ•ด ํŒ๋ณ„๋œ ๋‹จ์ˆœ ์œ ํ˜•์ด ์•„๋‹Œ ํ•œ, ์ด๋Š” ๋ชจ๋ธ ์†์„ฑ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฉฐ, ์ด ๊ฒฝ์šฐ ํ•ด์„๋˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ๋‚จ์•„ ์žˆ์Šต๋‹ˆ๋‹ค.

Type Conversion

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

String ๊ธฐ๋ฐ˜ ์š”์ฒญ ์ž…๋ ฅ์„ ๋‚˜ํƒ€๋‚ด๋Š” ์ผ๋ถ€ ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ์ธ์ˆ˜(์˜ˆ: @RequestParam, @RequestHeader, @PathVariable, @MatrixVariable ๋ฐ @CookieValue)๋Š” ์ธ์ˆ˜๊ฐ€ String์ด ์•„๋‹Œ ๋‹ค๋ฅธ ๊ฒƒ์œผ๋กœ ์„ ์–ธ๋œ ๊ฒฝ์šฐ ํ˜•์‹ ๋ณ€ํ™˜์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ ํ˜•์‹ ๋ณ€ํ™˜์€ ๊ตฌ์„ฑ๋œ ๋ณ€ํ™˜๊ธฐ์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹จ์ˆœ ํ˜•์‹(์˜ˆ: int, long, Date ๋“ฑ)์ด ์ง€์›๋ฉ๋‹ˆ๋‹ค. ํ˜•์‹ ๋ณ€ํ™˜์€ WebDataBinder(DataBinder ์ฐธ์กฐ) ๋˜๋Š” ํฌ๋งทํŒ…๋ณ€ํ™˜์„œ๋น„์Šค์— ํฌ๋งทํ„ฐ๋ฅผ ๋“ฑ๋กํ•˜์—ฌ(Spring ํ•„๋“œ ํฌ๋งทํŒ… ์ฐธ์กฐ) ์‚ฌ์šฉ์ž ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ˜•์‹ ๋ณ€ํ™˜์˜ ์‹ค์งˆ์ ์ธ ๋ฌธ์ œ๋Š” ๋นˆ String ์†Œ์Šค ๊ฐ’์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฐ’์€ ํ˜•์‹ ๋ณ€ํ™˜์˜ ๊ฒฐ๊ณผ๋กœ null์ด ๋˜๋Š” ๊ฒฝ์šฐ ๋ˆ„๋ฝ๋œ ๊ฒƒ์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” Long, UUID ๋ฐ ๊ธฐํƒ€ ๋Œ€์ƒ ์œ ํ˜•์˜ ๊ฒฝ์šฐ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. null์„ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ ค๋ฉด ์ธ์ˆ˜ ์ฃผ์„์— ํ•„์ˆ˜ ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์ธ์ˆ˜๋ฅผ @Nullable๋กœ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

 

Matrix Variables

See equivalent in the Servlet stack

RFC 3986์—์„œ๋Š” ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์˜ ์ด๋ฆ„-๊ฐ’ ์Œ์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. Spring WebFlux์—์„œ๋Š” Tim Berners-Lee์˜ "์ด์ „ ๊ฒŒ์‹œ๋ฌผ"์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ "ํ–‰๋ ฌ ๋ณ€์ˆ˜"๋ผ๊ณ  ๋ถ€๋ฅด์ง€๋งŒ URI ๊ฒฝ๋กœ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ผ๊ณ ๋„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ–‰๋ ฌ ๋ณ€์ˆ˜๋Š” ๊ฐ ๋ณ€์ˆ˜๋ฅผ ์„ธ๋ฏธ์ฝœ๋ก ์œผ๋กœ ๊ตฌ๋ถ„ํ•˜๊ณ  ์—ฌ๋Ÿฌ ๊ฐ’์„ ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„ํ•˜์—ฌ ๋ชจ๋“  ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์— ๋‚˜ํƒ€๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: "/cars;color=red,green;year=2012"). ๋ฐ˜๋ณต๋˜๋Š” ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ ๊ฐ’์„ ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ: "color=red;color=green;color=blue").

์Šคํ”„๋ง MVC์™€ ๋‹ฌ๋ฆฌ WebFlux์—์„œ๋Š” URL์— ํ–‰๋ ฌ ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๊ฐ€ ์š”์ฒญ ๋งคํ•‘์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฆ‰, URI ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€์ˆ˜ ์ฝ˜ํ…์ธ ๋ฅผ ๋งˆ์Šคํ‚นํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ฆ‰, ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์—์„œ ํ–‰๋ ฌ ๋ณ€์ˆ˜์— ์•ก์„ธ์Šคํ•˜๋ ค๋ฉด ํ–‰๋ ฌ ๋ณ€์ˆ˜๊ฐ€ ์˜ˆ์ƒ๋˜๋Š” ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์— URI ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

// GET /pets/42;q=11;r=22

@GetMapping("/pets/{petId}")
fun findPet(@PathVariable petId: String, @MatrixVariable q: Int) {

    // petId == 42
    // q == 11
}
 

๋ชจ๋“  ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์— ํ–‰๋ ฌ ๋ณ€์ˆ˜๊ฐ€ ํฌํ•จ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด ํ–‰๋ ฌ ๋ณ€์ˆ˜๊ฐ€ ํฌํ•จ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋˜๋Š” ๊ฒฝ๋กœ ๋ณ€์ˆ˜๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
        @MatrixVariable(name = "q", pathVar = "ownerId") q1: Int,
        @MatrixVariable(name = "q", pathVar = "petId") q2: Int) {

    // q1 == 11
    // q2 == 22
}
 

ํ–‰๋ ฌ ๋ณ€์ˆ˜๋ฅผ ์„ ํƒ์ ์œผ๋กœ ์ •์˜ํ•˜๊ณ  ๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด ๊ธฐ๋ณธ๊ฐ’์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// GET /pets/42

@GetMapping("/pets/{petId}")
fun findPet(@MatrixVariable(required = false, defaultValue = "1") q: Int) {

    // q == 1
}
 

๋ชจ๋“  ํ–‰๋ ฌ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด ๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด MultiValueMap์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23

@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
        @MatrixVariable matrixVars: MultiValueMap<String, String>,
        @MatrixVariable(pathVar="petId") petMatrixVars: MultiValueMap<String, String>) {

    // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
    // petMatrixVars: ["q" : 22, "s" : 23]
}
 

@RequestParam

์„œ๋ธ”๋ฆฟ ์Šคํƒ์—์„œ ๋™๋“ฑํ•œ ํ•ญ๋ชฉ ์ฐธ์กฐ

@RequestParam ์ฃผ์„์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ฉ”์„œ๋“œ ์ธ์ˆ˜์— ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ ์กฐ๊ฐ์€ ์‚ฌ์šฉ๋ฒ•์„ ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค.

@Controller
@RequestMapping("/pets")
class EditPetForm {

    // ...

    @GetMapping
    fun setupForm(@RequestParam("petId") petId: Int, model: Model): String { 
        val pet = clinic.loadPet(petId)
        model["pet"] = pet
        return "petForm"
    }

    // ...
}

 

Servlet API "์š”์ฒญ ๋งค๊ฐœ๋ณ€์ˆ˜" ๊ฐœ๋…์€ ์ฟผ๋ฆฌ ๋งค๊ฐœ๋ณ€์ˆ˜, ์–‘์‹ ๋ฐ์ดํ„ฐ ๋ฐ ๋ฉ€ํ‹ฐํŒŒํŠธ๋ฅผ ํ•˜๋‚˜๋กœ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ WebFlux์—์„œ๋Š” ServerWebExchange๋ฅผ ํ†ตํ•ด ๊ฐ๊ฐ์— ๊ฐœ๋ณ„์ ์œผ๋กœ ์•ก์„ธ์Šค๋ฉ๋‹ˆ๋‹ค. @RequestParam ์ฟผ๋ฆฌ ๋งค๊ฐœ ๋ณ€์ˆ˜์—๋งŒ ๋ฐ”์ธ๋”ฉ๋˜์ง€๋งŒ ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ ๋งค๊ฐœ ๋ณ€์ˆ˜, ์–‘์‹ ๋ฐ์ดํ„ฐ ๋ฐ ๋‹ค์ค‘ ํŒŒํŠธ๋ฅผ ๋ช…๋ น ๊ฐœ์ฒด์— ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@RequestParam ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์†Œ๋“œ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ํ•„์š”ํ•˜์ง€๋งŒ @RequestParam์˜ ํ•„์ˆ˜ ํ”Œ๋ž˜๊ทธ๋ฅผ false๋กœ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ java.util.Optional ๋ž˜ํผ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ˆ˜๋ฅผ ์„ ์–ธํ•˜์—ฌ ๋ฉ”์†Œ๋“œ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์„ ํƒ์‚ฌํ•ญ์ž„์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ˜•์‹ ๋ณ€ํ™˜์€ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ ๋งค๊ฐœ ๋ณ€์ˆ˜ ํ˜•์‹์ด String์ด ์•„๋‹Œ ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ํ˜•์‹ ๋ณ€ํ™˜์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

@RequestParam ์ฃผ์„์ด Map<String, String> ๋˜๋Š” MultiValueMap<String, String> ์ธ์ˆ˜์— ์„ ์–ธ๋˜๋ฉด ๋งต์€ ๋ชจ๋“  ์ฟผ๋ฆฌ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์ฑ„์›Œ์ง‘๋‹ˆ๋‹ค.

@RequestParam ์‚ฌ์šฉ์€ ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค(์˜ˆ: ์†์„ฑ ์„ค์ •). ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹จ์ˆœ ๊ฐ’ ์œ ํ˜•(BeanUtils#isSimpleProperty์— ์˜ํ•ด ํŒ๋ณ„๋จ)์ด๊ณ  ๋‹ค๋ฅธ ์ธ์ˆ˜ ํ•ด์„๊ธฐ์— ์˜ํ•ด ํ•ด์„๋˜์ง€ ์•Š๋Š” ์ธ์ˆ˜๋Š” @RequestParam๋กœ ์–ด๋…ธํ…Œ์ด์…˜์ด ์ถ”๊ฐ€๋œ ๊ฒƒ์ฒ˜๋Ÿผ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

 

 

728x90
'Framework/spring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • Spring One 2023 ์„ธ์…˜ ๋ชจ์Œ
  • [spring boot] ์Šคํ”„๋ง๋ถ€ํŠธ 3.1.0 M2 ๋ฆด๋ฆฌ์ฆˆ ๋…ธํŠธ
  • Spring Boot 3.0 Release Notes
  • spring AcceptHeaderLocaleResolver(๋‹ค๊ตญ์–ด) properties ์„ค์ •
์ง€๋‹ˆ๐Ÿงž‍โ™‚๏ธ๐Ÿฅญ
์ง€๋‹ˆ๐Ÿงž‍โ™‚๏ธ๐Ÿฅญ
์ผ์ƒ, ๊ฒŒ์ž„, ๋ง›์ง‘, ์—ฌํ–‰, ๊ฐœ๋ฐœ, IT ๋ธ”๋กœ๊ทธ๐Ÿงž
  • ์ง€๋‹ˆ๐Ÿงž‍โ™‚๏ธ๐Ÿฅญ
    ์š”์ˆ  ๋žจํ”„๐Ÿซ–
    ์ง€๋‹ˆ๐Ÿงž‍โ™‚๏ธ๐Ÿฅญ
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • All (549)
      • Languages (57)
        • JAVA (13)
        • JSP (1)
        • C_C++ (4)
        • Html (3)
        • CSS (1)
        • JavaScript (18)
        • Python (3)
        • Kotlin (13)
        • TypeScript (1)
      • Framework (14)
        • spring (11)
        • jstl (1)
        • angular (2)
      • Tool (28)
        • Eclipse (5)
        • vsCode (3)
        • scrcpy (2)
        • Git (1)
        • IntelliJ (6)
        • Visual-studio (1)
        • UML (1)
        • Gradle (8)
      • DB (6)
        • Oracle (1)
        • MySql (3)
        • Mongo (2)
      • OS (14)
        • Linux (2)
        • Windows (12)
      • Server (8)
        • Tomcat (1)
        • Apache (1)
        • Node.js (6)
      • Programmings (25)
        • Design Pattern (2)
        • Funny (20)
        • Algorithms (3)
      • Cloud (8)
        • Docker (1)
        • Kubernetes (4)
        • Istio (1)
        • ArgoCD (2)
      • IT (5)
        • gRPC (3)
        • RESTful (3)
        • Web UI (5)
        • AI (4)
      • Book (6)
      • TIP (187)
      • Life (53)
      • Game (83)
      • Storage (22)
      • ์‹๋‹น (15)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
    • ์‚ฌ์ดํŠธ๋งต
    • RSS
    • ๊ธฐํƒ€ ์†Œ๋“
  • ๋งํฌ

    • ๊ตฌ๊ธ€
    • ๋„ค์ด๋ฒ„
    • ์ •๋ถ€24
    • Spring Framework ๋ฆด๋ฆฌ์ฆˆ ๋…ธํŠธ
    • Kotlin ๋ฆด๋ฆฌ์ฆˆ ๋…ธํŠธ
    • ์นด์นด์˜ค ์• ๋“œํ•
    • ๋ธ”๋กœ๊ทธ ์‚ฌ์ดํŠธ๋งต
    • ๋ธ”๋กœ๊ทธ RSS
  • ๊ณต์ง€์‚ฌํ•ญ

    • ์•ˆ๋…•ํ•˜์„ธ์š”
  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ํ•œ๋ˆˆ์— ๋ณด๋Š” ์˜ค๋Š˜์˜ ๋‰ด์Šค
    ์˜ค๋Š˜์˜๋‰ด์Šค
    ๋ธŒ๋ฆฌํ•‘
    ๋‰ด์Šค
    ํƒœ๊ตญ
    ๋ฐ์ผ๋ฆฌ ๋‰ด์Šค
    ๋ชฌ์Šคํ„ฐํ—Œํ„ฐ์™€์ผ์ฆˆ
    ํ€˜์ŠคํŠธ
    ๋‰ด์Šค ๋ธŒ๋ฆฌํ•‘
    ๋ชฌ์Šคํ„ฐํ—Œํ„ฐ๋‚˜์šฐ
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.3
์ง€๋‹ˆ๐Ÿงž‍โ™‚๏ธ๐Ÿฅญ
[Spring] Web on Reactive Stack(๋ฐ˜์‘ํ˜• ์Šคํƒ์˜ ์›น)
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”