New Score :0
High Score :0
Run Best
NICE BUSINESS TYPE INDICATOR
3. ๊ธ์ ์ ์น๊ตฌ์๊ฒ ๋น๋ ธ๋๋ฐ ์ค๋์ด ๋์ ์ฃผ๊ธฐ๋ก ํ๋ .. ๊ทธ๋ฐ๋ฐ ์นด๋๊ฐ์ ๋ด์ผํ๋ ๋ ๋ ์ค๋์ธ๋ฐ... ์ด๊ฑธ ์ด์ฉ๋...
4. ์ฐ๋ฆฌ ํ์ฌ๋ ์ค์ํ ์์ฌ ๊ฒฐ์ ์ ํ ๋?
5. ์ด์ฌํ ์ผํ ๋๋ฅผ ์ํ ์ ๋ฌผ์ ์ฃผ๊ณ ์ถ๋ค. ์ด๋ค๊ฒ ์ข์๊น?
6. ์ํ์์ ํฌ์์ํ์ ์ถ์ฒ๋ฐ์๋ค. ์ด๋ค๊ฑธ ๊ฐ์ ํ์ง?
7. ํ์ฌ์์์ ๋๋?
8. ๊ฟ์์ ๊นจ์ด๋๋ 20๋ ์ ์ผ๋ก ๋์๊ฐ๋ค. ๋น์ ์ด ์ ์ผ ๋จผ์ ํ๋์ผ์?
9. ๋ด๊ฐ ์ธ์ฌ ๋ด๋น์๋ผ๋ฉด ์ ๊ท ์ ์ฌ์ ์ฑ์ฉ ์ ์ ์ผ ์ค์ํ๊ฒ ๋ณด๋๊ฒ์?
10. ํ์ฌ์ ์ ๋ง ์ซ์ดํ๋ ๋๋ฃ๊ฐ ์๋ค๋ฉด?
11. ๊ฐ๋ํ ์ง์ ๊ฐ์ฅ์ด ๋์๋ค.. ์๋ ์ ์์ผ ๋ ์ ๋ฌผ์?
12. ํ์ ํ์ฌ ์ถ๊ทผ ์คํ์ผ์?
13.ํ์ฌ ์ฒด์ก๋ํ ํ๋ ๋ ์ด๋ค. ์ค๋ ๋ญํ์ง?
14. ๋์ ์ ๋ฌด ์คํ์ผ์?
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์ ๋ํด ์ค๋ช ํฉ๋๋ค.
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 ์ด๋ํฐ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ ์ค๋๋ค.
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
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
์์ฒญ์ด ํ๋ก์(์: ๋ถํ ๋ถ์ฐ ์ฅ์น)๋ฅผ ํต๊ณผํ๋ฉด ํธ์คํธ, ํฌํธ ๋ฐ ์ฒด๊ณ๊ฐ ๋ณ๊ฒฝ๋ ์ ์์ต๋๋ค. ๋ฐ๋ผ์ ํด๋ผ์ด์ธํธ ๊ด์ ์์ ์ฌ๋ฐ๋ฅธ ํธ์คํธ, ํฌํธ ๋ฐ ์ฒด๊ณ๋ฅผ ๊ฐ๋ฆฌํค๋ ๋งํฌ๋ฅผ ๋ง๋๋ ๊ฒ์ด ์ด๋ ต์ต๋๋ค.
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
์น ์ฒ๋ฆฌ๊ธฐ API์์ WebFilter๋ฅผ ์ฌ์ฉํ์ฌ ํํฐ์ ๋๋จธ์ง ์ฒ๋ฆฌ ์ฒด์ธ๊ณผ ๋์ ์น ์ฒ๋ฆฌ๊ธฐ ์๋ค์ ๊ฐ๋ก์ฑ๊ธฐ ์คํ์ผ ๋ ผ๋ฆฌ๋ฅผ ์ ์ฉํ ์ ์์ต๋๋ค. WebFlux ๊ตฌ์ฑ์ ์ฌ์ฉํ ๋ WebFilter๋ฅผ ๋ฑ๋กํ๋ ๊ฒ์ ์คํ๋ง ๋น์ผ๋ก ์ ์ธํ๊ณ (์ ํ์ ์ผ๋ก) ๋น ์ ์ธ์ @Order ์ฌ์ฉํ๊ฑฐ๋ Ordered๋ฅผ ๊ตฌํํ์ฌ ์ฐ์ ์์๋ฅผ ํํํ๋ ๊ฒ๋ง ํผ ๊ฐ๋จํฉ๋๋ค.
CORS
์คํ๋ง WebFlux๋ ์ปจํธ๋กค๋ฌ์ ์ฃผ์์ ํตํด CORS ๊ตฌ์ฑ์ ๋ํ ์ธ๋ถํ๋ ์ง์์ ์ ๊ณตํฉ๋๋ค. ๊ทธ๋ฌ๋ ์คํ๋ง ๋ณด์๊ณผ ํจ๊ป ์ฌ์ฉํ ๋๋ ์คํ๋ง ๋ณด์์ ํํฐ ์ฒด์ธ๋ณด๋ค ๋จผ์ ์ฃผ๋ฌธํด์ผํ๋ ๋ด์ฅ CorsFilter์ ์์กดํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
์์ธํ ๋ด์ฉ์ CORS ๋ฐ CORS ์น ํํฐ ์น์ ์ ์ฐธ์กฐํ์ธ์.
1.2.4. Exceptions
์น ์ฒ๋ฆฌ๊ธฐ 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
์คํ๋ง ์น ๋ฐ ์คํ๋ง ์ฝ์ด ๋ชจ๋์ 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
์ฌ๋ฌ ํจํด์ด 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
@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
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
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๋ก ์ด๋ ธํ ์ด์ ์ด ์ถ๊ฐ๋ ๊ฒ์ฒ๋ผ ์ฒ๋ฆฌ๋ฉ๋๋ค.