리액티브 프로그래밍에서 javascript의 비동기 프로그래밍 문제를 해결하는데 도움을 주는 라이브러리
➡️ 이벤트나 데이터들을 시간과 연계되어 sequential하게 처리 할 수 있다
Reactive Programming Paradigm을 Observable 과 Observer 및 Operator 등의 개념을 통해 실현함
과거의 애플리케이션은 서버의 요청을 클라이언트가 처리하고, 클라이언트는 서버의 처리가 끝날때까지 기다렸다가, 처리가 끝나면 서버로부터 응답을 받아 결과를 렌더링하는 동기식 구조가 대부분이었다.
하지만 애플리케이션의 규모가 커지고 복잡해지면서 복잡한 비동기식 요청을 처리해야만하는 시대가 되었고 이런 비동기식 요청을 효과적으로 처리하기 위해 반응형 프로그래밍이 탄생하게 되었다.
(Promise와 call back의 한계로 인해 탄생함)
반응형 프로그래밍은 데이터 스트림과 변화의 전파에 관련된 선언형 프로그래밍 패러다임이다.
해당 패러다임으로 정적(arrays), 동적(event emitter) 데이터 스트림을 쉽게 표현할 수 있으며, 변경된 데이터의 흐름 전달이 용이하다.
데이터 스트림: 키 입력, 마우스, 터치, HTTP 호출 등의 이벤트
변화의 전파: 데이터가 변경 될 때 마다 이벤트를 발생시켜 데이터를 전달하는 것
어떤 방식으로 하는지(How)를 알려준다. = 구현체
무엇(What)과 같은지를 설명한다.
///명령형
const list = [1,2,3,4,5,6,7];
let max = 0;
for(let i = 0; i < list.lenght; i++ ){
if(list[i] > max){
max= list[i]
}
}
//선언형
const list = [1,2,3,4,5,6,7];
let max = Math.max(...list);
Pull과 Push는 두 개의 다른 프로토콜로, 데이터 생산자(Data Producer)와 데이터 소비자(Data Consumer)가 통신하는 방법을 제공한다.
중요 * 여기서 데이터 생산자가 서버, 데이터 소비자가 클라이언트라고 생각하면 안됨.
Rx 내에서 말하는 생산자는 ‘데이터 변경을 수행하는 이벤트’이고, 소비자는 ‘데이터 변경 이벤트를 처리하는 것’을 말한다.

소비자는 데이터 생산자로부터 데이터를 받는 시기를 결정한다. 생산자는 데이터가 소비자에게 언제 전달되는지 알지 못한다.
모든 JavaScript 함수는 Pull 시스템이다.
함수는 데이터 생성자이며, 함수를 호출하는 코드는 호출에서 단일 반환 값을 ‘pulling’하여 데이터를 소비한다.
생산자는 소비자에게 데이터를 보낼 시기를 결정한다. 소비자는 언제데이터를 받을지 모른다.
Promise는 자바스크립트에서 가장 많이 쓰이는 Push이다. Promise(생산자)는 callback(소비자)에게 나온 값을 전달하고, 함수와 다르게 언제 해당 callback 값을 보낼지(push) 결정한다.
ReactiveX는 새로운 푸시 시스템인 Observable을 도입했다. Observable은 여러 값의 생산자로, 이를 Observer(소비자)에게 보낸다(push).
promise와 비교하자면 멀티아이템가능, cancle가능
Rx를 설명하는데 가장 핵심이 되는 단어는 Observable(옵저버블)이다.
공식문서에서는 옵저버블을 ‘여러 개의 값을게으르게(lazy) Push하는 것’으로 정의하고 있다.
Observable은 느긋한 계산법이다.
계산의 결과값이 필요할 때까지 계산을 늦추는 기법이다.
느긋하게 계산하면 값을 미리 저장하지 않아 공간을 절약할 수 있고, 값이 꼭 필요할 때만 계산하기 때문에 성능에도 좋은 영향을 준다.
promise에서는 결과에 대해서 resolve(), reject() 함수를 통해서 데이터를 전달,
observable에서 observer 객체가 위와같은 역할을 함
Observable.create(
(observer) => {
try {
observer.next('item');
} catch(e) {
observer.error(e);
} finally {
observer.complete();
}
}).subscribe(
(x) => console.log(x),
(err) => console.error(err),
() => console.log('complete')
)
실행시점
객체를 생성할 때 바로 실행된다. (즉시로딩)
모든 결과값 then()은 같은 계산 값을 공유한다.

