[Redux] 리덕스 미들웨어

괴발·2022년 8월 11일
0

리덕스 미들웨어 (Redux Middleware)

리덕스 미들웨어는 리덕스가 지니고 있는 핵심 기능.
Context API나 Mobx에 경우에는 미들웨어를 지원하지 않는다.
리덕스 미들웨어를 사용하면 액션이 디스패치 된 다음, 리듀서에서 해당 액션을 받아와서 업데이트하기 전에 추가적인 작업을 할 수 있다.

액트에서 리덕스의 미들웨어를 사용하기 위해선 index.js에서 store를 설정해주는 과정에서 미들웨어를 사용하는 것을 선언할 수 있다.

import { applyMiddleware, createStore } from 'redux';

const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk)(createStore)

<Provider store={createStoreWithMiddleware} >
        <App />
 </Provider>

redux 라이브러리에서 applyMiddleware 함수에 사용할 미들웨어들을 추가해준 뒤에 Store에 추가해주면 된다. 물론 미들웨어도 npm으로 install 해야 한다.

리덕스의 동작 순서는 액션이 dispatch가 된 후 리듀서를 호출 하는데 기존에 있던 상태(state)를 dispatch한 액션으로 바꾼다.

이때 리듀서가 호출되어 상태를 바꾸기 이전에 동작하는 것이 미들웨어이다.

미들웨어가 할 수 있는 동작은 예시로

  • 특정 조건에 따라 액션이 무시되게 만들 수 있습니다.
  • 액션을 콘솔에 출력하거나, 서버쪽에 로깅을 할 수 있습니다.
  • 액션이 디스패치 됐을 때 이를 수정해서 리듀서에게 전달되도록 할 수 있습니다.
  • 특정 액션이 발생했을 때 이에 기반하여 다른 액션이 발생되도록 할 수 있습니다.
  • 특정 액션이 발생했을 때 특정 자바스크립트 함수를 실행시킬 수 있습니다.

보통 리덕스는 전역 상태로 관리하는 것은 컴포넌트 전역으로 쓰이는 상태(데이터)들을 관리한다. 리덕스가 미들웨어를 사용하는 이유는 비동기 작업을 처리할 때, 예를들어 상태(데이터)는 보통 DB에 저장되어 있기 때문에 API 통신으로 백엔드에서 DB를 가져오기 위해 사용한다.

그렇기에 리덕스로 액션 생성자를 통해 가져오는 데이터들은 대개 비동기 통신으로 가져오는 경우가 많기 때문에 미들웨어가 쓰이는 것이다.

예를 들어 클라이언트(리액트)에서 백엔드 API를 연동해야 된다면, Redux Thunk와 redux-promise-middleware 를 사용할 수 있다.




Redux Thunk

Redux Thunk는 액션 생성자가 리턴하는 것을 객체가 아닌 함수를 사용할 수 있게 한다. 그리고 함수를 리턴하면 그 함수를 실행이 끝난 뒤에 값을 액션으로 넘겨준다.
이 미들웨어를 사용하면 액션 객체가 아닌 함수를 디스패치 할 수 있다.

기존에 액션 생성자가 리턴하는 객체로는 처리하지 못했던 비동기 작업을 Redux Thunk를 사용하면서 일반 함수를 리턴할 수 있게 됨에 따라 일반 함수에서 가능한 모든 동작들이 가능해진다.

const thunk = store => next => action =>
  typeof action === 'function'
    ? action(store.dispatch, store.getState)
    : next(action)

미들웨어를 사용하면 함수를 디스패치 할 수 있다고 했는데요, 함수를 디스패치 할 때에는, 해당 함수에서 dispatch 와 getState 를 파라미터로 받아와주어야 한다. 이 함수를 만들어주는 함수를 우리는 thunk 라고 부른다.

const getComments = () => (dispatch, getState) => {
  // 이 안에서는 액션을 dispatch 할 수도 있고
  // getState를 사용하여 현재 상태도 조회 할 수 있습니다.
  const id = getState().post.activeId;

  // 요청이 시작했음을 알리는 액션
  dispatch({ type: 'GET_COMMENTS' });

  // 댓글을 조회하는 프로미스를 반환하는 getComments 가 있다고 가정해봅시다.
  api
    .getComments(id) // 요청을 하고
    .then(comments => dispatch({ type: 'GET_COMMENTS_SUCCESS', id, comments })) // 성공시
    .catch(e => dispatch({ type: 'GET_COMMENTS_ERROR', error: e })); // 실패시
};

thunk 함수에서 async/await를 사용해도 상관 없습니다.

const getComments = () => async (dispatch, getState) => {
  const id = getState().post.activeId;
  dispatch({ type: 'GET_COMMENTS' });
  try {
    const comments = await api.getComments(id);
    dispatch({ type:  'GET_COMMENTS_SUCCESS', id, comments });
  } catch (e) {
    dispatch({ type:  'GET_COMMENTS_ERROR', error: e });
  }
}
profile
괴발개발

0개의 댓글