WebFlux로 “외부 HTTP 호출 → 응답 수신”을 할 때
스레드를 잡아두지 않고(Non-blocking), 데이터가 도착하는 이벤트에 반응해서 다음 단계로 흘려보내는 파이프라인으로 동작한다.
이 구조로 대기 기간이 없는 Nonblocking 방식의 통신을 지원한다.
Webflux 구성요소를 정리해보면 다음과 같다.
호출 주체: WebClient
반환 타입: Mono / Flux (응답을 담는 그릇)
외부 응답을 “값”으로 받는 게 아니라 “스트림”으로 받는다.
Mono
Flux
요청 구성에 필요한 것 준비
(1) URL / QueryParam
.uri("https://...") 또는 .uri(uriBuilder -> uriBuilder.path("/x").queryParam("a", 1).build())
(2) Method
.get() / .post() / .put() / .delete()
(3) Headers (인증/추적/콘텐츠 협상)
Authorization(Bearer/API-Key), Content-Type / Accept, Trace/Correlation-Id (로그 추적용)
(4) Body (POST/PUT)
JSON: .bodyValue(reqDto), Form: .body(BodyInserters.fromFormData(...)), 파일 멀티파트: MultipartBodyBuilder
응답 수신에 필요한 것들
(1) 상태코드 처리: retrieve() + onStatus(...)
(2) 바디 파싱: bodyToMono(...) / bodyToFlux(...)
(3) 타임아웃/리트라이/서킷브레이커 같은 “복원력”
Thread/Blocking 규칙: “절대 막지 말기”
WebFlux 에서 가장 중요한 단 하나:
.block(), .join(), JDBC 같은 블로킹 호출을 같은 체인에서 섞으면 WebFlux의 장점이 깨짐
왜냐면 WebFlux는 이벤트 루프 기반이라, 블로킹이 들어오면 이벤트 루프 스레드를 점유해버려서 전체 처리량이 떨어진다.
가능하면 R2DBC(DB), reactive driver를 쓰는 게 정석
“내 서비스”에서 컨트롤러까지 이어지는 형태
외부 호출 결과를 WebFlux 컨트롤러가 그대로 리턴할 때의 이상적인 흐름:
Controller는 Mono
Service는 WebClient로 외부 호출하고 Mono로 반환
중간에 매핑/검증/에러처리/로깅을 map/flatMap/onErrorMap/doOnNext 등으로 체이닝
즉, “받은 다음 처리”가 아니라 “받으면 처리하도록 선언”하는 스타일.
필수인 부가 요소
외부 호출은 “호출 성공” 뒤에 필요할 수 있는 요소
로깅/트레이싱: request-id, response time, status 기록
메트릭: 성공/실패/latency
에러 모델링: 외부 에러를 내부 에러코드로 매핑 (예: PG 오류 → 우리 도메인 에러)
논블로킹(Non-blocking)이란?
“기다림 자체가 없다”
스레드가 I/O 대기 때문에 멈추지 않음
“데이터 준비됨” 이벤트가 올 때만 처리
보통 이벤트 루프(Event Loop) 구조
WebFlux에서의 논블로킹
WebFlux는:
요청 N개 = 소수 이벤트 루프 스레드
외부 API 대기 중에도 스레드는 놀지 않음
비동기: 언제 올지 모르는 결과를 Mono/Flux로 표현
논블로킹: 그 결과를 기다리느라 스레드를 묶지 않음
WebFlux: 비동기와 논블로킹을 동시에 강제하는 모델
기존 MVC에서는
요청 1개 = 스레드 1개
외부 API 1초 지연 → 스레드 1초 점유 (대기, blocking)
비동기로 할 수 있지만, 대기가 필요할 수 있어 논블로킹은 아니다.
'IT기술 > spring' 카테고리의 다른 글
| [jpa] @GeneratedValue 전략 살펴보기 (0) | 2024.08.14 |
|---|---|
| [Spring] Spring boot 프로젝트에서 application.properties와 application.yml 파일이 동시에 존재할 (0) | 2024.05.21 |
| [JPA] 1+N 문제, 현상 정리 (0) | 2024.04.01 |
| [Spring] JPA 에러must be manually assigned before calling 'persist()' 에러 해결하기 (0) | 2024.03.27 |
| [JPA] EntityExistsException: detached entity passed to persist 문제 해결하기 (0) | 2024.03.24 |