프린세스 다이어리

[RxJS] Observable 스트림의 흐름을 합치는 3가지 방법 merge(), concat(), switch() 본문

FE

[RxJS] Observable 스트림의 흐름을 합치는 3가지 방법 merge(), concat(), switch()

개발공주 2022. 2. 27. 09:27
728x90
회사 FE 스터디때 내 차례에서 발표한 내용 정리

 

옵저버블을 하나로 합치지 않는다면?

예: 마우스와 터치 동작에 대한 각각의 옵저버블

const mouseUp$ = Rx.Observable.fromEvent(document, 'mouseup');
const touchEnd$ = Rx.Observable.fromEvent(document, 'touchend');

이 2개의 옵저버블을 개별적으로 구독할 수는 있겠지만 한계점이 있다.

  • 동일한 코드일 가능성이 큰 구독 영역이 두 개 존재
  • 둘 사이에 공유되어야 하는 모든 코드는 외부 공유 상태가 필요.
  • 두 개의 구독을 추적해야 해서 잠재적인 메모리 누수 영역 하나 더 생김

→ 둘은 유사한 이벤트를 방출하기 때문에, 단일 코드 블록으로 두 구독을 관리하는 게 낫다.


1. merge()

스트림을 이벤트 방출되는 대로 병합하는 연산자. 두 이벤트에서 OR 연산자를 수행하는 것과 같다.

각 옵저버블의 이벤트가 수신된 순서대로 각 스트림의 이벤트를 연결함.

// 정적 메서드 형식
merge(source1$, source2$, ...)

// 인스턴스 형식
source1$.merge(source2$).merge(...)

 

 

(1) Don't

Rx.Observable.merge(mouseUp$, touchEnd$)
  .do(event => console.log(event.type))
  .map(event => {
    switch(event.type) {
        case 'touchend':
            return {left: event.changedTouches[0].clientX,
                    top: event.changedTouches[0].clientY};
        case 'mouseup':
            return {left: event.clientX,
                    top:  event.clientY};
      }
  })
  .subscribe(obj =>
    console.log(`Left: ${obj.left}, Top: ${obj.top}`));

→ 타입 판별하려고 더 많은 코드를 도입하는 건 병합의 목적에 맞지 않음. if/else, switch문 지양해야.

 

(2) Do

const conformantMouseUp$ = mouseUp$.map(event => ({
  left: event.clientX,
  top: event.clientY
}));

const conformantTouchEnd$ = touchEnd$.map(event => ({
  left: event.changedTouches[0].clientX,
  top: event.changedTouches[0].clientY,
}));

Rx.Observable.merge(conformantMouseUp$, conformantTouchEnd$)
  .subscribe(obj =>
    console.log(`Left: ${obj.left}, Top: ${obj.top}`));

→ 병합 전에 데이터를 미리 정규화하여 merge() 이후 조건부 로직 불필요.

 

(3) Quiz

다음 두 스트림에 대해 merge를 한다면 마지막 구독 시 콘솔에 어떻게 찍힐까?

const source1$ = Rx.Observable.of(1, 2, 3);
const source2$ = Rx.Observable.of("a", "b", "c");
Rx.Observable.merge(source1$, source2$).subscribe(console.log);
  • 답: 1 2 3 a b c
  • 데이터를 동기로 불러오기 때문

2. concat()

한 스트림이 완료된 후 다음 스트림을 구독하는 연산자. 순서를 지켜야됨

const source$ = concat(...streams);

 

두 옵저버블의 데이터는 인터리빙(교차)되지 않고 뒤에 추가됨

 

mouseUp 이벤트는 본질적으로 무한한 스트림.

 

(1) Quiz

마우스업 이벤트를 50개만 취했을 때, 마우스 이벤트가 50회 방출되는 동안의 터치 이벤트는 콘솔에 찍힐까?

Rx.Observable.concat(mouseUp$.take(50), touchEnd$).subscribe((event) =>
  console.log(event.type)
);
  • 답: 누락된다.
  • take로 50개 이벤트만 취해서 mouseUp 이벤트를 유한하게 만들면, 구독자는 50개의 mouseUp 이후에 touchEnd를 보고, 이전의 터치 이벤트는 누락됨

3. switch()

새 observable이 들어오면 이전 observable 구독을 멈추고 새로운 것만 구독하는 연산자

Rx.Observable.fromEvent(document, "click")
  .map((click) => Rx.Observable.range(1, 3))
  .switch()
  .subscribe(console.log);

키 입력 이벤트 처리를 신경쓸 필요가 없음.

  • 네트워크 요청 처리 후 이전 요청이 나중에 도착해서 오래된 데이터가 새로운 데이터 덮어씌우는 거 방지

 

728x90
Comments