카테고리 없음
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를 반환하는 함수와 함께 사용해야 합니다. (이때 비동기 흐름을 "평탄화")
- 다른 Mono나 Flux를 반환하는 함수를 처리할 때 필요합니다.
예시: 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())));
}
이 예시에서:
- getOrderById 메서드는 Mono<Order>를 반환합니다.
- 먼저 orderService.getOrder(id)로 주문을 가져옵니다.
- 그런 다음 flatMap을 사용해 customerService.getCustomer()로 고객을 가져옵니다.
- 고객 정보를 사용해 주문의 고객 이름을 업데이트합니다.
📈 결론
- Mono와 Flux는 비동기/논블로킹 처리에 필요한 기본 단위입니다.
- 다양한 고급 연산자(map, flatMap, filter, zip 등)를 활용하여 복잡한 비동기 흐름을 제어할 수 있습니다.
- Mono는 0 또는 1개의 값, Flux는 여러 값을 처리하는 데 적합합니다.
이제 비동기 프로그래밍과 관련된 리액티브 스트림의 강력한 연산자들을 잘 활용할 수 있을 거예요!
추가적으로 더 다뤄보고 싶은 예시나 특정 연산자가 있으면 말씀해 주세요.