June

next-redux-wrapper issue with Redux-observable 2

연결된 이전 포스트 : next-redux-wrapper issue with Redux-observable 1


얼마 전에 올렸던 next-redux-wrapper issue with Redux-observable 포스트에 대한 해결책을 찾아 결과를 공유하는 글을 간단~히 준비했습니다. (매우 짧습니다)


stackoverflow를 열심히 찾다 링크와 같은 내용을 확인했습니다.

redux-observable을 사용하면서 비동기 통신 후, dispatch에 대한 싱크가 맞지 않는다 라는 문의의 답변으로 아래와 같이 rootEpic에 대해 toPromise를 추가해라 라는 내용이었고, saga에서의 await store.sagaTask.toPromise(); 문구와 유사하다고 느꼈습니다.

stackoverflow

그래서 해당 부분을 개발 코드에 적용해 보기로 하였고, 로직 자체에 대한 레퍼런스가 없어 100퍼센트 이해하진 못하였으나, 문제가 해결되는 것을 볼 수 있었습니다.


조금 특이하게, dispatch에 action을 넣는 방식이 아니라 각 action에 dispatch를 넣는 방식으로 돌아가는 구조였습니다. (이 부분에 대한 이해가 100프로가 되지 않습니다.)

export const getServerSideProps = wrapper.getServerSideProps(async context => {
  console.log('2. Page.getStaticProps uses the store to dispatch things');

  /**
   * @issue
   * user/epics파일의 fetchUserEpic함수 return 타입을 UserEpic으로 잡으면, of()에 모든 Action을 넣으라는 에러가 나게 됨.
   */
  const initialData$ = of(fetchUserAction()); // 1. toPromise를 적용할 초기 비동기 함수를 넣음
  const actions = await rootEpic(initialData$, rootStore).pipe(toArray()).toPromise(); // 2. initialData actions에 toPromise를 적용함
  actions.forEach(context.store.dispatch); // 3. actions를 반복하며 dispatch를 적용함

  console.log('2. the end');
});

기존에 올렸던 포스트의 전반적인 store 세팅을 진행하는 store.tsx에서 달라지는 부분이 없엇고,

// redux-saga => store.tsx
...
export interface SagaStore extends Store {
    sagaTask?: Task;
}

export const makeStore: MakeStore<State> = (context: Context) => {
    // 1: Create the middleware
    const sagaMiddleware = createSagaMiddleware();

    // 2: Add an extra parameter for applying middleware:
    const store = createStore(reducer, applyMiddleware(sagaMiddleware));

    // 3: Run your sagas on server
    /*
    * @IMPORTANT 중요한 건 이부분!!! 이 부분이 observable에 없음. 없는 그대로 진행
    */
    (store as SagaStore).sagaTask = sagaMiddleware.run(rootSaga);

    // 4: now return the store:
    return store;
};
...

reducer 또한 마찬가지로, 기존 redux-saga에서와 동일하게 action.type === HYDRATE 구문이 추가되는 것이 동일합니다.

// reducer.tsx
const rootReducer = (state: any, action: RootActionType) => {
  if (action.type === HYDRATE) {
    return {
      ...state,
      ...action.payload
    };
  } else {
    return combinedReducer(state, action);
  }
};

해당 PoC를 진행하면서 typesafe-actions 에서 redux-toolkit을 사용하기로 결정되었기에 PoC 또한 이를 변경하는 작업을 함께 진행하였는데요! 결국 핵심적으로 바뀐 부분은 위와 같이 getInitialProps (getStaticProps, getServerSideProps) 부분이었습니다.

약간의 문제로 보자면, typesafe-actions에서 redux-toolkit으로 교체하며 action들에 대한 type을 typesafe-actions 대비 redux-toolkit이 제대로 지원해주지 못하고 있습니다. (애초에 redux-thunk만 정식 지원이긴 합니다... create async thunk...너무 thunk만 지원을... ㅠㅠ)

결론적으로 typesafe-actions를 지우지 못하고, type을 뽑아오는 부분에서만 사용하고 있습니다. 이 부분에 대해 좋은 방법을 아시고 계신 분은 알려주시면 감사하겠습니다!! ㅎㅎ