개발 방법론 & 아키텍쳐

Redux 단점 보완: 비동기 상태 관리

atomicdev 2024. 10. 15. 16:11
728x90
 

Redux에서 비동기 상태 관리를 위해 미들웨어가 필요한 단점을 해결할 수 있는 방법을 몇 가지 추천해 드리겠습니다. Redux는 기본적으로 동기적 상태 관리에 적합하지만, 비동기 작업을 처리하기 위해 미들웨어를 추가로 설정해야 하는 번거로움이 있습니다. 이 문제를 해결하면서 비동기 상태 관리를 더 쉽게 구현할 수 있는 방법을 소개합니다.

Redux Toolkit을 사용한 비동기 상태 관리

1. Redux Toolkit의 createAsyncThunk 사용

createAsyncThunk는 Redux Toolkit에서 제공하는 비동기 상태 관리를 위한 강력한 유틸리티입니다. 미들웨어 설정 없이 비동기 작업을 간단하게 처리할 수 있으며, 비동기 액션의 진행 상태 (pending, fulfilled, rejected)를 자동으로 관리해 줍니다. createAsyncThunk를 사용하면 redux-thunk를 별도로 설치하거나 설정할 필요가 없으며, 코드도 훨씬 간결해집니다.

createAsyncThunk 예시:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// 비동기 작업 생성
export const fetchUserData = createAsyncThunk('user/fetchUserData', async (userId) => {
  const response = await fetch(`/api/users/${userId}`);
  return response.json();
});

const userSlice = createSlice({
  name: 'user',
  initialState: {
    user: null,
    status: 'idle', // 'idle', 'loading', 'succeeded', 'failed'
    error: null,
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUserData.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchUserData.fulfilled, (state, action) => {
        state.status = 'succeeded';
        state.user = action.payload;
      })
      .addCase(fetchUserData.rejected, (state, action) => {
        state.status = 'failed';
        state.error = action.error.message;
      });
  },
});

export default userSlice.reducer;

장점:

  • 자동 비동기 상태 관리: pending, fulfilled, rejected 상태를 자동으로 처리해 줍니다.
  • 코드 간소화: 미들웨어 설정 없이 비동기 액션을 쉽게 작성할 수 있습니다.
  • 직관적인 상태 관리: 각 비동기 요청의 진행 상태를 쉽게 파악할 수 있습니다.

2. Redux Saga를 사용한 비동기 흐름 관리

비동기 작업이 복잡하거나 여러 비동기 액션이 연속적으로 발생하는 경우, Redux Saga를 사용하면 더 효율적으로 비동기 흐름을 관리할 수 있습니다. Redux Saga는 제너레이터 함수를 사용하여 비동기 작업을 선언적으로 처리할 수 있고, 복잡한 비동기 로직을 더 쉽게 관리할 수 있습니다.

Redux Saga 설정 예시:

npm install redux-saga
import { call, put, takeEvery } from 'redux-saga/effects';
import { fetchUserDataApi } from './api'; // API 호출 함수

// 사가 생성
function* fetchUserData(action) {
  try {
    const user = yield call(fetchUserDataApi, action.payload);
    yield put({ type: 'FETCH_USER_SUCCESS', payload: user });
  } catch (e) {
    yield put({ type: 'FETCH_USER_FAILURE', message: e.message });
  }
}

// 사가 감시
function* mySaga() {
  yield takeEvery('FETCH_USER_REQUEST', fetchUserData);
}

export default mySaga;

장점:

  • 복잡한 비동기 로직 처리: 비동기 작업이 복잡하거나 다수의 비동기 요청이 필요한 경우 더 유리합니다.
  • 비동기 작업 제어: 비동기 작업의 흐름을 세밀하게 제어할 수 있어, API 요청 순서, 재시도 등의 로직을 쉽게 구현할 수 있습니다.

3. React Query 사용

