성장, 그리고 노력

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

RxJS

[RxJS] Redux-observable - Epic (에픽) + 배경지식

제이콥(JACOB) 2019. 12. 9. 00:26

RxJS를 사용한 Redux-observable을 알기 전에 중요한 패턴 하나만 먼저 알고 지나가려고 한다.

Saga Pattern (사가 패턴)

RxJS에서 나온 개념은 아니다. 1987년 논문에서 처음 알려졌다(https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf, 영어를 좋아하고 잘한다면 읽어봐도 좋을거 같다. 물론 난 아쉽게 아직 그러지 못한다). 분산 트랜잭션의 가장 알려진 패턴이며 분산 시스템의 데이터 일관성을 유지하는 방법이다.

간단히 말하면 이러하다. 아래와 같은 일련의 로직이 있다고 해보자.

  1. 물건을 주문한다.
  2. 카드사에 결제정보를 넘긴다.
  3. 카드사에 결제를 한다.
  4. 카드사로부터 결제 정보를 받는다.
  5. 물건 주문처리를 완료한다.

각 단계는 이전 단계가 완료되어야지 이행된다. 즉 후속 단계는 이전 트랜젝션의 완료에 의해 트리거(trigger)되는 것이다.

(혹시 모를일 없겠지만, 트리거란 직접 호출하는 것이 아니라 특정 이벤트에 반응하여 자동적으로 호출되는 것)


(참고로 분산 트랜잭션의 롤백을 제공되지 않으며 이전에 수행한 작업을 보완하기 위해 다른 트랜잭션을 구현해야 한다.)


 

갑자기 무슨 사가 패턴이지 하겠지만, 이러한 개념이 Redux-observable에 녹아져 있다. 특히 마이크로 서비스를 개발할 때 자주 등장하는 개념이며, 작은 규모의 개발이라면 이러한 개념이 녹아있는 Redux-observable을 굳이 사용하지 않는 편이 생산성이 더 좋다. 하지만 큰 서비스를 운영한다면 이 미들웨어를 한번 고려해볼 만하다.

이제 실제 우리 애플리케이션을 예로 들어보겠다. 위랑 솔직히 똑같다.

  1. 로그인을 시도한다.
  2. 로딩 화면이 나온다.
  3. 서버와 통신 후 로그인 성공(정보)/실패 값을 받는다.
  4. 로딩 화면을 제거한다.
  5. 성공 / 실패에 따른 알맞은 화면 또는 값을 보여준다.

로그인을 시도하는 하나의 액션(LOG_IN_REQUEST)이 시작되면 이어서 여러 트리거 액션이 실행된다. 사가는 이런 서브 트랜잭션 집합이다. Redux-observable은 이렇게 서로 연결된 서브 트랜잭션들을 하나로 묶고 그것을 에픽(Epic)이라고 부른다.

이쯤 되면 그냥 감을 왔을 거라고 믿는다.
"Redux-observable은 서브 트랜잭션을 모두 에픽에서 처리하겠구나, 사가 패턴을 녹여놨구나, 데이터 관리의 일관성을 유지할 수 있겠구나."
뭐 따지자면 이런 생각들이다. 이제 간단하게 Redux-observable을 정의해보자면, RxJS와 Saga 패턴을 이용해서 리덕스를 보다 효율적으로 동작하도록 도와주는 하나의 미들웨어이다.

redux-saga와 다른 점이라고 한다면 같은 사가 패턴을 이용했지만(완벽하게 100% 구현한 건 둘 다 아님), redux-observable이 RxJS를 사용했기 때문에 더 선언적이며 기존 RxJS의 기능을 활용하며 확장했다.

Epic (에픽)

서브 트랜잭션들을 하나로 묶은 단위이며, 액션 스트림을 받아서 액션 스트림을 리턴하는 함수이다. redux-observable 미들웨어에 에픽을 등록해두면 리덕스에 새로운 액션을 보낼 때마다 action$ 옵저버블을 발행한다. 그리고 발행한 액션과 등록된 에픽을 실행시키는 액션이 동일하면 해당 에픽이 동작한다.

const checkLoginEpic = (action$: ActionsObservable<Actions>) =>
  action$.pipe(
    filter(isActionOf(checkLogin)), // typesafe-actions lib...
...
    }),
  );

이런 식으로 구성되며 위 예제는 몇 가지 라이브러리를 더 사용하고 있어서(평소에 사용하는...) 그냥 공식 홈페이지에서 사용하는 예제를 들고 오겠다.

const pingEpic = action$ => action$.pipe(
  filter(action => action.type === 'PING'),
  mapTo({ type: 'PONG' })
);

// 'PING' 액션을 발행하면 pingEpic을 실행시킨다.
dispatch({ type: 'PING' });
반응형

'RxJS' 카테고리의 다른 글

다시 보는 RxJS - 기본편 + Observable 만들기  (0) 2020.08.10
RxJS 기초 - 스트림(Stream)  (0) 2020.02.01
왜 RxJS를 사용해야 하는가?  (0) 2020.02.01
[RxJS] Subjects  (0) 2019.12.08