본문 바로가기
Angular/RxJS

Learn RxJS Basics [Angular를 위한 RxJS이해하기]

by chief_sac 2021. 10. 27.
반응형

 

Angular를 공부하면서 우리는 맞닥드리는 2가지의 스크립트가 있습니다.
하나는 프로그래밍 언어인 타입스크립트 그리고 한가지는 RxJs라고 하는 라이브러리 입니다. 이것이 무엇인지를 알고 Angular라는 프레임워크를 이해하는것은 Spring을 이해하기위해 Java공부를 선행적으로 먼저 하는 것과 유사하다고 저는 믿고있습니다. 그렇기 때문에 타입스크립트와 RxJS의 기초적인 부분을 이해한다면 Angular를 이해하며 들어가기 굉장히 수월할것이라 생각합니다.


우리는 SPA를 접하게 될때 React와 Vue를 흔히 접근합니다 Angular는 러닝커브가 너무 높다며 사람들이 거부하지만 그 이유는 이러한 앞서 사용되는 라이브러리와 프로그래밍언어의 이해가 낮은 상태로 그저 Document를 읽게된다면 생기는 익숙하지 않음이 크다고 봅니다. 물론 저또한 이에 해당한다 느껴 공부를 하고 있습니다. 이를 공부하면서 조금 씩 더 발전한 개발자가 될 수 있을거라 생각하기에 글을 작성합니다.

 

RxJS를 이해하기 위해서 우리는 처음 이 라이브러리가 무엇을 위한 라이브러리인지를 얇게라도 읽어 보는것이 좋습니다.

참고자료
-

 

RxJS Book - Async code

Async code Follow me on Twitter , happy to take your suggestions on topics or improvements /Chris Async code is code that isn't done immediately when being called. setTimeout(() => { console.log('do stuff'); }, 3000 ) 1 2 3 3s seconds in the future the tim

softchris.github.io

 

ReactiveX - Observable

Observable ReactiveX에서 옵저버는 Observable을 구독한다. Obseravable이 배출하는 하나 또는 연속된 항목에 옵저버는 반응한다. 이러한 패턴은 동시성 연산을 가능하게 한다. 그 이유는 Observable이 객체를

reactivex.io

 

Angular 가이드

Angular 가이드

angular.kr

----

 

 

RxJS는 Reactive Extensions For JavaScript 라이브러리이다.
여기서 Reactive Extensions는 ReactiveX 프로젝트에서 출발한 리액티브 프로그래밍을 지원하기위해 확장했다는 뜻이다. ReactiveX 는 Observer 패턴, Iterator 패턴, 함수형 프로그래밍을 조합
하여 제공한다.

 

- 위 내용을 한번쯤 머리에 담고 글을 한번 쭉 읽어보시면 아마 약간의 도움이 될것같습니다.

 

목차

  • RxJS vs Promises
  • What is Observable LifeCycle (옵저버블 생명주기란?)
  • Marble Diagrams(마블 다이어그램이란)
  • Operators(연산자)
    • Filtering
    • RateLimiting
    • Transformation
    • Combination

해당 제목을 순서대로 내용을 정리 하였습니다.

 

 

 

 

 

 

RxJS vs Promises


  • RxJS는 이벤트처리를 위한 강력한 기능을 제공하고있어 다양한 프레임워크와 라이브러리, 그리고 유틸리티를 활용되고있습니다.

  • RxJS에서 큰 비중을 가진 기능 중에 하나인 데이터 스트림을 받아 데이터를 처리하는 Observable라는 기능은 JavaScript에서 제공하는 Promise라는 데이터 처리 기능과 유사합니다.

  • 그렇기 때문에 많은 개발자들은 JS Promise vs RxJS Observable구도를 가지고 다양한 시선에서 논쟁을 하고 있습니다.

  • Observable의 장점
    • 옵저버블은 명시적으로 구독 하기 전까지는 실행되지 않지만, Promise는 객체를 생성할 때 바로 실행됩니다. 데이터를 받는 쪽에서 원하는 시점을 결정하는 경우라면 옵저버블이 더욱 효율적이다.
    • 옵저버블은 데이터를 여러개 보낼 수 있지만, Promise는 하나만 보낼 수 있어, 데이터를 여러번 나눠서 보내는 경우라면 Observable이 더욱 효율적입니다.
    • 옵저버블은 체인과 구독을 구별하지만, Promise의 경우 .then() 하나로 사용합니다. 다른곳에서 가져온 데이터를 복잡하게 가공해야한다면 옵저버블이 더욱 효율적이다.
    • 옵저버블에서 제공하는 subscribe()는 에러도 함께 처리할 수 있습니다. Promise는 .catch()를 사용하는 위치에 따라 에러를 처리하는 로직이 달라져야하지만 옵저버블은 에러 처리 로직을 한군데에 집중 할 수있다.
  • Promise의 장점
    • Promise의 가장 좋은 점은 JS에 내장되어있어 아무것도 추가할 필요가 없고 서브파티 라이브러리가 없습니다. RxJS의 축소된 크기는 45.6kb인데 그만한 가치가 프로젝트를 진행함에 필요한지를 생각 해 보고 사용해야합니다.
  • 결국 어떠한 것을 지정해서 하는것이라기 보다는 상황에 따라 사용하는것을 판단하여 사용해야할 것같다.

 

 

