React/Redux 마스터

Redux Toolkit 활용 실습(4): 미들웨어 간소화

atomicdev 2024. 10. 24. 15:25
728x90

Redux Toolkit의 createAsyncThunk를 사용한 비동기 처리와 미들웨어 간소화

이번 포스팅에서는 Redux ToolkitcreateAsyncThunk를 사용하여 비동기 처리를 간소화하고, 미들웨어 설정을 어떻게 개선할 수 있는지를 Before -> After 형식으로 설명하겠습니다. createAsyncThunk는 비동기 작업을 위한 간편한 방법을 제공하여 코드의 복잡성을 줄이고 유지보수를 쉽게 만듭니다.

1. 비동기 처리를 위한 미들웨어 사용의 필요성

Redux에서 비동기 작업(예: API 호출)을 처리하기 위해서는 미들웨어가 필요합니다. 기존 Redux 방식에서는 Redux ThunkRedux Saga와 같은 미들웨어를 설정해야 하며, 이는 코드의 복잡성을 증가시키는 원인이 됩니다. Redux Toolkit의 createAsyncThunk는 이러한 복잡한 설정을 간소화하고 코드의 가독성을 높입니다.

 

Before: 기존 Redux 방식의 미들웨어 설정

기존 Redux 방식에서 비동기 작업을 처리하기 위해서는 Redux Thunk를 설치하고 스토어 설정에 추가해야 했습니다.

  1. React 애플리케이션 생성 및 Redux 설치 먼저 React 애플리케이션을 생성하고 Redux 및 Redux Thunk를 설치합니다.
    npx create-react-app my-redux-app
    cd my-redux-app
    npm install redux react-redux redux-thunk

  2. 복잡한 스토어 설정 Redux Thunk를 사용하기 위해 스토어에 미들웨어를 추가하는 설정을 해야 합니다.
    // src/store/store.js
    import { createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';
    import rootReducer from '../reducers';
    
    const store = createStore(rootReducer, applyMiddleware(thunk));
    
    export default store;
  3. 액션 작성과 비동기 작업 처리 비동기 작업을 처리하기 위해 액션 크리에이터에서 Thunk 함수를 사용해야 합니다. 예를 들어, 사용자 데이터를 가져오는 액션을 작성하려면 다음과 같은 코드가 필요합니다.
    // src/actions/userActions.js
    export const fetchUser = (userId) => {
      return async (dispatch) => {
        dispatch({ type: 'FETCH_USER_REQUEST' });
        try {
          const response = await fetch(`/api/user/${userId}`);
          const data = await response.json();
          dispatch({ type: 'FETCH_USER_SUCCESS', payload: data });
        } catch (error) {
          dispatch({ type: 'FETCH_USER_FAILURE', error });
        }
      };
    };

  4. 리듀서 작성 리듀서는 비동기 작업의 상태(로딩, 성공, 실패)를 처리해야 합니다.
    // src/reducers/userReducer.js
    const initialState = {
      user: null,
      loading: false,
      error: null,
    };
    
    const userReducer = (state = initialState, action) => {
      switch (action.type) {
        case 'FETCH_USER_REQUEST':
          return {
            ...state,
            loading: true,
          };
        case 'FETCH_USER_SUCCESS':
          return {
            ...state,
            loading: false,
            user: action.payload,
          };
        case 'FETCH_USER_FAILURE':
          return {
            ...state,
            loading: false,
            error: action.error,
          };
        default:
          return state;
      }
    };
    
    export default userReducer;

After: Redux Toolkit의 createAsyncThunk 사용

Redux ToolkitcreateAsyncThunk를 사용하면 비동기 작업과 관련된 코드가 크게 간소화됩니다. 이제 Redux Toolkit을 사용하여 같은 기능을 어떻게 쉽게 구현할 수 있는지 살펴보겠습니다.

  1. React 애플리케이션 생성 및 Redux Toolkit 설치 Redux Toolkit을 사용할 애플리케이션을 생성하고 필요한 패키지를 설치합니다.
    npx create-react-app my-redux-toolkit-app
    cd my-redux-toolkit-app
    npm install @reduxjs/toolkit react-redux
     
  2. 스토어 설정 간소화 configureStore를 사용하여 기본적으로 Redux DevTools와 미들웨어가 설정되어 있어 추가적인 설정이 필요 없습니다.
    // src/app/store.js
    import { configureStore } from '@reduxjs/toolkit';
    import userReducer from '../features/user/userSlice';
    
    const store = configureStore({
      reducer: {
        user: userReducer,
      },
    });
    
    export default store;
  3. createAsyncThunk를 사용한 비동기 액션 생성 createAsyncThunk를 사용하여 비동기 작업을 처리합니다. 비동기 액션을 쉽게 생성하고, 상태 관리를 자동으로 처리해줍니다.
    // src/features/user/userSlice.js
    import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
    
    export const fetchUser = createAsyncThunk('user/fetchUser', async (userId) => {
      const response = await fetch(`/api/user/${userId}`);
      return response.json();
    });
    
    const userSlice = createSlice({
      name: 'user',
      initialState: {
        user: null,
        status: 'idle',
        error: null,
      },
      reducers: {},
      extraReducers: (builder) => {
        builder
          .addCase(fetchUser.pending, (state) => {
            state.status = 'loading';
          })
          .addCase(fetchUser.fulfilled, (state, action) => {
            state.status = 'succeeded';
            state.user = action.payload;
          })
          .addCase(fetchUser.rejected, (state, action) => {
            state.status = 'failed';
            state.error = action.error.message;
          });
      },
    });
    
    export default userSlice.reducer;

컴포넌트에서 비동기 액션 사용하기 useDispatchuseSelector를 사용하여 컴포넌트에서 비동기 액션을 쉽게 사용할 수 있습니다.

// src/components/UserProfile.js
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchUser } from '../features/user/userSlice';

function UserProfile({ userId }) {
  const user = useSelector((state) => state.user.user);
  const status = useSelector((state) => state.user.status);
  const dispatch = useDispatch();

  useEffect(() => {
    if (status === 'idle') {
      dispatch(fetchUser(userId));
    }
  }, [status, userId, dispatch]);

  if (status === 'loading') {
    return <div>Loading...</div>;
  }

  if (status === 'failed') {
    return <div>Error loading user data.</div>;
  }

  return (
    <div>
      {user ? <h2>Welcome, {user.name}!</h2> : <p>No user data available.</p>}
    </div>
  );
}

export default UserProfile;

전통적인 Redux 미들웨어 설정과 Redux Toolkit의 createAsyncThunk 를 비교

 

상세한 실행 순서와 소스코드 보기

 

요약

  • 기존 방식: Redux Thunk를 사용하여 비동기 작업을 처리할 때, 스토어 설정과 액션 작성이 복잡하고 유지보수가 어려웠습니다.
  • Redux Toolkit 방식: createAsyncThunk를 사용하여 비동기 작업을 간단하게 처리하고, configureStore로 스토어 설정을 자동화하여 코드의 복잡성을 줄였습니다.

실습 코드 요약

  • 기존 방식에서는 비동기 작업을 처리하기 위해 많은 코드와 복잡한 설정이 필요했습니다.
  • Redux Toolkit을 사용하면 이러한 과정이 간단해지고, 비동기 작업과 관련된 코드의 양이 줄어들며 유지보수가 쉬워집니다.
728x90