Mono 고급 정리 - zip, fromCallable, block까지 실전 활용 정리

2025. 5. 20. 22:21Back-End/Spring-Boot

반응형

이전 글에서 Mono의 기본 생성 방식과 연산자들 (flatMap, map, switchIfEmpty 등)을 정리했었다.
이번에는 한 단계 더 나아가서, 실무에서 자주 마주치는 상황에서 어떻게 Mono를 응용할 수 있을지 다뤄보자.

 

1. Mono.zip() - 여러 Mono를 병렬로 실행하고 결과를 합칠 때

zip()은 두 개 이상의 Mono를 동시에 실행해서, 결과를 묶어서 처리할 수 있게 해준다.
(모든 Mono가 완료되기 전까지는 실행되지 않음)

예제: 사용자 정보와 포인트 정보를 동시에 가져오기

Mono<User> userMono = userRepository.findById("123");
Mono<Point> pointMono = pointService.getPointByUserId("123");

Mono<Tuple2<User, Point>> zipped = Mono.zip(userMono, pointMono);

zipped.map(tuple -> {
    User user = tuple.getT1();
    Point point = tuple.getT2();
    return new UserResponse(user, point);
});

 

Mono.zip은 성능 측면에서 유리할 때가 많다. (병렬 실행 구조)

 

 

2. Mono.fromCallable() - 블로킹 코드도 Mono로 감싸서 비동기로 처리하기

예를 들어 파일 읽기, 외부 라이브러리 호출처럼 블로킹 작업이 필요할 때 사용한다.

Mono<String> fileContentMono = Mono.fromCallable(() -> {
    // 블로킹 코드
    return Files.readString(Path.of("sample.txt"));
}).subscribeOn(Schedulers.boundedElastic());

반드시 .subscribeOn()과 함께 사용하자.
Reactor는 기본적으로 non-blocking 쓰레드만 사용하므로, 블로킹 코드를 별도 쓰레드로 넘겨야 한다.

 

 

3. subscribe() vs block() - 차이를 명확히 알아두자

이 둘은 Mono의 실행 시점과 관련이 있다.

subscribe()

  • 비동기적으로 실행
  • 값이 오면 콜백으로 처리
  • Spring WebFlux에서는 보통 프레임워크가 자동으로 subscribe 해줌
Mono.just("hello")
    .subscribe(value -> System.out.println("값: " + value));

block()

  • 현재 쓰레드를 블로킹하면서 값이 나올 때까지 기다림
  • 테스트 코드나 초기 부트스트랩 코드에서만 사용하는 것이 좋음
String result = Mono.just("hello").block(); // 즉시 결과가 필요할 때

실서비스 코드에서 block()을 쓰는 순간, WebFlux의 비동기 이점이 사라진다.
꼭 필요한 경우가 아니라면 피하자.

4. Mono를 반환하는 서비스 계층 작성 예시

public Mono<UserDto> getUserWithPoint(String userId) {
    return Mono.zip(
            userRepository.findById(userId),
            pointService.getPointByUserId(userId)
        )
        .map(tuple -> {
            User user = tuple.getT1();
            Point point = tuple.getT2();
            return new UserDto(user, point);
        });
}

이처럼 여러 비동기 작업을 묶어 최종 결과를 만들어내는 게 Mono의 핵심 활용 방식이다.

 

5. 예외 처리 - onErrorResume, onErrorReturn

onErrorResume

Mono.just("value")
    .flatMap(this::dangerousOperation)
    .onErrorResume(e -> {
        log.warn("에러 발생", e);
        return Mono.just("기본값");
    });

onErrorReturn

Mono.just("value")
    .flatMap(this::dangerousOperation)
    .onErrorReturn("기본값");

onErrorResume은 대체 흐름을 Mono로 지정할 수 있어 더 유연하다.

 

마무리

Spring WebFlux에서 Mono는 단순한 비동기 데이터 컨테이너를 넘어서,
다양한 상황에 맞게 데이터를 결합하고, 처리 흐름을 제어하는 핵심 도구가 된다.

  • 여러 비동기 결과를 묶을 땐 zip()
  • 블로킹 코드는 fromCallable()로 감싸고 subscribeOn() 붙이기
  • 절대 block() 남용하지 않기
반응형