성장, 그리고 노력

부족하더라도 어제보다 더 잘해지자. 노력은 절대 배신하지 않는다.

RxJS

왜 RxJS를 사용해야 하는가?

제이콥(JACOB) 2020. 2. 1. 17:18

2020/01/27 - [Javascript] - 1부 - 자바스크립트 함수 표현식, IIFE 그리고 비동기

2020/01/28 - [Javascript] - 2부 - 자바스크립트 비동기적 프로그래밍(콜백, 프라미스)

 

 이미 앞에서 콜백(callback)과 프라미스(promise)라는 멋진 개념들을 살펴봤다(asnyc await는 나중에 살펴볼 것이다).

 

 그런데 궁금해 진다.

 

 웹이 커지며 여러 요구 사항이 등장하여 여러 콜백을 중첩시켜 연속 전달 스타일(CPS, Continuation-Passing Style)을 사용했다. 그리고 가독성부터 코드 추측이 어렵기 때문에 ES6에서는 FP(Functional Programing) 패턴을 따른 Promise을 대안으로 제시했다. 그런데 왜 RxJS를 공부해야 할까? 

 

cf) CPS: https://2ality.com/2012/06/continuation-passing-style.html


Promise로 나아졌지만, 한계가 존재한다

다시 복습하는 Promise

 프라미스는 비동기(Asynchronous)과 오래 걸리는 실행 연산(long-running operation)을 래핑(wrap)한 데이터 유형으로, 작업이 완료되어 계산된 결과값을 구독자(subscriber)가 받게 될 때 프로세스가 완료된 것으로 간주한다. 프라미스가 실행된 후에는 값을 변경할 수 없으므로 사실상 함수형의 특징인 불변(immutable) 타입이다. 

 

아래는 ajax()가 프라미스를 반환할 때의 예제이다. 

ajax("/items").then(items =>
    items.forEach(item =>
        ajax(`/data/${item.getId()}/info`)
            .then(dataInfo => ajax(`/data/files/${datainfo.files}`))
            .then(processFiles)
    )
);

선언적 프로그래밍 

 선언적 프로그래밍이란 프로그램(함수 등)이 어떤 방법으로 해야 하는지를 나타내기보다 무엇과 같은지를 설명하는 경우를 말한다. 즉 달성하려는 것을 표현하는 방식이다. 

 우린 이 코드를 선언적으로 바꿀 수 있다. 선언적으로 바꾸는 이유는 RxJS는 FP에서의 선언적인 디자인을 선호하기도 하고 프라미스 또한 그런 방식을 적용하여 부수 효과(side effects)를 순수 함수 방식으로 표현할 수 있기 때문이다. 

const getItems = () => ajax('/items');
const getInfo = item => ajax(`/data/${item.getId()}/info`);
const getFiles = dataInfo => ajax(`/data/files/${datainfo.files}`);

 위에 처럼 리팩토링 한 이후 Promise를 사용하여 비동기 흐름을 연결해 보자. Promise.all() 함수를 사용하면 각각의 프라미스를 하나의 결과 배열로 매핑할 수 있다. 

getItems()
    .then(items => items.map(getInfo))
    .then(promises => Promise.all(promises))
    .then(infos => infos.map(getFiles))
    .then(promises => Promise.all(promises))
    .then(processFiles);

then()은 호출 사이에 시간이 포함되어 있다는 것을 명시적(explicitly)으로 나타내는데, 이것 정말 좋은 것이다. 어떤 단계에서 실패하면 catch() 블록으로 오류를 처리하고 명령 체인을 계속 수행할 수 있다. 

 

프라미스의 한계

 앞에서 열심히 한번 더 프라미스에 대해 홍보(?)했지만 한계는 존재한다.

  1. 마우스 움직임이나 파일 스트림의 바이트 시퀀스처럼 둘 이상의 값을 생성하는 데이터 소스(data sources)를 처리할 수 없다.
  2. 에러 발생시 재실행하는 기능이 없으며 Promise가 불변이라 취소할 수 없다. 예를 들어 XmlHttpRequest 객체 기반으로 하는 HTTP 호출은 중단할 수 있지만(XMLHttpRequest.abort()), 프라미스 인터페이스를 통해서는 사용할 수 없다. 

물론 문제점을 콜백과 프라미스, 이벤트 이미터(EventEmitter, 내 글에서 다루진 않았다) 등을 잘 사용해서 해결해 나갈 수 있다.  하지만 이렇게 구현하면 코드의 가독성 및 이해하기 어려운 코드가 만들어질 수가 있다. 또한 비동기 코드들을 다둘때 여러가지 추가적 문제가 발생한다. 

 

RxJS가 필요한 이유

 RxJS는 FP와 RP의 패러다임을 결합함으로써 비동기와 이벤트 기반 데이터 소스를 효과적으로 다루며 많은 문제를 해결할 수 있다. 그래서 RxJS는 FRP(Functional Reactive Programming)라고 한다. 나중에 좀 더 알아보고 기존 방식의 한계를 더 살펴보자.

 

  1. 비동기 함수가 있는 for, while 등의 제어문은 제대로 동작하지 않는다. 반복 사이의 지연시간을 인식하지 못하기 때문이다.
  2. 콜백 내에서 try/catch 블록이 중첩된다면 에러 처리 전략이 근방 복잡해진다. 
  3. 에러 발생시 재시도 로직을 구현하는게 매우 어렵다.
  4. 콜백 헬이 발생하여 코드를 가독성 및 이해하기가 매우 힘들다. 또한 복잡하게 다른 변수와 중첨된 함수가 얽혀있기 때문에 단위 테스트 하기 힘들다.
  5. 이벤트를 취소하기 힘들다. 예를 들어 오래 걸리는 HTTP 요청을 취소한다고 생각하면 된다.
  6. 스토틀링(throttling)과 디바운싱(debouncing) 같은 사용자와 UI 컴포넌트의 상호 작용을 조절해 줄 수 있는 방법이 필요하다.
  7. 자바스크립트로 만든 클라이언트 측 코드는 예젠에는 메모리 관리를 걱정하지 않아도 됐다(브라우저가 저수준에서 처리). 하지만 현재의 자바스크립트 이플리케이션은 매우 복잡하고 커졌으며, 많은 이벤트 리스너들이 메모리 누수를 일으키고 브라우저 프로세스의 크기를 커지게 하고 있다. 

 RxJS(Reactive Extensions for JavaScript)는 위와 같은 문제들을 해결할 수 있는 여러 오퍼레이터들을 제공하며, 간단한 순차 단계의 체인과 유사한 프로그래밍 모델로 비동기 데이터 흐름을 효과적으로 처리한다.

https://itnext.io/go-reactive-with-rxjs-c07dd3964cf2

 또한 RxJS는 파일 읽기, HTTP 호출, 키 입력 또는 마우스 움직임 등의 이벤트 데이터 소스를 처리하는 단일 프로그래밍을 사용하여 콜백 또는 Promise 기반 라이브러리를 정확히 같은 방식으로 대체한다. 

반응형

'RxJS' 카테고리의 다른 글

다시 보는 RxJS - 기본편 + Observable 만들기  (0) 2020.08.10
RxJS 기초 - 스트림(Stream)  (0) 2020.02.01
[RxJS] Redux-observable - Epic (에픽) + 배경지식  (2) 2019.12.09
[RxJS] Subjects  (0) 2019.12.08