react를 다루는 기술

18. 리덕스 미들웨어를 통한 비동기 작업 관리(2)

crab. 2022. 6. 24. 08:54

redux-thunk

  • Thunk는 특정 작업을 나중에 할 수 있도록 미루기 위해 함수 형태로 감싼 것을 의미합니다.
  • 예를 들어 주어진 파라미터에 1을 더하는 함수를 만들고 싶다면 다음과 같이 작성할 것입니다.
const addOne = x => x + 1;
addOne(1); // 2
  • 이 코드를 실행하면 addOne을 호출했을 때 바로 1 + 1이 연산됩니다.
  • 그런데 이 연산 작업을 나중에 하도록 미루고 싶다면 어떻게 해야 할까요?
const addOne = x => x + 1;
function addOneThunk (x){
const thunk = () => addOne(x);
return thunk;
}

const fn = addOneThunk(1);
setTimeout(() => {
const value = fn(); // fn이 실행되는 시점에 연산
console.log(value);
}, 1000);
  • 만약 addOneThunk를 화살표 함수로만 사용한다면 다음과 같이 구현할 수 있습니다.
const addOne = x => x + 1;
const addOneThunk = x => () => addOne(x);

const fn = addOneThunk(1);
setTimeout(() => {
const value = fn(); // fn이 실행되는 시점에 연산
console.log(value);
}, 1000);
  • redux-thunk는 액션 생성 함수에서 일반 액션 객체를 반환하는 대신에 함수를 반환합니다.
  • increaseAsync와 decreaseAsync 함수를 만들어 카운터 값을 비동기적으로 한번 변경시켜 봅시다.
// 1초 뒤에 increase 혹은 decrease 함수를 디스패치함
export const increaseAsync = () => dispatch => {
  setTimeout(() => {
    dispatch(increase());
  }, 1000);
};
export const decreaseAsync = () => dispatch => {
  setTimeout(() => {
    dispatch(decrease());
  }, 1000);
};
  • API를 요청해야 할 때마다 17줄 정도 되는 thunk 함수를 작성하는 것과 로딩 상태를 리듀서에서 관리하는 작업은 귀찮을 뿐 아니라 코드도 길어지게 만듭니다.
  • 그러므로 반복되는 로직을 따로 분리하여 코드의 양을 줄여 봅시다.
export default function createRequestThunk(type, request) {
  // 성공 및 실패 액션 타입을 정의합니다.
  const SUCCESS = </span><span class="co49">${</span><span class="cd2 co34">type</span><span class="co33">}</span><span class="cd2 co31">_SUCCESS;
  const FAILURE = </span><span class="co49">${</span><span class="cd2 co34">type</span><span class="co33">}</span><span class="cd2 co31">_FAILURE;
  return params => async dispatch => {
    dispatch({ type }); // 시작됨
    try {
      const response = await request(params);
      dispatch({
        type: SUCCESS,
        payload: response.data
      }); // 성공
    } catch (e) {
      dispatch({
        type: FAILURE,
        payload: e,
        error: true
      }); // 에러 발생
      throw e;
    }
  };
}

// 사용법: createRequestThunk(‘GET_USERS‘,api.getUsers);
  • 컨테이너 컴포넌트에서 try/catch 구문을 사용하여 에러 값을 조회할 수도 있습니다.
useEffect(() => {
    // useEffect에 파라미터로 넣는 함수는 async로 할 수 없기 때문에
    // 그 내부에서 async 함수를 선언하고 호출해 줍니다.
    const fn = async () => {
      try {
        await getPost(1);
        await getUsers(1);
      } catch (e) {
        console.log(e); // 에러 조회
      }
    };
    fn();
  }, [getPost, getUsers]);