[Redux] RTK-thunk를 이용한 비동기 처리

Wonhyun Kwon·2023년 6월 8일
0

Redux

목록 보기
5/5
post-thumbnail

❗️이 포스트는 Redux-toolkit(RTK) 개념에 의거하여 작성됐으며, 개념이 부족하다면 위 개념을 숙지 후 읽기를 추천합니다.


1. Redux-toolkit Thunk ?

ThunkRedux 에서 비동기 처리를 도와주는 대표적인 미들웨어 중 하나이다.
말 그대로 미들웨어기 때문에 라이브러리를 설치하여 사용해야 했지만, RTK 는 그 자체를 내포하고 있어 그냥 사용하면 된다는 장점이 있다.




2. 비동기 작업 ?

Redux 는 본래 동기(Synchronous)적으로 작업이 처리 된다.

action ➡️ dispatch(action) ➡️ reducer ➡️ store

즉, 데이터의 흐름이 항상 단방향이기 때문에 쉽게 제어할 수 있다는 장점을 지닌다.

하지만, 외부 API나 기타 작업이 오래걸리는 것이 필요할 땐 동기처리가 부담이 되는 경우가 있다. 이를 위해 Redux 는 다른 상태 관리 도구와의 대표적인 차별점으로 미들웨어를 지원함으로서 비동기(Asynchronous) 작업을 지원한다.




3. 사용하는 이유

아래와 같은 코드가 있다고 해보자.

<Button
  title="+"
  onPress={async () => {
    const res = await axios.get('SERVER_API');
    const data = await res.json();
    dispatch(set(data.value));
    // 위 dispatch를 풀어서 쓴 코드는 아래와 같다.
    // dispatch({type: 'counterSlice/set', payload: data.value});
  }}
/>
<Text>{count} | {status}</Text>

버튼을 클릭했을 때 onPress 에 의하여 안에 함수가 실행이 된다.
async/await 구문에 의하여 비동기적인 작업 처리가 되는데, SERVER_API 라는 어떠한 외부 API로부터 데이터를 받고, 이를 dispatch 하여 상태를 업데이트 하는 코딩이다.

이 코드는 사실 실행하는 데 있어 아무런 문제가 없다.

그래도 개발 관점에서 보는 문제점이란,

  1. 코드 가독성이 떨어진다.
  2. 동일한 작업을 다른 곳에서 사용할 때 코드 중복이 생긴다.
  3. 유지 보수가 어려워진다.

따라서 이를 하나의 함수로 만들어 그 것만 호출하면 얼마나 좋을까? 라는 생각에서 출발하여 미들웨어를 사용하는 이유를 곱씹어 생각하면 될 것 같다.




4. 사용 방법

1) createAsyncThunk

우리가 최종적으로 원하는 코드는 아래와 같다.

<Button
  title="+"
  onPress={async () => {
    dispatch(asyncUpFetch());
  }}
/>
<Text>{count} | {status}</Text>

asyncUpFetch() 라는 함수와 같이 알아서 비동기처리를 도와주는 함수 하나를 구현하고 싶은 것이다.

함수의 정의는 다음과 같다.

const asyncUpFetch = createAsyncThunk(
  'counterSlice/asyncUpFetch',
  async () => {
    const res = await axios.get('SERVER_API');
    const data = await res.json();
    return data.value;
  }
);

이는 자세히 보면 action type 이 있고, 어떤 데이터를 return 할 것인지에 대한 파라미터가 있는 것을 보면 이 전에 배운 action creator 와 완전히 동일하다!!

❗️ createAsyncThunk action creator 이다!!

즉, 비동기 작업을 처리하는 action 을 만들어준다!!
따라서, action type 을 직접 적어줘야 한다.


2) pending, fulfilled, rejected

비동기 작업에는 세 가지 상태가 있다.

  1. pending
    비동기 작업을 시작했을 때 상태
  2. fulfilled
    비동기 작업이 끝났을 때 상태
  3. rejected
    오류가 생겨서 중단됐을 때 상태

이 세 가지는 따로 만드는 것이 아니라, createAsyncThunk 를 사용하면 자동으로 만들어지는 약속된 것이다.


3) extraReducers

위 세 가지 상태에 따른 reducer 가 필요할 것이다.
이는 RTK의 리듀서가 아닌 extraReducers 라는 객체에 따로 구현한다.

const counterSlice = createSlice({
  name: 'counterSlice',
  initialState: {
    value: 0,
  },
  // 동기
  reducers: {
    up: (state, action) => {
      state.value = state.value + action.payload;
    },
  },
  // 비동기
  extraReducers: (builder) => {
    builder.addCase(asyncUpFetch.pending, (state, action) => {
      state.status = 'Loading';
    })
    builder.addCase(asyncUpFetch.fulfilled, (state, action) => {
      state.value = action.payload;
      state.status = 'Complete';
    })
    builder.addCase(asyncUpFetch.rejected, (state, action) => {
      state.status = 'Fail';
    })
  }
});

addCase 메소드를 통해 각각 세 가지 상태일 때에 따른 처리를 해준다.

❗️asyncUpFetch() 함수에서 return 값인 data.value 는 자동으로 fulfilled 상태의 action.payload 로 받는다.


4) reducers vs extraReducers

createSlice 안에 존재하는 두 리듀서 객체의 차이점은 다음과 같다.

  1. reducers 는 동기 작업, extraReducers 는 비동기 작업이다.
  2. reducersaction creatorRTK 가 자동으로 만드는 반면, extraReducers 는 수동으로 만들어야 된다.

즉, 한마디로 비동기 작업actino creator 를 직접 만들어서 extraReducers 안에서 구현해야 한다.

profile
모든 사용자가 만족하는 UI를 만드는 FE 개발자 권원현입니다.

0개의 댓글