카테고리 없음

Spring Webflux 연산자

진열사랑 2025. 5. 12. 17:23

Mono와 Flux의 기본 개념을 다뤘으니, 이제 고급 연산자조합에 대해서도 자세히 설명드릴게요.
이러한 연산자는 비동기 흐름을 변환, 결합, 필터링하는 데 매우 유용합니다.


📚 주요 연산자

1. map vs flatMap

map

  • map은 데이터를 변환할 때 사용합니다.
  • 단일 값을 변환하는 데 적합합니다.
  • 비동기 처리가 필요 없다면 map을 사용합니다.

예시: map

Mono<User> getUserById(String id) {
    return Mono.just(new User(id, "Alice"))
               .map(user -> user.getName().toUpperCase()); // 이름을 대문자로 변환
}

flatMap

  • flatMap은 비동기 작업을 처리할 때 사용합니다.
  • Mono 또는 Flux를 반환하는 함수와 함께 사용해야 합니다. (이때 비동기 흐름을 "평탄화")
  • 다른 MonoFlux를 반환하는 함수를 처리할 때 필요합니다.

예시: flatMap

Mono<User> getUserById(String id) {
    return Mono.just(id)
               .flatMap(this::fetchUserFromDatabase) // 비동기 작업을 처리
               .flatMap(user -> Mono.just(user.getName().toUpperCase())); // 이름 대문자 변환
}

private Mono<User> fetchUserFromDatabase(String id) {
    return Mono.just(new User(id, "Alice"));
}

요약

  • map: 단순 변환 (값이 하나일 때)
  • flatMap: 비동기 작업이나 다른 Mono, Flux로 변환

2. filter

  • filter는 조건에 맞는 값만 통과시키고 나머지는 제거합니다.
  • 값이 조건을 만족하지 않으면 Mono 또는 Flux를 반환합니다.

예시: filter

Mono<User> getUserById(String id) {
    return Mono.just(new User(id, "Alice"))
               .filter(user -> user.getName().equals("Alice")); // 이름이 "Alice"인 경우만 통과
}

3. zip (두 개 이상의 Mono/Flux 결합)

  • 여러 Mono나 Flux를 결합해 하나의 결과로 만듭니다.
  • 순차적인 작업이 아니라, 동시 처리 후 결과를 결합할 때 유용합니다.

예시: zip

Mono<User> userMono = Mono.just(new User("1", "Alice"));
Mono<Order> orderMono = Mono.just(new Order("1", "Laptop"));

Mono<Tuple2<User, Order>> combined = Mono.zip(userMono, orderMono);
combined.subscribe(tuple -> {
    User user = tuple.getT1(); // 첫 번째 Mono (User)
    Order order = tuple.getT2(); // 두 번째 Mono (Order)
});

4. concat, merge, combineLatest

  • concat: 여러 Flux를 순차적으로 연결하여 처리합니다.
  • merge: 여러 Flux를 병렬로 병합합니다.
  • combineLatest: 여러 Flux에서 가장 최신의 값을 결합하여 처리합니다.

예시: concat

Flux<String> flux1 = Flux.just("A", "B", "C");
Flux<String> flux2 = Flux.just("D", "E");

Flux<String> concatenatedFlux = Flux.concat(flux1, flux2);
concatenatedFlux.subscribe(System.out::println); // A B C D E

예시: merge

Flux<String> flux1 = Flux.just("A", "B", "C");
Flux<String> flux2 = Flux.just("D", "E");

Flux<String> mergedFlux = Flux.merge(flux1, flux2);
mergedFlux.subscribe(System.out::println); // A B C D E (순서가 섞일 수 있음)

예시: combineLatest

Flux<String> flux1 = Flux.just("A", "B", "C");
Flux<String> flux2 = Flux.just("X", "Y");

Flux<String> combinedFlux = Flux.combineLatest(flux1, flux2, (s1, s2) -> s1 + s2);
combinedFlux.subscribe(System.out::println); // BX CY

5. collectList & collectMap

  • collectList: 여러 값을 리스트로 수집하여 반환합니다.
  • collectMap: 키와 값을 형식으로 수집합니다.

예시: collectList

Flux<String> flux = Flux.just("A", "B", "C", "D");

Mono<List<String>> collectedList = flux.collectList();
collectedList.subscribe(System.out::println); // [A, B, C, D]

예시: collectMap

Flux<String> flux = Flux.just("A", "B", "C");

Mono<Map<String, Integer>> collectedMap = flux.collectMap(s -> s, String::length);
collectedMap.subscribe(System.out::println); // {A=1, B=1, C=1}

6. onErrorReturn & onErrorResume

  • onErrorReturn: 오류 발생 시 기본 값을 반환합니다.
  • onErrorResume: 오류 발생 시 대체 동작을 정의할 수 있습니다.

예시: onErrorReturn

Mono<String> mono = Mono.error(new RuntimeException("Oops"))
                         .onErrorReturn("Default Value");

mono.subscribe(System.out::println); // "Default Value"

예시: onErrorResume

Mono<String> mono = Mono.error(new RuntimeException("Oops"))
                         .onErrorResume(e -> Mono.just("Error occurred"));

mono.subscribe(System.out::println); // "Error occurred"

🚀 고급 연산자 예시

비동기 API 호출에서 map, flatMap, zip 사용하기

public Mono<Order> getOrderById(String id) {
    return orderService.getOrder(id)
            .flatMap(order -> customerService.getCustomer(order.getCustomerId())
                    .map(customer -> order.setCustomerName(customer.getName())));
}

이 예시에서:

  1. getOrderById 메서드는 Mono<Order>를 반환합니다.
  2. 먼저 orderService.getOrder(id)로 주문을 가져옵니다.
  3. 그런 다음 flatMap을 사용해 customerService.getCustomer()로 고객을 가져옵니다.
  4. 고객 정보를 사용해 주문의 고객 이름을 업데이트합니다.

📈 결론

  • Mono와 Flux는 비동기/논블로킹 처리에 필요한 기본 단위입니다.
  • 다양한 고급 연산자(map, flatMap, filter, zip 등)를 활용하여 복잡한 비동기 흐름을 제어할 수 있습니다.
  • Mono는 0 또는 1개의 값, Flux는 여러 값을 처리하는 데 적합합니다.

이제 비동기 프로그래밍과 관련된 리액티브 스트림의 강력한 연산자들을 잘 활용할 수 있을 거예요!
추가적으로 더 다뤄보고 싶은 예시나 특정 연산자가 있으면 말씀해 주세요.