티스토리 뷰
rx를 사용하며 개발을 하면서 필요한 웬만한 기능들은 rx의 operator로 제공되고 있습니다. 따라서 이 operator 들만 잘 숙지하고 있으면 rx를 사용하며 기능이 부족하다 느끼는 경우는 거의 없을거라 생각합니다.
이번 포스팅에서는 수 많은 rxjs의 operator 중 가장 헷갈리는 map, flatMap, switchMap, concatMap, mergeMap에 대해 정리해보도록 하겠습니다.
map
map operator는 js의 array나 Java의 Stream과 같은 API를 사용해 보셨다면 이미 알고 있을거라 생각됩니다. map operator는 다른 API들과 동일하게 발생하는 요소들을 다른 값으로 변환해 줍니다.
map operator는 아래 예제와 같이 발생하는 값을 다른 값으로 변환, 대체해야 하는 경우에 적합합니다.
import { fromEvent } from 'rxjs';
import { map } from 'rxjs/operators';
const clicks = fromEvent(document, 'click');
const positions = clicks.pipe(map(ev => ev.clientX));
positions.subscribe(x => console.log(x));
flatMap / mergeMap
Java의 Stream이나 js의 array 사용하다보면 flatMap이라는 메소드를 쉽게 볼 수 있습니다. rxjs에서도 마찬가지로 flatMap operator를 제공합니다. 다만 정식 명칭은 flatMap이 아닌 mergeMap이며 flatMap은 mergeMap의 별칭처럼 사용됩니다.
우선 mergeMap은 mergeAll 을 사용해 동작하기 때문에 mergeAll을 먼저 알아야 합니다. 위의 그림처럼 a ~ d까지 방출하는 Observable 과, e ~ g까지 방출하는 Observable을 방출시키는 Observable이 있습니다. 이렇게 만들어지는 다수의 Observable 들을 하나의 Observable로 만들어 줄 수 있는 operator가 mergeAll 입니다.
여기서 주의해야 할 점은 첫번째 Observable의 요소 방출이 다 끝나기도 전에 두 번째 Observable의 요소가 방출되면 Observable 의 순서와 상관 없이 먼저 발생한 요소 순서로 방출됩니다. 따라서 위의 그림에서는 d가 출력되기 전에 e가 먼저 출력되는 것을 볼 수 있습니다.
따라서 mergeMap 은 mergeAll 과 같이 동작합니다. 위 그림은 1, 3, 5를 배출하는 Observable을 mergeMap을 통해 각 요소에 10을 곱해 3번씩 배출하는 Observable로 만들어 줍니다. 여기서 주의할 점은 30이 다 배출되기 전에 50이 같이 배출된다는 점 입니다.
이를 코드로 나타내면 아래와 같습니다.
import { of, interval } from 'rxjs';
import { mergeMap, map, take, toArray } from 'rxjs/operators';
const letters = of(1, 3, 5);
const intervalTime = { 1: 100, 3: 300, 5: 300 };
letters.pipe(
mergeMap(x => interval(intervalTime[x]).pipe(map(i => x * 10), take(3))),
toArray()
).subscribe(x => console.log(x));
// console : [10, 10, 10, 30, 50, 30, 50, 30, 50]
concatMap
그렇다면 요소의 순서가 섞이지 않고 Observable 의 순서대로 요소를 방출하고 싶으면 어떻게 해야 할까요? 그 경우 concatMap 을 사용하면 됩니다. concatMap은 Observable의 순서대로 요소가 방출됩니다.
따라서 mergeMap과 다르게 30을 3번 전부 방출하기 전까지 50이 방출되지 않습니다. 이에대한 간단한 예제는 아래와 같습니다.
import { of, interval } from 'rxjs';
import { concatMap, map, take, toArray } from 'rxjs/operators';
const letters = of(1, 3, 5);
const intervalTime = { 1: 100, 3: 300, 5: 300 };
letters.pipe(
concatMap(x => interval(intervalTime[x]).pipe(map(i => x * 10), take(3))),
toArray()
).subscribe(x => console.log(x));
// console : [10, 10, 10, 30, 30, 30, 50, 50, 50]
switchMap
마지막으로 switchMap에 대해 알아보겠습니다. switchMap은 다소 복잡합니다. 구독중이던 Observable이 끝나기 전에 새로운 Observable을 구독하게 되면 이전에 구독중이던 Observable 의 구독을 취소하고 다음 Observable 구독을 시작합니다.
switchMap의 의 경우 서버에 요청을 했다가 취소하고 다른 요청을 보내야 하는 상황처럼 진행중인 작업을 취소하는 경우에 적합하다고 합니다. switchMap의 간단한 예제는 아래와 같습니다.
import { of, timer, interval } from 'rxjs';
import { switchMap, concatMap, delay, map, take, toArray } from 'rxjs/operators';
const delayTime = { 1: 100, 3: 300, 5: 200 };
const letters = of(1, 3, 5).pipe(concatMap(x => of(x).pipe(delay(delayTime[x]))));
letters.pipe(
switchMap(x => interval(100).pipe(map(_ => x * 10), take(3))),
toArray()
).subscribe(console.log);
// console : [10, 10, 10, 30, 30, 50, 50, 50]
'프로그래밍' 카테고리의 다른 글
Javascript 좋은 테스트 작성법 1-1 (411) | 2019.09.02 |
---|---|
웹사이트 스크래핑 라이브러리 Puppeteer 소개 (390) | 2019.08.15 |
Ubuntu 에서 NodeJS 설치 및 HTTP 서버 구동 (416) | 2017.02.10 |
패키징 구조의 장단점 (403) | 2017.02.09 |
DataBase 회복기법 (376) | 2016.11.16 |