Marble Diagrams(마블 다이어그램이란)


  • 마블다이어그램? 갑자기 조금 생소해보이는 다이어그램같은것이 튀어나오는 것은 상당히 뜬금없지만 RxJS 비동기 프로그래밍을 위한 해당 api를 잘 이해하기위해서는 마블 다이어그램을 이해하여야합니다.
  • 또 알아보기 전에 ReactiveX는 무엇인지를 파악하고 들어가야하는데
ReactiveX는 줄임말입니다 reactive + extension을 나타내는데 한글로 직역하게되면 "반응을 보이는 영향력확대" 라고 직역하게됩니다. 이는 옵저버블 스트림으로 비동기 프로그래밍을 하기위한 api로서 RxJS의 부모와 같은 개념입니다.
  • 위와같은 부분을 지원하게 될때 즉 비동기를위한 옵저버블스트림을 이해할때 필요한 Observable이 어떠한 전환을 했을때 그것을 어떤형식으로 표현하는지를 보여주는 것입니다.
  • Marble은 구슬이라는 것을 의미하는데 즉 마블다이어그램 즉"구슬도표"를 이용하여 보여준다는 것입니다.
  •  
  • 상자안의 연산자를 이용하여 적용했을때 생기는 변화를 사람이 보기쉽게 한 표라고 모시면 편할것같습니다.

 

 

What is Observable LifeCycle (옵저버블 생명주기란?)


  • Observable생성: Observable을 생성하려면 먼저 RxJS에서 Observable을 가져옵니다.
import { Observable } from "rxjs";

const observable = new Observable<number>(observer => {
  
});
  • Observable 구독: subscribe()를 이용해 스트림이 전달 될때마다 지정된 옵저버블 처리 로직을 실행 할 수있습니다.
observable.subscribe(value => {
  // observer handles notifications
});
  • Observable 취소: 옵저버블은 구독을 취소할 수 있습니다.
const subscription = observable.subscribe(() => {
  // observer handles notifications
});

subscription.unsubscribe();
  • 에러처리: 옵저버블에서 구독자의 에러 핸들러 함수에서 에러를 처리할 수 있으며 구독자가 자동으로 구독을 해지합니다.
observable.subscribe(() => {
  throw new Error('my error');
});

 

 

 

Operators(연산자)


  • RxJs의 연산자는 정말 어마무시하게 많다. 심지어 카테고리별로 나누어져서 세부적으로 들어가면 더더욱 많다 이를 외운다면 정말정말 좋겠지만 이것만 외우고있는것은 효율성이 극악의 가까운 저는 http://reactivex.io/documentation/ko/operators.html#categorized 해당링크를 통해 확인 하면서 하면될것같다.
  • 하지만 그중에서 정말 필요한 연산자는 숙지해두는것이 옵저버블을 사용할때 굉장히 크고 좋은 부분이라고 믿고있다 그렇기 때문에 4가지 정도를 정리해보려고한다.
Filtering

: 옵저버블 소스에서 선택적으로 항목을 배출하는 연산자들

  • filter: JS에서 Array.prototype.filter와 비슷한 함수라고 보면된다. 용도는 이름에서 알수있듯이 특정 조건을 만족하는 데이터를 걸러서 리턴한다.
    import { range, pipe } from 'rxjs'
    import { filter } from 'rxjs/operators'
    
    range(1, 10)
      .pipe(filter(x => x % 5 === 0))
      .subscribe(x => console.log(x))  // 5와 10이 출력된다.​

 

  • first : 처음값 1개만 발행 하고 더이상 값을 발행하지 않고 complete함수를 호출하는 연산자이다.
    import { range, pipe } from 'rxjs'
    import { first} from 'rxjs/operators'
    
    range(1, 10)
    	.pipe(first((x, i) => i === 3 || x === 7))
    	.subscribe(x => console.log(x)) // 4출력​

 

  • last: Observable의 마지막 항목 1개만 배출한다.
    import { range, pipe } from 'rxjs'
    import { last} from 'rxjs/operators'
    
    range(1, 10)
    	.pipe(last((x, i) => i === 3 || x === 7))
    	.subscribe(x => console.log(x)) // 7출력​

 

