Post

Spring WebFlux map vs flatMap

Spring WebFlux에서 Reactive Stream 내 데이터 변환에는 map 메서드와 flatMap 메서드가 사용됩니다.

이번 시간에는 데이터 변환에 사용되는 두 메서드의 차이에 대해 정리해보고자 합니다.



map

map 메서드는 데이터를 동기적으로 변환하기 위해 사용됩니다.

Stream, Optional과 비슷하게 내부의 데이터만 변환합니다.

1
2
3
4
5
6
7
8
Optional<String> optional = Optional.of("hello")
  .map(String::toUpperCase);

Stream<String> stream = Stream.of("hello")
  .map(String::toUpperCase);

Mono<String> mono = Mono.just("hello")
  .map(String::toUpperCase)


당연하게도, 반환 값이 래핑되지 않은 단순 값이기 때문에 MonoFlux를 반환할 수 없어 비동기 작업이 불가능합니다.

1
2
3
4
5
Mono<User> mono = Mono.just("ilhwanee")
  .map(userId -> {
      Mono<User> userMono = userRepository.findUserByUserId(userId);
      return userMono;; // Error!
  });

따라서 map안에서 Mono 및 Flux를 사용하는 경우 block 메서드를 사용해서 값을 꺼내야 하므로 Reactive Stream의 논블로킹 특성이 깨지게 되어 성능 저하의 원인이 됩니다.


이러한 특성으로 인해 map 메서드는 다음과 같은 상황에서 사용됩니다.

  • 데이터 변환
  • 단순한 계산 작업


그렇다면 flatMap은 어떨까요?



flatMap

flatMap은 Reactive Stream 내 데이터를 비동기적으로 변환합니다.

Flux에서 데이터 개수 증가가 가능하며, 비슷한 메서드인 flatMapMany를 사용하면 Mono -> Flux로 변환 가능합니다.

Flux -> Mono 변환은 reduce, next, collectList 등 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// flatMap 사용
Mono<Order> mono = Mono.just(uuid)
  .flatMap(id -> {
      Mono<Order> orderMono = orderRepository.findyOrderById(id);
      return orderMono;
  });

// Flux 데이터 개수 증가
  Flux<String> flux2 = Flux.just("hi", "bye")
  .flatMap(value -> Flux.fromArray(value.split("")));

  flux.subscribe(System.out::println);
/** 출력
 * h
 * i
 * b
 * y
 * e
 */

// Mono에서 Flux로 변환
Flux<Post> flux1 = Mono.just("ilhwanee")
  .flatMapMany(author -> {
      Flux<Post> postFlux = postRepository.findPostsByAuthor(author);
      return postFlux;
  });


flatMapmap과 달리 비동기 호출이 가능하기 때문에,

  • 비동기 호출
  • 외부 서비스 API 호출
  • DB 작업

등에 사용됩니다.


단, 기존 스트림에서 flatMap의 호출 순서는 보장되지만 비동기 작업이 먼저 처리되면 작업이 완료된 순서대로 소비됩니다.

만약 소비되는 순서도 중요하다면 flatMap 대신 flatMapSequential을 사용해야 합니다.

  • flatMapSequential : 비동기로 처리하되, 결과를 입력 순서대로 정렬해줌

만약 실행 흐름도 중요하면 직렬 처리하는 concatMap 사용



결론

Reactive Redis, WebClient 사용 등 비동기 호출하여 반환 값이 MonoFlux인 경우 flatMap을,

내부 데이터만 변환하는 동기적으로 해결 가능한 경우는 map을 사용하면 됩니다!



This post is licensed under CC BY 4.0 by the author.