꼬프로

Categories

1부 Chapter 01. RxJS가 해결하려고 했던 문제 1 - 입력 데이터의 오류

RxJS란?
RxJS(ReactiveX for Javascript)는 마이크로소프트가 만든 ReactiveX의 자바스크립트 버전 라이브러리
입력오류, 상태오류, 로직오류로 인해 발생하는 문제를 효과적으로 처리하기 위한 고민의 산출물
오류가 발생하지 않는 어플리케이션을 만들기위한 일관된 방식
안전하게 데이터 흐름을 처리하도록 도와주는 라이브러리


1.1. 웹 애플리케이션의 입력 데이터

게시판의 동작 과정 - 데이터가 흐르는 관점

  1. 목록화면과 조회화면은 서버로부터 데이터를 불러와 브라우저에 전달
  2. 브라우저에 전달된 정보를 브라우저의 UI객체에 전달
  3. 편집화면은 브라우저의 UI 객체를 통해 사용자의 입력정보를 전달받고 이를 다시 브라우저의 다른 UI 객체나 브라우져 객체로 전달
  4. 사용자가 입력한 정보를 브라우저 UI 객체나 브라우저 객체를 이용하여 서버로 전달

게시판의 동작 과정 - 상태 머신 관점

위의 1과 2의 과정에서 입력값은 서버로부터 전달받은 게시글 데이터가 됨, 이 두 과정의 입력값이 동일 할지라도 입력값을 받는 브라우저와 브라우저 UI객체는 서로다른 시점에 입력값을 전달 받음
1과정이 Ajax로 JSON 데이터를 받아와 브라우저의 객체로 저장하는 경우 Ajax는 비동기 호출로 데이터를 받기까지 시간이 걸림

// XMLHttpRequest에 의해 입력된 데이터
let result;
const xhr = new XMLHttpRequest();
xhr.onload = function (e) {
	// Ajax를 통해 얻은 데이터를 result에 저장한다
	{
		list:[
			"게시글 1번. 안녕하세요",
			"게시글 2번. 안녕하세요",
			"게시글 3번. 안녕하세요"
		]
	}
	result = JSON.parse(xhr.responseText);
}
xhr.open('GET',url);
xhr.send();

브라우져 UI객체는 이미 브라우저에 존재하는 JSON 데이터를 받기 때문에 동기 호출로 바로 결과 데이터를 얻음

<ul>
	<li></li>
	<li></li>
	<li></li>
</ul>
Array.from(document.querySelectorAll('li').forEach((v,i) => {
	// Ajex의 결과인 result 변수를 이용하여 DOM에 정보를 표현
	v.innerText = result.list[i]
}));

3과 4의 과정도 입력값은 사용자의 입력이 되지만 전달된 입력값을 처리하는 시점은 상황에 따라 각각 다름

과정 데이터 데이터 흐름 전달 시점 예제
1 게시글 서버 -> 브라우저 비동기 Ajax 통신으로 JSON 데이터를 받음
2 게시글 브라우저 -> 브라우저 UI 객체 동기 JSON 데이터를 UI에 반영
3 사용자 입력 내용 사용자 -> 브라우저 UI 객체 비동기 textarea를 통해 사용자 입력을 받음
4 사용자 입력 내용 브라우저 UI 객체 -> 브라우져 -> 서버 동기, 비동기 textarea의 데이터를 JSON 객체로 저장 후, 서버로 Ajax 요청

1.2. 입력 데이터의 전달 시점이 다양하다

입력 데이터가 같을지라도 실제 각 객체들 사이로 데이터가 전달되는 시점은 다름

1.2.1. 동기

동기 방식은 작업이 들어온 순서에 맞게 하나씩 진행
호출하는 함수가 호출되는 함수의 작업 완료를 기다린 후 그 다음을 진행

장점 : 순차 진행이기 때문에 개발이 쉬움, 코드 흐름과 프로그램의 흐름이 같음
단점 : 처리하는 작업이 많을 경우 느림, 특히 브라우져 같이 단일 UI 스레드를 사용하는 경우에는 해당 작업 완료까지 브라우져는 대기하고 있어야 함

1.2.2 비동기

비동기 방식은 순서에 상관 없이 산발적으로 작업이 진행
호출하는 함수가 호출되는 함수의 작업 완료를 기다리지 않고 다음을 진행
호출되는 함수의 작업이 완료되면 별도의 이벤트나 콜백함수를 통해 결과를 전달

장점 : 효율적인 작업 진행
단점 : 복잡해지고 오류 확률은 높아짐