(이 이후는 연산자명과 설명만 입력하겠습니다.)

 

 

  • Debounce : Observable의 시간 흐름이 지속되는 상태에서 다른 항목들은 배출하지 않고 특정시간에 그 시점에 존재하는 항목하나를 옵저버블로부터 배출한다.
    사용형식: debounce(i => timer(200 * i)))
  • Distinct : 옵저버블이 배출되는 항목들 중에 중복되는 항목을 제거한 후 보여준다. 즉, 내부에서 자체 구현한 Set 자료구조로 이미 발행된 값을 중복 없이 저장했다가 같은 값을 전달받으면 발행하지 않는다.
    사용형식:.pipe(distinct(obj => obj.id))
  • ElementAt : 옵저버블의 n번째 항목만 배출한다.
    사용형식 : ElementAt(n)
  • IgnoreElements : 항목들을 배출하지는 않고 종료 알림만 보낸다.
    사용형식: subject.IgnoreElements()
  • Sample : 특정 시간간격으로 최그네 옵저버블이 배출한 항목을 배출한다.
    사용형식 : sample(clicks)
  • Skip : 옵저버블이 배출한 처음 n개의 항목들을 숨긴다.
    사용형식 : skip(2)
  • SkipLast : 옵저버블이 배출한 처음 n개의 항목들만 배출한다.
    사용형식 : skipLast(2)
  • Take : 옵저버블이 배출한 처음 n개의 항목들만 배출한다.
    사용형식 : take(2)
  • TakeLast : 옵저버블이 배출한 마지막 n개의 항목들만 배출한다.
    사용형식 : takeLast(2)

 

RateLimiting

import 'rxjs-observable';

    sourceObservable
        .rateLimit(5, 1000)
        .subscribe(event => doSomething(event));
  • RxJs에 대한 속도 제한 연산자이며, 슬라이딩 윈도우 할당량이 있는 API를 호출할때 유용하게 사용된다.
  • 또한 토큰 버킷 알고리즘이라는 것을 사용하는데 이는 지정된 양의 토큰이있습니다. 값이 방출되면 해당 토큰을 사용하고 그 토큰은 잠시 기다린 후 재생성됩니다.

 

Transformation


  • Buffer : Observable로부터 정기적으로 항목들을 수집하고 묶음으로 만든 후에 묶음 안에 있는 항목들을 한번에 하나씩 배출하지 않고 수집된 묶음 단위로 배출한다
    사용형식 : buffer(interval(400))
  • FlatMap : 하나의 Observable이 발행하는 항목들을 여러개의 Observable로 변환하고, 항목들의 배출을 차례차례 줄 세워 하나의 Observable로 전달한다
    사용형식 : flatMap(value => value)
  • GroupBy : 원본 Observable이 배출하는 항목들을 키(Key) 별로 묶은 후 Observable에 담는다. 이렇게 키 별로 만들어진 Observable들은 자기가 담고 있는 묶음의 항목들을 배출한다
    사용형식 : groupBy(x => x % 2 === 0)
  • Map : Observable이 배출한 항목에 함수를 적용한다
    사용형식 : map(x => x * 10)
  • Scan : Observable이 배출한 항목에 연속적으로 함수를 적용하고 실행한 후 성공적으로 실행된 함수의 리턴 값을 발행한다
    사용형식 : scan((x,y) ⇒ x+y)
  • Window : 정기적으로 Observable의 항목들을 더 작은 단위의 Observable 윈도우로 나눈 후에, 한번에 하나씩 항목들을 발행하는 대신 작게 나눠진 윈도우 단위로 항목들을 배출한다
    사용형식 : window(interval(350))

 

Combination


  • And/ Then / When : 두개 의 옵저버블들이 배출한 항목들을 'Pattern'과 'Plan' 중계자를 이용해서 결합한다.
    사용형식 : and() , when() , then({(x,y) ⇒ z}, ...)
  • CombineLatest : 두 개의 Observable 중 하나가 항목을 배출할 때 배출된 마지막 항목과 다른 한 Observable이 배출한 항목을 결합한 후 함수를 적용하여 실행 후 실행된 결과를 배출한다
    사용형식 : combineLatest((x,y) ⇒ "" + x + y)
  • Join : A Observable과 B Observable이 배출한 항목들을 결합하는데, 이때 B Observable은 배출한 항목이 타임 윈도우를 가지고 있고 이 타임 윈도우가 열린 동안 A Observable은 항목의 배출을 계속한다. Join 연산자는 B Observable의 항목을 배출하고 배출된 항목은 타임 윈도우를 시작시킨다. 타임 윈도우가 열려 있는 동안 A Observable은 자신의 항목들을 계속 배출하여 이 두 항목들을 결합한다
    사용형식 : join({(x,y) ⇒ z}, ...)
  • Merge : 복수 개의 Observable들이 배출하는 항목들을 머지시켜 하나의 Observable로 만든다
    사용형식 : merge(req1$, req2$, req3$)
  • StartWith : 소스 Observable이 항목을 배출하기 전에 다른 항목들을 앞에 추가한 후 배출한다
    사용형식 : startWith(1)
  • Switch : Observable들을 배출하는 Observable을 싱글 Observable로 변환하다. 변환된 싱글 Observable은 변환 전 소스 Observable들이 배출한 항목들을 배출한다
    사용형식 : switch()
  • Zip : 명시한 함수를 통해 여러 Observable들이 배출한 항목들을 결합하고 함수의 실행 결과를 배출한다
    사용형식 : zip(fn)
반응형