Redux 대신 React Query를 사용하면, 서버 상태(데이터)를 관리하는 비동기 작업이 훨씬 간단해집니다. React Query는 API 요청을 관리하고, 자동으로 캐싱, 데이터 동기화, 오류 처리 등을 수행해줍니다. React Query는 서버 상태 관리에 특화된 도구로, 비동기 데이터를 관리하는 데 매우 적합합니다.

React Query 예시:

npm install react-query
import { useQuery } from 'react-query';

const fetchUserData = async () => {
  const response = await fetch('/api/user');
  return response.json();
};

const MyComponent = () => {
  const { data, error, isLoading } = useQuery('user', fetchUserData);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return <div>{data.name}</div>;
};

장점:

  • 자동 캐싱 및 동기화: 서버에서 가져온 데이터를 자동으로 캐싱하고, 상태가 변경되면 자동으로 동기화해줍니다.
  • 복잡한 비동기 처리 불필요: 비동기 로직을 자동으로 관리해주므로 비동기 상태 관리를 쉽게 할 수 있습니다.

4. Redux Thunk로 간단한 비동기 작업 처리

Redux Thunk는 가장 널리 사용되는 미들웨어 중 하나로, 간단한 비동기 작업을 처리하기에 충분합니다. 액션 생성 함수에서 비동기 작업을 처리할 수 있으며, 비동기 요청을 실행한 후 상태를 업데이트할 수 있습니다. Redux Thunk는 설정이 간단하고, 작은 규모의 애플리케이션에서 효과적입니다.

Redux Thunk 예시:

npm install redux-thunk
export const fetchUserData = () => async (dispatch) => {
  dispatch({ type: 'FETCH_USER_REQUEST' });
  try {
    const response = await fetch('/api/user');
    const data = await response.json();
    dispatch({ type: 'FETCH_USER_SUCCESS', payload: data });
  } catch (error) {
    dispatch({ type: 'FETCH_USER_FAILURE', error });
  }
};

장점:

  • 설정이 간단: 작은 규모의 비동기 작업에는 충분히 간단하고 효율적입니다.
  • 다양한 비동기 작업 처리 가능: 비동기 API 호출, 상태 변경 로직을 쉽게 작성할 수 있습니다.

5. RTK Query 사용 (Redux Toolkit 포함)

Redux Toolkit에서 제공하는 RTK Query는 비동기 데이터 요청과 상태 관리를 통합하는 도구입니다. API 요청, 캐싱, 데이터 변환 등을 자동으로 처리해 줍니다. 비동기 작업을 처리하는 데 있어서 미들웨어를 직접 설정하지 않아도 되며, 매우 간편한 방식으로 데이터를 처리할 수 있습니다.

RTK Query 예시:

npm install @reduxjs/toolkit react-redux
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

// API 정의
export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: (builder) => ({
    getUserById: builder.query({
      query: (id) => `user/${id}`,
    }),
  }),
});

export const { useGetUserByIdQuery } = api;
const MyComponent = () => {
  const { data, error, isLoading } = useGetUserByIdQuery(1);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return <div>{data.name}</div>;
};

장점:

  • 자동화된 비동기 요청: API 요청, 캐싱, 재요청 등의 복잡한 비동기 작업을 자동으로 처리해줍니다.
  • Redux와의 자연스러운 통합: Redux Toolkit과 자연스럽게 통합되어 비동기 작업을 간편하게 처리할 수 있습니다.

결론

Redux의 비동기 상태 관리를 더 쉽게 하기 위해서는 Redux Toolkit의 createAsyncThunk, RTK Query, Redux Thunk, Redux Saga 등을 활용할 수 있습니다. 상황에 따라 적절한 도구를 선택하여 비동기 작업을 더 효율적으로 관리할 수 있으며, 이러한 도구들은 미들웨어 설정을 최소화하면서도 복잡한 비동기 로직을 효과적으로 처리해줍니다.

728x90