리턴 개수
값을 하나만 보낼 수 있으며, 여러개를 보낼 경우 나중에 보낸값은 무시된다

조직/반환
then()하나로 데이터의 조직과 반환을 같이 진행한다
]
취소
실행 도중에 취소할 수 없다.
에러 처리
then()이나 catch()를 사용하는데, 위치에 따라 에러를 처리하는 로직이 달라져야 한다.

비동기 처리만 가능
실행시점
소비자가 구독하기 전까지는 실행되지 않는다. (지연로딩)
subscribe()는 여러번 호출될 수 있으며 각각의 구독은 모두 자신만의 계산값을 가지고 있다.

리턴 개수
값을 여러개 보낼 수 있으며, 보낸 값 모두 리턴한다

조직/반환
데이터의 조직과 반환을 나눌수 있다.
구독자가 있을 때만 subscriber 함수가 실행되어 값을 계산한다.
rx에서 제공하는 operators로 데이터를 변경/추출이 가능하다.
다른 곳에서 데이터를 복잡하게 가공해야 한다면 Promise보다 Observable이 더 효율적이다.

취소(구독해제)
실행 도중에 취소할 수 있다. (구독 해제_)

에러 처리
subscribe() 함수는 에러도 함께 처리할 수 있으며, 에러를 발견 할 경우자동으로 구독을 취소한다.
에러발생 후 재호출이 가능하다

동기, 비동기 처리 모두 가능
Subject는 Observer나 Observable처럼 행동한다. Observer이기 때문에 하나 이상의 Observable을 구독할 수 있으며, 동시에 Observable이기도 하기 때문에 항목들을 하나 하나 거치면서 재배출하고 관찰하며 새로운 항목들을 배출할 수도 있다.
Subject는 여러 Observer 에서 구독이 가능하며, Subject에서 전달하는 값을 Observer 들이 전달 받는다.
각각의 구독자에게 맞춤형 서비스를 하는 것을 unicast, cold 발행 이라고 하고 모든 구독자에게 똑같은 서비스를 하는
것을 mulicast, hot 발행이라고 한다.
Observable.create()Observable.subscribe()observer.next()observer.complete() Observable.unsubscribe() //사용 예시
//view
useEffect(() => {
const subscriptions: Subscription[] = [
vm.output.bannersInfo.subscribe((banners) => {
setBanners(banners);
vm.input.exposeBanner(banners[0], props.store);
}),
];
return () => subscriptions.forEach((s) => s.unsubscribe());
}, []);
//viewmodel
this.data.bannersInfo = banners.map((banner) => new DataModel.BannerInfo(banner));
this.output.bannersInfo.next(this.data.bannersInfo);
output = { bannersInfo: new Subject<DataModel.BannerInfo[]>()};
//두개의 스트림 사용하기
return this.storeRepository
.getActiveStore()
.pipe(mergeMap((store) => this.settlementRepository.getSettlementOrderDetailList(orderId, store.id)));
//다양한 오퍼레이터 사용 예시
return this.storeRepository.getActiveStore().pipe(
mergeMap((store) => {
const newArray = [];
for (var i = 0; i < wholesalers.length; i += 10) {
newArray.push(wholesalers.slice(i, i + 10));
}
newArray.map((n) => n.push(store.id));
return from(newArray);
}),
mergeMap((array) => {
const storeId = array[array.length - 1];
const popArray = array.slice(0, -1);
return this.settlementRepository.getRetailerInStoreCreditsByWholesalerIds(storeId, popArray);
}),
tap((data) => {
resultData.push(data);
}),
last(),
map(() => resultData.flatMap((d) => d))
);