2025. 5. 20. 22:15ㆍBack-End/Spring-Boot
Spring WebFlux를 처음 시작하면 가장 먼저 마주치는 게 바로 Mono와 Flux다.
이 중 Mono는 "0 또는 1개의 데이터를 비동기적으로 처리하는 Publisher"다.
한 줄 요약: Mono는 '1개의 데이터 또는 아무 것도 없는' 비동기 데이터 그릇이다.
왜 Mono를 써야 할까?
전통적인 Spring MVC에서는 메서드가 값을 리턴하면, 그 값은 동기적으로 바로 반환된다.
하지만 WebFlux는 Reactive Streams 기반으로 동작하기 때문에, Mono를 통해 데이터를 "나중에", "비동기로", "필요할 때" 처리한다.
@GetMapping("/user/{id}")
public Mono<User> getUser(@PathVariable String id) {
return userRepository.findById(id); // Mono<User>
}
위 코드는 사용자 조회 요청에 대해 Mono<User>를 반환한다.
즉, 클라이언트는 응답이 "지금 당장" 오는 게 아니라, "언제든 값이 생기면" 받게 된다.
Mono 사용 시나리오
- DB에서 하나의 row만 조회할 때 (findById)
- 외부 API에서 단일 응답을 받을 때
- 캐시에 값을 조회하거나, 없으면 비어있는 응답을 처리할 때
Mono 생성하기
1. Mono.just(value)
값이 있는 Mono 생성
Mono<String> mono = Mono.just("hello");
2. Mono.empty()
아무 값도 없는 Mono
Mono<String> emptyMono = Mono.empty();
3. Mono.error(Throwable)
에러를 담은 Mono
Mono<String> errorMono = Mono.error(new RuntimeException("Something went wrong"));
자주 쓰는 Mono 연산자
1. map(Function)
Mono 안의 값을 가공할 때 사용
Mono<String> upperMono = Mono.just("hello")
.map(String::toUpperCase); // "HELLO"
2. flatMap(Function)
내부에서 Mono를 반환하는 비동기 호출을 체이닝할 때 사용
Mono<User> userMono = Mono.just("123")
.flatMap(userRepository::findById); // Mono<User>
map은 값을 바꾸고,
flatMap은 다른 Mono를 반환하는 함수를 이어붙일 때 쓴다.
3. switchIfEmpty(Mono)
값이 없을 때 대체 Mono를 반환
userRepository.findById("notfound")
.switchIfEmpty(Mono.just(new User("default")));
4. doOnNext(), doOnError(), doOnSuccess()
사이드 이펙트를 걸어두는 용도 (로깅 등)
Mono.just("data")
.doOnNext(data -> log.info("값: " + data))
.doOnError(e -> log.error("에러 발생", e));
예제 코드 - 사용자 정보 업데이트 API
@PutMapping("/user/{id}")
public Mono<ResponseEntity<String>> updateUser(@PathVariable String id,
@RequestBody Mono<UserUpdateRequest> requestMono) {
return requestMono
.flatMap(request -> userRepository.findById(id)
.switchIfEmpty(Mono.error(new UserNotFoundException()))
.flatMap(user -> {
user.setName(request.getName());
return userRepository.save(user);
}))
.map(updatedUser -> ResponseEntity.ok("업데이트 완료"))
.onErrorResume(e -> {
if (e instanceof UserNotFoundException) {
return Mono.just(ResponseEntity.status(404).body("사용자를 찾을 수 없습니다."));
}
return Mono.just(ResponseEntity.status(500).body("서버 에러"));
});
}
이 코드 흐름은 다음과 같다
- requestMono를 통해 요청 바디를 Mono로 받는다.
- flatMap으로 내부 요청 값을 꺼내고, DB 조회
- 사용자가 없으면 switchIfEmpty로 에러 발생
- 값이 있다면 수정 → 저장
- 성공/에러에 따라 응답 다르게 반환
Mono vs Flux
구분 Mono Flux
| 설명 | 0 또는 1개의 데이터 | 0개 이상의 데이터 |
| 예시 | 사용자 정보 | 사용자 목록, 실시간 데이터 스트림 |
마무리
Mono는 처음에는 어색할 수 있지만, 실제 비동기 처리에서는 매우 강력한 도구다.
flatMap, switchIfEmpty, onErrorResume 같은 연산자만 잘 익혀도 대부분의 흐름은 처리할 수 있다.
핵심은 Mono는 “나중에 값이 생길 수 있음”을 전제로 한다는 것.
즉, 기다리지 말고, 흐름을 이어가라.
'Back-End > Spring-Boot' 카테고리의 다른 글
| 스프링 부트 GC 로그 해석 실전 — Metadata GC Threshold로 Full GC가 길어질 때 (4) | 2025.08.12 |
|---|---|
| Mono 고급 정리 - zip, fromCallable, block까지 실전 활용 정리 (0) | 2025.05.20 |
| DTO ↔ Entity 변환에 대한 고민과 MapStruct (0) | 2025.03.16 |
| JPA에서 JPQL 사용하는 법 (0) | 2025.03.16 |
| 파라미터 포맷 변환(String -> LocalDateTime) (0) | 2025.03.13 |