Peter Cho

[RxJs 퀵스타트] RxJS 가 해결하려고 했던 문제

2018.10.21 | 3 Minute Read

입력 데이터의 오류

입력 데이터의 전달 시점은 다양하다. 동기비동기 로직을 함께 사용하여 동작 순서를 보장하기 위해 우리는 많은 작업을 하고 있다.

RxJS는 이런 구조적인 문제를 개선하기 위해 단 하나의 방식을 사용할 수 있는 구조를 제공한다. 이런 구조의 일원화는 개발을 단순화시킨다. 이런 단순화는 결국에는 오류 발생 빈도를 낮추고, 생산성 향상에 도움을 준다.

동기와 비동기의 차이점을 시간이라는 개념을 도입함으로서 해결하려고 한다. 동기와 비동기는 시간의 축으로 봤을 때는 같은 형태이다. 또한 이런 형태는 시간을 인텍스로 둔 컬렉션으로 생각할 수도 있다. RxJS에서는 이를 스트림(Stream)이라 표현한다. 이런 스트림을 표현하는 Observable클래스를 제공한다.

Observable은 시간을 인덱스로 둔 컬렉션을 추상화한 클래스이다. 이 클래스는 동기비동기의 동작 방식으로 전달된 데이터를 하나의 컬렉션으로 바라볼 수 있게 해준다. 이렇게 함으로써 개발자는 데이터가 어떤 형태로 전달되는지에 대해 더이상 고민할 필요가 없어진다.

fromEvent, from, of 함수는 Observable 생성자를 이용하여 만든 팩토리 메소드이다.

const {fromEvent, from, of} = rxjs
const key$ = fromEvent(document, 'keydown')
const arrayFrom$ = from([10, 20, 30])
const numberOf$ = of(10, 20, 30)

상태 전파 문제

웹 애플리케이션의 상태 변화로 인한 문제점은 크게 세가지가 있다.

  1. 인터페이스 변경되면 함께 변경해야 한다.
  2. 상태를 확인하기 우해 인터페이스에 대한 의사소통 비용이 발생한다.
  3. 다수가 A라는 한 클래스에 의존 관계가 있는 경우 A의 변경 여부를 반영하기 위해 다수에 A의 상태를 모두 반영해야 한다.

이러한 문제를 해결하기 위해 우리가 이미 알고 있는 솔루션 옵서버 패턴을 사용한다. 느슨한 결함, 자동 상태 전파, 인터페이스의 단일화 할 수 있다.

class Subject {
  add(observer) {}
  remove(observer) {}
  nofity(observer) {}
}

class Observer {
  update(status) {}
}

하지만 RxJS는 옵서버 패턴의 아쉬웠던 몇 가지를 개선하였다.

  1. 상태 변화는 언제 종료되는 지
  2. 상태 변화에서 에러가 발생할 경우 => 인터페이스의 확장을 통해 종료시점과 에러발생을 해결했다.
  3. Observer에 의해 Subject 상태가 변경되는 경우 => Read-Only와 단방향 데이터 흐름으로 해결
class Observable {
  subscribe(observer) {}
}

class Observer {
  next(status) {}
  error(error) {}
  complete() {}
}

리액티브 프로그래밍은 데이터 흐름과 상태 변화 전파에 중점을 둔 프로그램 패러다임이다. 사용되는 프로그래밍 언어에서 데이터 흐름을 쉽게 표현할 수 있어야 하며 기본 실행 모델이 변경 사항을 데이터 흐름을 통해 자동으로 전파한다는 것을 의미한다.

로직 오류

웹 애플리케이션은 전달받은 입력값을 로직을 통해 새로운 결과를 반환하거나 표현한다. 로직은 산술적인 로직이나 비즈니스적인 로직이 될 수 있다. 또는 if문과 같이 간단한 프로그램의 흐름을 담당하는 부분일 수도 있다.

RxJS은 오퍼레이터는 항상 새로운 Observable을 반환함으로써 불변 객체를 반환한다. 불변 객체는 생성 후 그 상태를 바꿀 수 없는 객체이다. 불변 객체는 외부에서 값을 변경할 수 없기 때문에 불변 객체를 사용하는 것만으로도 프로그램의 복잡도가 줄어들 수 있다.

로직상에 존재하는 반복문, 분기문, 변수를 제거하기 위해서 함수형 프로그래밍 개념을 근간으로 하는 오퍼레이터를 제공한다. Observable이 제공하는 오퍼레이터를 통해 생성, 변환, 병합, 분리와 같은 다양한 연산을 적용할 수 있으며 항상 Immutable Object를 반환한다.

불변 객체 Observable

불변 객체(Immutable Object)은 생성 후 그 상태를 바꿀 수 없는 객체이다. 불변 객체는 외부에서 값을 변경할 수 없기 때문에 불변 객체를 사용하는 것으로도 프로그램의 복잡도가 줄어들 수 있다.

Observable은 새로운 Observable을 만들고 그 Observable이 오퍼레이터를 호출한 원래의 Observable을 내부적으로 구독한다. 즉, 링크드 리스트 형태로 기존 Observable 객체와 새롭게 만든 Observable 객체를 오퍼레이터로 연결한다.

map 오퍼레이터는 다음과 같은 원리로 구현되었다. 실제 구현은 lift 함수를 이용하여 이전 Observable과 연결하는 방식을 사용한다.

const map = function (transformationFn) {
  const source = this
  const result = Observable(observer => {
    source.subscribe(
      (x) => { observer.next(transformationFn(x)) },
      (err) => { observer.error(err) },
      () => { observer.complete() }
    )
  })
  return result
}