1.3 동기화 비동기를 함께 사용할 수밖에 없는가?

동기 방식은 안정적이고 오류가 적음
비동기 방식은 효율적이지만 추가 작업이 많고 오류의 확율이 높음
모두 동기 방식으로 개발하면 될 것 같지만 그것은 불가능
기반 플랫폼 API(키보드/마우스 이벤트, Ajax/fetch 등)가 비동기 방식
단일 스레드 기반의 브라우저 환경에서는 비동기 방식을 사용하지 않으면 특정 작업이 프로세스를 독점하여 성능 이슈가 발생
동기와 비동기를 함께 사용 할 수 밖에 없음


1.4 RxJS는 어떻게 개선하였나?

입력 데이터에 대한 구조적 문제를 개선
단 하나의 방식을 사용 할 수 있는 구조를 제공
구조의 일원화는 개발을 단순화 시킴
오류 빈도를 낮추고 생산성 향상에 도움

RxJS는 동기와 비동기의 차이점을 시간이라는 개념을 도입함으로 해결하고자 함

대표적인 비동기 처리 방식인 event의 예
버튼에서 발생하는 click 이벤트를 처리하기 위해 이벤트 핸들러를 등록

button.addEventListener("click", event => {
	// todo
});

한번 이벤트 핸들러를 등록하면 버튼을 누를 때마다 호출됨
버튼을 누르는 행위가 우리가 원하는 데이터라면 시간 축을 기준으로 다음과 같음 모습을 보임

시간을 축으로 한 이벤트의 흐름

이벤트가 아닌 동기 방식인 함수 호출도 시간이라는 개념을 도입하면 다음과 같이 표현 가능

시간을 축으로 한 함수의 흐름

결국 동기와 비동기화는 시간의 축으로 봤을 때는 같은 형태
이런 형태를 시간을 인덱스로 둔 컬렉션으로 생각 할 수도 있음
RxJS에서는 이를 스트림(stream)이라 표현

시간의 흐름에 따라 데이터가 발생 함

이런 스트림(stream)을 표현하는 Observable(옵저블) 클래스를 제공

1.4.1 Observable

Observable은 시간을 인덱스로 둔 컬렉션을 추상화한 클래스
동기나 비동기 방식으로 전달된 데이터를 하나의 컬렉션으로 바라 봄
개발자는 동기 비동기 고민할 필요 없이 Observable를 통해 데이터를 전달 받기만 하면 됨

1.4.2 모든 데이터는 Observable 인스턴스로 만들 수 있다.

Observable는 모든 데이터를 다룸

  • 키보드를 눌러서 입력된 데이터
  • 마우스를 이동하거나 클릭해서 입력된 데이터
  • Ajax/fetch 요청을 통해 얻은 데이터
  • Web socket을 통해 전달된 데이터
  • Messages를 통해 전달된 데이터

여러 데이터가 RxJS로 모이는 모습을 형상화 함

모든 데이터를 Observable를 통해 동일한 형태로 사용
RxJS는 Observable를 만들기 위해 다양한 함수를 제공

키보드나 마우스와 같은 이벤트를 만들 때

fromEvent(HTMLElement,”이벤트타입”);

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

배열 같은 itersble, array-like, Promise 데이터를 만들 때

from(Itersble|Array-like|Promise)

const { from } = rxjs;
const arrayFrom$ = form([10,20,30]);
const itersbleFrom$ = form(new Map([[1,2],[3,4],[5,6]]));
const ajaxPromiseFrom$ = form(fetch("./api/some.json"));
// rxjs6에서부터 fromPromise가 제거되고 from으로 통합

단일 데이터를 연속으로 전달하는 경우

of(…items);

const { of } = rxjs;
const numberOf$ = of(10,20,30);
const stringOf$ = of('A','B','C');

위에서 소개한 함수들은 Observable 생성자를 이용하여 만든 팩토리 메소드

Observable 객체의 변수명은 관용적으로 접미사로 $를 붙인다.
Observable 객체는 스트림(stream), 그래서 s와 유사한 $를 약어로 사용. 예) clickStream => clcik$


1.5 정리

  • 입력 데이터 전달 방식의 구조적인 문제점을 시간을 인텍스로 둔 컬렉션으로 추상화시킴으로서 동기화 비동기 입력 데이터를 하나의 컬렉션으로 취급
  • 이를 스트림(stream)이라 부르며 그 구현체가 Observable 클래스
  • Observable은 모든 데이터를 표현할 수 있는 일원화된 데이터 구조
  • 개발자는 Observable를 사용함으로서 동기 비동기를 고민할 필요가 없음