--- title: WebClient vs RestClient — reactive blocking 차이와 baseline 선택 source_type: official-doc status: raw confidence: high url: https://docs.spring.io/spring-framework/reference/web/webflux-webclient.html archive_url: related_branches: [feature-outbound-http-client-baseline] related_projects: [ca-tmpl] tags: [ca-outbound-http, spring, webclient, restclient, reactive, blocking] created: 2026-05-22 last_reviewed: 2026-05-27 --- # WebClient vs RestClient — reactive blocking 차이와 baseline 선택 > Layer: `raw/official-docs/` — Spring Framework reference "WebClient" 페이지 + "Synchronous Use" 하위 페이지 + REST Clients 페이지의 비교 발췌. ca-tmpl outbound baseline 에서 WebClient 가 baseline 이 아닌 이유의 근거. ## Parent / 활용 branch (필수) | Branch | 이 자료가 정당화하는 결정 | |---|---| | [[raw/branch-notes/feature-outbound-http-client-baseline]] | outbound baseline 으로 RestClient (sync) 채택 + WebClient 는 별도 extension document 결정 근거 | ## 컨텍스트 ca-tmpl outbound baseline 에서 **WebClient 가 baseline 이 아닌 이유** 의 근거. 동기 baseline 단순성 vs reactive 도입 비용. ## 출처 / Source - Spring Reference "WebClient": https://docs.spring.io/spring-framework/reference/web/webflux-webclient.html - Spring Reference "WebClient — Synchronous Use": https://docs.spring.io/spring-framework/reference/web/webflux-webclient/client-synchronous.html - Spring Reference "REST Clients" (RestClient 비교 절): https://docs.spring.io/spring-framework/reference/integration/rest-clients.html - 아카이브 URL: (미수집) - 저자 / 조직: Spring Framework (VMware/Broadcom). Rossen Stoyanchev (Spring committer) 발표 자료는 보조 참고 (본 raw 인용 범위 밖). - 발행일: rolling docs (확인 시점 Spring Framework 7.0.x) - 마지막 확인일: 2026-05-27 ## 핵심 인용 / Key quotes (verbatim) > [§WebClient — Introduction] "Spring WebFlux includes a client to perform HTTP requests. `WebClient` has a functional, fluent API based on Reactor (see [Reactive Libraries]) which enables declarative composition of asynchronous logic without the need to deal with threads or concurrency. It is fully non-blocking, supports streaming, and relies on the same codecs that are also used to encode and decode request and response content on the server side." > [§WebClient — HTTP client libraries] "`WebClient` needs an HTTP client library to perform requests. There is built-in support for the following:" / "Reactor Netty" > [§WebClient — Synchronous Use] "`WebClient` can be used in synchronous style by blocking at the end for the result:" > [§WebClient — Synchronous Use — example] "Person person = client.get().uri(\"/person/{id}\", i).retrieve().bodyToMono(Person.class).block();" > [§WebClient — Synchronous Use] "However if multiple calls need to be made, it's more efficient to avoid blocking on each response individually, and instead wait for the combined result" > [§WebClient — Synchronous Use] "With `Flux` or `Mono`, you should never have to block in a Spring MVC or Spring WebFlux controller. Simply return the resulting reactive type from the controller method. The same principle apply to Kotlin Coroutines and Spring WebFlux, just use suspending function or return `Flow` in your controller method." > [§REST Clients — Choices for making calls to REST endpoints] "RestClient — synchronous client with a fluent API" / "WebClient — non-blocking, reactive client with fluent API" ## Claims Extracted / 추출된 주장 | Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove | |---|---|---|---|---|---| | WEBCLIENT-C1 | WebClient 는 Reactor 기반 functional/fluent API 를 제공하며 **fully non-blocking** + streaming 지원, server-side 와 동일한 codec 재사용 | [§WebClient — Introduction] "WebClient has a functional, fluent API based on Reactor ... It is fully non-blocking, supports streaming, and relies on the same codecs that are also used to encode and decode request and response content on the server side." | `official-vendor-doc` | Spring WebFlux 환경에서 outbound HTTP | "non-blocking" 이 모든 downstream library 에서 동일하게 보장된다는 뜻 아님 — HTTP client library 선택에 의존 | | WEBCLIENT-C2 | WebClient 는 별도 HTTP client library 가 필요하며 **Reactor Netty** 가 built-in 지원 1차 옵션 | [§WebClient — HTTP client libraries] "WebClient needs an HTTP client library to perform requests. There is built-in support for the following:" + "Reactor Netty" | `official-vendor-doc` | WebClient default 사용 환경 | Reactor Netty 만 지원된다는 뜻 아님 — Jetty/HttpComponents/JDK HttpClient 등 다른 옵션이 별도 절에 명시 (본 인용 외 항목은 본 raw 범위 밖) | | WEBCLIENT-C3 | WebClient 는 `block()` 으로 결과를 받아 **synchronous style 사용 가능** | [§WebClient — Synchronous Use] "WebClient can be used in synchronous style by blocking at the end for the result:" + "Person person = client.get().uri(\"/person/{id}\", i).retrieve().bodyToMono(Person.class).block();" | `official-vendor-doc` | WebClient 를 동기 코드에서 호출하는 경우 | "권장한다" 는 뜻은 아님 — 가능성 명시일 뿐 | | WEBCLIENT-C4 | 다수 호출 시 각 응답마다 blocking 하지 않고 **combined result 를 기다리는 것이 효율적** | [§WebClient — Synchronous Use] "However if multiple calls need to be made, it's more efficient to avoid blocking on each response individually, and instead wait for the combined result" | `official-vendor-doc` | 여러 outbound call 의 직렬 처리 비교 | 단일 호출의 `block()` 자체가 deadlock 을 일으킨다는 의미 아님 — 본 인용은 효율성 논의 | | WEBCLIENT-C5 | Spring MVC / WebFlux **controller 내부에서는 절대 block 하지 말고** reactive type (`Flux`/`Mono`/`Flow`/suspending function) 을 그대로 return | [§WebClient — Synchronous Use] "With Flux or Mono, you should never have to block in a Spring MVC or Spring WebFlux controller. Simply return the resulting reactive type from the controller method." | `official-vendor-doc` | Spring MVC 또는 WebFlux controller method 작성 | "controller 외 모든 곳에서 block 해도 된다" 는 뜻 아님 — controller scope 의 명시적 권고 | | WEBCLIENT-C6 | Spring Framework 가 공식 REST client 선택지를 RestClient (sync, fluent) 와 WebClient (non-blocking, reactive, fluent) **2개로 명시** | [§REST Clients — Choices] "RestClient — synchronous client with a fluent API" + "WebClient — non-blocking, reactive client with fluent API" | `official-vendor-doc` | Spring 신규 코드의 client 선택 | "RestClient 가 항상 우월" 또는 "WebClient 가 항상 우월" 의 뜻 아님 — runtime model 에 따른 선택 | | WEBCLIENT-C7 | 본 raw 인용 시점 (2026-05-27) 의 WebClient 페이지 + Synchronous Use 페이지는 **"blocking on a non-blocking thread can deadlock the entire event loop"** 라는 정확한 문구 또는 "Reactor scheduler is shared with WebFlux" 의 정확한 경고문을 **포함하지 않음** | (negative finding — WebFetch 2회 모두 해당 문구 미발견) | `needs-confirmation` | reactor scheduler / event loop deadlock 경고 인용 시 | 이 부정 확인은 deadlock 위험이 **거짓** 이라는 뜻이 아니라 **본 페이지에서는 미명시** 라는 뜻 — 별도 출처 (Project Reactor 문서 / Rossen Stoyanchev 발표) 보강 필요 | ## Usage Boundaries / 적용 경계 - **이 자료가 직접 증명하는 것**: - `WEBCLIENT-C1` ~ `C6`: WebClient 정의, Reactor Netty built-in, `block()` 으로 sync 사용 가능, 다수 호출의 combined result 효율, controller 내 block 금지, Spring 공식 2-선택지 - **이 자료가 증명하지 않는 것**: - "Reactor scheduler used by WebClient is shared with WebFlux, and blocking on a non-blocking thread can deadlock the entire event loop" 의 **정확한 문구** — 본 WebFetch 에서 미확인 (`WEBCLIENT-C7`). 별도 출처 (Reactor 공식 문서 또는 Spring blog) 보강 필요 - "reactor.netty.ioWorkerCount = max(1, availableProcessors())" 의 정확한 default — 본 페이지 미언급, Reactor Netty 공식 문서 별도 확인 필요 - "RestClient is the recommended choice if your application primarily uses synchronous HTTP requests" 의 정확한 문구 — 본 WebFetch 에서 REST Clients 페이지 일부만 확인됨. 명시적 권장 문구는 별도 검증 필요 (Javadoc NOTE 의 "more modern API" 가 동등한 의미는 `RESTCLIENT-C4` 에서 확인됨) - thread-per-request vs event-loop 의 성능 trade-off 수치 - **내 프로젝트에 적용하려면 추가 확인이 필요한 것**: - ca-tmpl 의 "WebClient extension doc" 범위 — SSE / 높은 fan-out / WebFlux runtime 의 정확한 경계 - WebClient `.timeout(Duration)` operator vs RestClient `requestFactory` timeout 의 정확한 API 차이 - `.onStatus()` vs `ResponseErrorHandler` error mapping 패턴 차이 ## 메모 / Notes (내 프로젝트 해석) > 본 섹션은 자료 직접 인용 아님. ca-tmpl 결정 컨텍스트 해석. - 두 client 의 trade-off (정확한 deadlock 수치/이론은 needs-confirmation): | 항목 | RestClient | WebClient | |---|---|---| | 모델 | sync, thread-per-request (해석) | async, event-loop (`WEBCLIENT-C1`) | | API | fluent (RestClient-style) | fluent (Reactor) (`WEBCLIENT-C1`) | | 사용 환경 | Spring MVC (해석) | Spring WebFlux 또는 Spring MVC (`WEBCLIENT-C3`) | | MVC 에서의 비용 | 0 (자연스러움) (해석) | thread bridging + Reactor 학습 비용 (해석, `WEBCLIENT-C5` 의 controller block 금지가 부분 근거) | | WebFlux 에서의 비용 | thread block 위험 (해석, needs-confirmation) | 0 (자연스러움) (해석) | | streaming | 제한적 (해석) | 1차 시민 (Flux) (`WEBCLIENT-C1`) | | timeout | requestFactory (해석, 별도 출처 필요) | `.timeout(Duration)` operator (해석, 별도 출처 필요) | | error mapping | `ResponseErrorHandler` (해석, 별도 출처 필요) | `.onStatus()` (해석, 별도 출처 필요) | - ca-tmpl 결정 정당성 (해석): - skeleton 의 baseline runtime 은 Spring MVC (blocking) — WebClient 를 baseline 에 두면 매 호출마다 `block()` 또는 thread bridging 필요. 이는 reactor event loop blocking risk 가정 (`WEBCLIENT-C7` 의 deadlock 문구는 needs-confirmation). - WebFlux runtime 이 필요한 use case 가 등장하면 **extension document** 로 WebClient 사용 가능 — baseline 변경 없이. - **반례 케이스** (WebClient 가 RestClient 보다 정당한 시점): - SSE / Server-Sent Events 클라이언트 (`WEBCLIENT-C1` 의 streaming 1차 시민 결합). - 매우 높은 concurrent outbound fan-out (예: dashboard aggregator) — thread-per-request 한계 (해석). - 이미 WebFlux 로 runtime 이 결정된 서비스 (`WEBCLIENT-C5` 의 controller 권고와 결합). - ca-tmpl "WebClient 는 별도 extension doc" 는 두 환경을 분리하는 합리적 line. - 시사점: WebClient 는 더 powerful 이 아니라 **다른 runtime model** (`WEBCLIENT-C6` 의 공식 2-선택지가 부분 근거). baseline 은 단일 model 이어야 운영 멘탈모델이 깨지지 않음 (해석). ## Related / 관련 - 같은 주제 다른 official-doc: - [[raw/official-docs/outbound-spring-restclient-baseline]] — RestClient baseline 채택 근거 - [[raw/official-docs/outbound-openfeign-declarative-client]] — declarative 대안 배제 근거 - [[raw/official-docs/outbound-resilience4j-vs-spring-retry]] — retry/circuit breaker library 비교 - 같은 주제 company-tech-blog: - [[raw/company-tech-blogs/outbound-stripe-rate-limit-retry-engineering]] — Stripe retry/backoff/idempotency 사례 - 인용하는 branch: - [[raw/branch-notes/feature-outbound-http-client-baseline]] - 인용하는 wiki: (미작성)