rxjs가 해결하려고 했던 문제

4589 단어 rxjsrxjs

RxJS 퀵스타트 책의 내용 중 일부를 정리한 글입니다.

1. 입력데이터의 오류

동기, 비동기

웹 애플리케이션에는 다양한 형태의 입력 데이터가 존재한다. 글 목록, 유저가 입력한 폼 데이터 등등... 이를 크게 2가지로 나눌 수 있는데, 동기비동기이다.

둘 중 하나로 통일할 수 있다면 좋겠지만 현실적으로 불가능하다. 그렇기에 중요한 것은 둘을 조화롭게 사용하는 방법이다.

rxjs

rxjs는 동작 방식이 다른 둘을 같은 방식으로 취급한다. 두 방식을 모두 시간에 따른 데이터의 흐름으로 본다. 시간을 인덱스로 둔 컬렉션의 형태라고 보는 것인데, 이를 스트림(stream) 이라고 하며 Observable 클래스를 통해 제공한다.

Observable

Observable은 스트림을 추상화한 클래스다. 동기, 비동기 관계없이 데이터를 하나의 컬렉션으로 취급할 수 있다. 개발자는 더이상 데이터가 전달되는 방식을 신경쓰지 않아도 된다. 그저 전달받은 데이터를 이용하기만 하면 된다.

Observable 클래스를 이용하면 모든 데이터를 Observable로 만들 수 있다. rxjs 네임스페이스에 내재되어 있는 다양한 함수를 이용하면 된다. 한 예로 키보드 입력, 마우스 클릭같은 이벤트를 처리할 때는 fromEvent를 이용한다.

const { fromEvent } = rxjs;

const key$ = fromEvent(document, "keydown");
const click$ = fromEvent(document, "click");

2. 상태 전파 문제

웹 애플리케이션은 상태 머신들로 이루어진 상태 머신이다. 유저와의 상호 작용 등으로 여러 변수들의 값이 변하고, 변한 값은 다른 상태값과 UI에 영향을 준다. 이 때, 필연적으로 상태값 사이에 의존성이 생길 수밖에 없는데 이로 인해 발생할 수 있는 문제점들이 있다.

  • A가 B에 의존적이라고 하면

  • B의 인터페이스가 변경되면 A도 함께 변경해야 한다.

  • A를 담당하는 개발자와 B를 담당하는 개발자 사이의 의사소통 비용이 발생한다.

  • B의 상태 반영을 A에서 직접 해줘야 한다.

Observer Pattern

rxjs는 이를 옵저버 패턴으로 풀어간다. 상태가 변경될 대상을 Subject, 상태 변경을 관찰하는 대상을 Observer라고 하는데 이 둘은 느슨하게 결합되어 있다. 느슨한 결합이란 둘은 여전히 상호작용을 하지만 서로에 대해선 잘 모르는 상태에 있음을 얘기한다.

하지만 아무것도 모르는 상태에선 최소한의 상호작용조차 할 수 없기 때문에 Subject는 Observer가 특정 인터페이스(update)를 구현한다는 건 알고 있다. 이 부분을 제외하면 약속된 부분이 없기 때문에 생성, 수정, 삭제 등에 따른 추가적인 비용이 발생하지 않는 것이다.

옵저버 패턴에서는 상태전파가 PUSH 방식으로 이루어진다. Subject가 자신의 상태변화를 Observer에게 알리는 방식이다. 상태변화에 따른 의사소통의 비용도 발생하지 않고 자동으로 알려주기 때문에 상태를 사용하는 개발자는 상태변화에 신경쓰지 않아도 된다.

하지만 옵저버 패턴에도 문제점이 있다.

  • 상태변화의 종료 시점을 알 수 없다.

  • 상태변화 중 에러발생 여부를 알릴 방법이 정해져 있지 않다.

  • Observer에 의해 Subject의 상태가 변경될 수 있다.

rxjs

rxjs는 어떻게 옵저버 패턴의 문제점들을 해결했을까?

먼저, 종료 시점과 에러를 알리기 위해 rxjs는 특정 인터페이스를 3개까지 구현하고 있다. update 대신 next를 사용하고 종료 시점을 알리기 위한 complete, 에러를 알리기 위한 error까지. 이 3가지 인터페이스를 subscribe 메서드의 인자로 전달받는다.

rxjs는 Subject를 Observable 클래스를 이용해 구현한다. Observer에 의해 Observable의 상태가 변경되는 일이 없도록 Observable 클래스에는 데이터를 전달받기 위한 어떤 메서드도 정의되어 있지 않다. 데이터가 양방향으로 흐르게 되면 복잡도가 증가하고 오류가 발생할 확률이 커지기 때문이다.

PUSH 방식

옵저버 패턴에서는 상태전파가 PUSH 방식으로 이루어진다. Object가 Subject의 상태를 일일이 확인하는 방식을 PULL 방식, Subject가 자신의 상태변화를 Object에게 알리는 방식을 PUSH 방식이라고 한다.

Subject의 상태변화를 일일이 확인할 필요도 없고 여러 개의 Object가 있더라도 상태변화가 자동으로 전달되기 때문에 상태전파에 굉장히 효과적인 방식이라고 할 수 있다.

3. 로직 오류

웹 애플리케이션의 로직은 반복문, 분기문, 변수에 의해 복잡도가 증가한다. 코드의 가독성은 떨어지고 오류가 발생할 확률은 증가한다. rxjs는 오퍼레이터를 제공함으로써 반복문, 분기문, 변수를 로직으로부터 분리한다.

ajax$
  .pipe(
    map(...),
    reduce(...)
  )

대략 이런 구조로 되어있는데 pipe 메서드 안에 적용하고자 하는 오퍼레이터들을 순서대로 인자로 넘겨 주면 된다. 각 오퍼레이터를 거칠 때마다 새로운 Observable이 생성되는데, 각 Observable은 바로 이전에 생성된 Observable을 구독한다.

연결 리스트의 구조라고 할 수 있는데, 이를 통해 source로부터 전돨된 데이터, 에러, 종료여부가 오퍼레이터들을 통해 구독한 Observer에게 전달될 수 있게 되는 것이다.

좋은 웹페이지 즐겨찾기