Redux Observable 간단 정리 하기

박성룡 ( Andrew park )
8 min readDec 8, 2019

--

Rudux 에서 비동기 처리가 필요하던 중 RxJS 와 함께 쓸 수 있는 Rudux Observable 을 알게 되여 이를 정리해보고자 한다.

Rudux ObservableRxJS 기반의 Rudux Middleware 다.

Rudux 미들웨어 는 dispatch 가 호출되었을때, Action 을 가로채, 작업을 처리하고, Next 로 다음 Middleware 에게 Action 을 전달한다.

기본 Next 는 RootReducer 이다.

// redux-thunk
const createThunkMiddleware = ({ dispatch, getState }) => (next) => (action) => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};

Redux Observable 개요

Redux Observable 는 RxJS 기반으로 Rudux Middleware 를 구현하였다.

dispatch 가 호출 되었을때, 구독하고 있는 Epic 들이 원하는 Action 이 있다면, Action 을 Observable 로 작업을 처리하고, 다시 store.dispatch 로 Action 을 호출하는 방법으로 구성되어 있다.

epic(action$, state$).subscribe(store.dispatch)

이때 Action 은 동기 Reducer 에서 먼저 처리하고, 이후 Action 을 구독중인 비동기 Reducer 인 Epic 들이 다음으로 처리한다.

const createEpicMiddleware = (store) => (next) => {
const epic$ = new Subject();
// ...
epic$.subscribe(store.dispatch);
return action => {
const result = next(action);
stateSubject$.next(store.getState());
actionSubject$.next(action);
return result;
};
};

Redux Observable 간단한 예제

Redux Observable 에서는 Action 을 처리하는 방법을 Epic 이라고 정의했다.

Epic 의 Action 이나 State 는 모두 Observable 로 구성되어 있기 때문에, RxJS 를 이해하고 있다면, 매우 쉽게 사용할 수 있다.

아래는 간단한 예제다.

const pingEpic = action$ => action$.pipe(
ofType('PING'),
// filter(action => action.type === 'PING'),
mapTo({ type: 'PONG' })
);
const pingReducer = (state = { isPinging: false }, action) => {
switch (action.type) {
case 'PING':
return { isPinging: true };
case 'PONG':
return { isPinging: false };
default:
return state;
}
};
// ...
dispatch({ type: 'PING' });

과정

  1. dispatch({ type: ‘PING’ }) 를 호출하여, 새로운 action을 발생시킨다.
  2. reducer 에서 action.type 이 ‘PING’ 을 확인하고 새로운 state 를 반환한다.
  3. epic 에서 action.type 이 ‘PING’ 이므로, 다음 Stream 을 진행한다.
  4. pipe 가 끝이 나면, 반환된 값을 dispatch({ type: ‘PONG’ }) 를 호출하여, 새로운 action을 발생시킨다.
  5. reducer 에서 action.type 이 ‘PONG’ 을 확인하고, 새로운 state 를 반환한다.
  6. epic 에서 원하는 action.type 이 ‘PONG’ 아니므로 무시한다.

Redux Observable Epic

Redux Observable 의 Epic 은 아래와 같이 정의되어 있다.

function (
action$: Observable<Action>,
state$: StateObservable<State>)
dependencies?: Dependencies
): Observable<Action>;

action$ 과 (store 에서 가져온 이전) state$ 는 Observeble 로 제공되며, 반환자는 반드시 Observable 이어야 한다.

(action$, state$) => action$.pipe(...)

반환된 값을 dispatch 다시 Action 을 Reducer 에게 전달되므로, 잘못 사용하게되면, 무한하게 호출될 수 있다.

Epic 에서는, Action type 을 구분하기위한, 특별한 operator 가 구현되어 있다.

ofType 는 Action.type 을 확인하고 다음 Stream 의 진행 여부를 결정할 수 있는데, RxJS 의 Filter 와 비슷하다.

ofType 은 쓰지 않아도 무방하지만, 사용하면 일관된 형태를 강제로 유지할 수 있다.

function ofType(...types): OperatorFunction<Input, Output> {
return filter((action): action is Output => {
const { type } = action;
const len = types.length;
if (len === 1) {
return keyHasType(type, types[0]);
} else {
for (let i = 0; i < len; i++) {
if (keyHasType(type, types[i])) {
return true;
}
}
}
return false;
});
}
const keyHasType = (type, key) => {
return type === key || (typeof key === 'function' && type === key.toString());
};

Redux Observable 설정

Redux Observable 에서는 Redux 의 combineReducers 와 비슷하게, combineEpics 으로 Epic 들을 그룹핑할 수 있다.

최종 Epic 그룹을 epicMiddleware 의 run 함수 에 전달해야만, Epic 들이 Action 구독을 시작한다.

const rootEpic = combineEpics(
aEpic, bEpic
);
const epicMiddleware = createEpicMiddleware({
dependencies: { /* some */ }
});
const store = createStore(
rootReducer,
applyMiddleware(epicMiddleware)
);
epicMiddleware.run(rootEpic);

Redux Observable Dependency

Redux Observable 에서는 Epic 들이 공통적으로 활용할 의존성 객체를 dependencies 로 전달할 수 있다.

createEpicMiddleware 로 Epic Middleware 를 생성할 때, dependence (의존성) 을 주입할 수 있다.

생성할 때 주입한 의존성 객체는 Epic 에서 직접 사용할 수 있다.

const service: Service = process.env.NODE_ENV === 'production' ?
new Service() : ServiceMock;
const epicMiddleware = createEpicMiddleware({
dependencies: { service }
});
// ...const fetchEpic = (action$, state$, { service }) => action$.pipe(
ofType('FETCH'),
mergeMap(({payload}) => service.fetch$()),
map(payload => ({
type: 'THEN',
payload
})
);

마무리

191208 기준 v1.2.0 은 RxJS 6 버전 이상 과 Redux 4 버전 이상을 지원한다.

아직은 내용을 정리한 단계 이지만, 조금더 활용해서 삽질기나, 고급 스킬을 다음 포스트로 작성할 수 있으면 좋겠다.

Redux Observable 은 RxJS 를 이해하고 있다면, 쉽게 접근할 수 있다.

operator 가 생각이 안날때 아래 링크를 참조하면 편하다.

--

--