본문 바로가기

개발 공부/React

Redux 미들웨어와 비동기 처리

Redux 미들웨어와 비동기 처리

Redux에서 비동기 처리는 미들웨어를 사용하여 다루게 됩니다.

미들웨어는 Redux의 디스패치액션 사이에서 동작하는 함수로, 비동기 액션을 처리하거나, 액션을 가로채서 특정 로직을 실행할 수 있습니다.

 

1. redux-thunk 미들웨어

redux-thunk는 Redux에서 비동기 작업을 처리하기 위한 가장 널리 사용되는 미들웨어입니다.

Thunk는 액션 생성자가 함수를 반환할 수 있게 해주며, 이 함수는 dispatch와 getState를 인자로 받아 비동기 작업을 처리할 수 있습니다.

npm install redux-thunk

 

Redux store 설정에서 redux-thunk를 미들웨어로 추가합니다

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { appReducer } from './reducer';

const store = createStore(appReducer, applyMiddleware(thunk));

 

그 후, 비동기 작업을 수행하는 액션 생성자를 작성할 수 있습니다. 예를 들어, API 호출을 처리하는 예시입니다.

// actions.ts
import { Dispatch } from 'redux';
import { INCREMENT, DECREMENT } from './actionTypes';

export const fetchData = () => {
  return async (dispatch: Dispatch) => {
    try {
      const response = await fetch('https://api.example.com/data');
      const data = await response.json();
      dispatch({ type: INCREMENT, payload: data });
    } catch (error) {
      console.error(error);
    }
  };
};
  • fetchData는 비동기 함수로, 데이터를 받아오고 이를 dispatch하여 Redux 상태를 업데이트합니다.
  • Thunk는 액션 생성자가 함수를 반환할 수 있게 해 주므로, 비동기 작업을 처리할 때 유용합니다.

2. Redux Toolkit을 통한 비동기 처리 지원

Redux Toolkit은 비동기 작업을 보다 쉽게 처리할 수 있는 방법을 제공합니다.

createAsyncThunk를 사용하면 비동기 액션을 작성할 때 필요한 로직을 간소화할 수 있습니다.

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

interface AppState {
  count: number;
  loading: boolean;
}

const initialState: AppState = {
  count: 0,
  loading: false,
};

export const fetchData = createAsyncThunk('counter/fetchData', async () => {
  const response = await fetch('https://api.example.com/data');
  return response.json();
});

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment(state) {
      state.count += 1;
    },
    decrement(state) {
      state.count -= 1;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchData.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchData.fulfilled, (state, action) => {
        state.loading = false;
        state.count += action.payload.amount;
      })
      .addCase(fetchData.rejected, (state) => {
        state.loading = false;
      });
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
  • createAsyncThunk는 비동기 함수 호출을 처리하는 데 필요한 액션 생성자(pending, fulfilled, rejected)를 자동으로 생성해 줍니다.
  • extraReducers를 통해 createAsyncThunk에서 생성된 액션을 처리하여 상태를 업데이트합니다.
  • loading 상태를 관리하여 비동기 처리 중 로딩을 표시할 수 있습니다.

3. Redux의 장점과 단점

 Redux의 장점

  • 예측 가능한 상태 관리: Redux는 상태를 일관되게 관리하며, 모든 상태 변경이 액션을 통해 일어나므로, 상태 변경의 흐름을 쉽게 추적할 수 있습니다. 이를 통해 디버깅과 유지보수가 용이해집니다.
  • 중앙 집중식 상태 관리: 모든 애플리케이션 상태가 하나의 store에 저장되므로, 상태의 변경을 추적하고, 컴포넌트 간에 상태를 공유하는 것이 편리합니다.
  • 비동기 작업 처리: redux-thunk와 createAsyncThunk 등을 통해 비동기 작업을 손쉽게 처리할 수 있습니다.
  • 강력한 도구 지원: Redux DevTools와 같은 디버깅 도구를 사용하여 상태 변경과 액션 흐름을 쉽게 추적할 수 있습니다.

 Redux의 단점

  • 상태 관리 코드의 복잡성: 상태를 관리하는 데 필요한 코드가 많아지며, 액션, 리듀서, 미들웨어 등 다양한 설정이 필요합니다. 특히, 작은 애플리케이션에서는 불필요하게 복잡할 수 있습니다.
  • 보일러플레이트 코드 증가: createAction, createReducer, createSlice 등을 사용하더라도, 액션 타입, 액션 생성자, 리듀서 등 보일러플레이트 코드가 많아질 수 있습니다.
  • 학습 곡선: Redux의 개념이 처음 접하는 사람에게는 이해하기 어려울 수 있습니다. 액션, 리듀서, 미들웨어 등의 개념을 익히는 데 시간이 걸릴 수 있습니다.

4. Redux의 대안

Redux는 많은 장점을 가지고 있지만, 그 단점들 때문에 다른 상태 관리 라이브러리들이 등장했습니다.

특히 React Context API, Recoil 등의 대안이 인기를 끌고 있습니다.

 

React Context API

React Context API는 React의 내장 기능으로, 컴포넌트 트리 전반에 걸쳐 데이터를 전달할 수 있도록 해줍니다. 상태를 전역으로 관리할 수 있지만, 성능에 민감한 대규모 애플리케이션에서는 최적화가 필요할 수 있습니다.

  • Context API는 간단한 상태 관리에는 적합하지만, 애플리케이션이 커질수록 상태가 깊어지거나 성능 저하가 발생할 수 있습니다.
  • 작은 애플리케이션이나, 간단한 상태를 공유해야 할 때 적합합니다.
import React, { useContext, useState } from 'react';

const AppContext = React.createContext<any>(null);

const AppProvider: React.FC = ({ children }) => {
  const [count, setCount] = useState(0);

  return (
    <AppContext.Provider value={{ count, setCount }}>
      {children}
    </AppContext.Provider>
  );
};

const Counter: React.FC = () => {
  const { count, setCount } = useContext(AppContext);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export { AppProvider, Counter };

 

Recoil

Recoil은 Facebook에서 개발한 상태 관리 라이브러리로, Redux보다 간결하고 더 효율적인 상태 관리 방법을 제공합니다.

Recoil은 복잡한 상태 관리를 단순화하고, 비동기 데이터 처리를 쉽게 할 수 있는 기능을 제공합니다.

  • Recoil의 주요 개념은 AtomSelector입니다. Atom은 상태를 저장하고, Selector는 상태를 변형하거나 비동기 작업을 처리하는 데 사용됩니다.
import { atom, selector, useRecoilState } from 'recoil';

const countState = atom({
  key: 'countState', // unique ID
  default: 0, // initial state
});

const doubleCount = selector({
  key: 'doubleCount',
  get: ({ get }) => {
    const count = get(countState);
    return count * 2;
  },
});

const Counter: React.FC = () => {
  const [count, setCount] = useRecoilState(countState);
  const doubledCount = useRecoilValue(doubleCount);

  return (
    <div>
      <h1>Count: {count}</h1>
      <h2>Doubled: {doubledCount}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

 

  • Atom: 상태의 기본 값과 구조를 정의합니다.
  • Selector: 상태를 변형하거나 비동기 데이터를 처리합니다.

 

'개발 공부 > React' 카테고리의 다른 글

React createContext / useContext  (0) 2024.12.18
Next.js / React  (1) 2024.12.16
Redux와 React 연동 / Redux Toolkit  (0) 2024.11.14
React Redux  (1) 2024.11.12
React useContext, useValue  (1) 2024.10.29