Mono/Flux)Mono/Flux는 2단계 참고@Controller, @RestController(= @Controller + @ResponseBody 메타 애노테이션), @RequestMapping/@GetMapping/@PostMapping 등 spring-web의 동일 애노테이션을 그대로 사용 — 짝이 되는 Web MVC와 매핑 방식은 같다Publisher@ResponseBody(및 @RestController)는 반환값을 HttpMessageWriter 로 인코딩해 응답 본문에 쓴다Mono/Flux를 구독해서, 그 비동기 값을 응답에 써 준다 (컨트롤러가 직접 subscribe하지 않음)
@RestController
class UserController {
@GetMapping("/users/{id}")
Mono<User> get(@PathVariable String id) { ... } // 0..1 → JSON 객체
@GetMapping("/users")
Flux<User> list() { ... } // 0..N → 기본 JSON 배열
@PostMapping("/users")
Mono<Void> create(@RequestBody Mono<User> body) { ... } // 본문 없음 — 완료 신호
}
| 반환 타입 | 의미 |
|---|---|
Mono<T> |
0..1 비동기 값 (단건 조회·저장) |
Flux<T> |
0..N (목록·스트림); text/event-stream이면 SSE |
Mono<Void> / void |
본문 없음 — Mono 완료 시 요청 처리 완료 (ServerWebExchange 인자나 @ResponseStatus가 있으면 “완전 처리”로 간주) |
ResponseEntity<Mono<T>>, Mono<ResponseEntity<T>> |
상태·헤더까지 비동기로 제어 |
HttpMessageReader 로 역직렬화 — MVC와 달리 리액티브 타입 + 완전 논블로킹 읽기/스트리밍을 지원Account가 아니라 Mono<Account>/Flux<Account>로 선언하면 본문을 블로킹 없이 디코드
void handle(@RequestBody Mono<Account> account) { ... } // 본문을 논블로킹으로 디코드
void handle(@Valid @RequestBody Mono<Account> account) { ... } // 검증 실패 → WebExchangeBindException → 400
@Valid를 리액티브 본문에 붙이면 검증 오류가 WebExchangeBindException(BindingResult 포함)으로 나며, onError* 연산자로 다룰 수 있다 — 검증 규칙 자체는 Bean Validation 참고Flux를 반환하면 여러 값이 오는 대로 스트리밍되고 버퍼링하지 않는다 — 큰 시퀀스를 메모리에 쌓지 않기 위함| 미디어 타입 | 렌더링 |
|---|---|
application/json |
값을 모아 JSON 배열로 |
application/x-ndjson |
값마다 개별 write·flush (스트리밍 JSON) |
text/event-stream |
SSE — 값마다 이벤트로 방출 |
text/event-stream을 요청받거나 produces로 선언해야 하며, 이벤트 메타데이터(id/event/retry)를 제어하려면 ServerSentEvent로 감싼다
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
Flux<ServerSentEvent<String>> stream() { ... } // 메타데이터 제어
// 데이터만 필요하면 Flux<String> + produces = "text/event-stream" 으로도 충분