[리액트] Redux-thunk

River Moon·2023년 9월 5일
0
post-thumbnail

Redux-thunk

비동기 통신을 위한 redux-middleware

Redux-Thunk를 이해하고 사용하기

Redux는 자바스크립트 앱에서 상태 관리를 위한 라이브러리다. 하지만 Redux 자체는 비동기 작업을 처리하기에는 불편한 점이 있다. 이럴 때 도움이 되는 것이 Redux-Thunk 미들웨어다. 이 포스트에서는 Redux-Thunk가 무엇인지, 왜 필요한지, 어떻게 사용하는지에 대해 알아보자.

Redux-Thunk란?

Redux-Thunk는 Redux의 미들웨어 중 하나로, 비동기 작업을 쉽게 처리할 수 있게 해준다. 기본적으로 액션 생성자가 객체 대신 함수를 반환할 수 있게 한다. 이 함수는 dispatchgetState 두 개의 인자를 받아 비동기 작업을 수행할 수 있다.

const fetchData = () => {
  return (dispatch, getState) => {
    fetch('some-api')
      .then(response => response.json())
      .then(data => dispatch({ type: 'FETCH_SUCCESS', payload: data }));
  };
};

왜 Redux-Thunk가 필요한가?

순수 함수의 제한

Redux의 액션 생성자와 리듀서는 순수 함수여야 한다. 순수 함수는 같은 인자에 대해 항상 같은 값을 반환해야 하고, 외부 상태에 의존하거나 변경하지 않아야 한다. 따라서 API 호출 같은 비동기 작업을 하기 어렵다.

미들웨어의 도입

Redux-Thunk와 같은 미들웨어를 사용하면 액션 생성자 내에서 비동기 작업을 할 수 있다. 미들웨어는 액션이 디스패치되어 리듀서에서 처리되기 전에 중간에 실행되는 코드다.

Redux-Thunk를 활용한 비동기 작업: 상세 예제

프로젝트 구조

my-redux-thunk-app/
├── src/
│   ├── store/
│   │   ├── actions/
│   │   │   └── fetchData.js
│   │   ├── reducers/
│   │   │   ├── dataReducer.js
│   │   │   └── index.js  
│   │   └── index.js
│   ├── App.js
│   └── index.js
└── package.json

필요한 패키지 설치

프로젝트 폴더에서 터미널을 열고 다음 명령어를 실행해 필요한 패키지를 설치한다.

npm install redux redux-thunk react-redux

store/reducers/dataReducer.js: 리듀서 설정

src/store/reducers/dataReducer.js 파일을 생성하고 다음과 같이 작성한다.

const initialState = {
  loading: false,
  data: null,
  error: null,
};

// dataReducer는 액션에 따라 state를 변경하는 함수다.
const dataReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'FETCH_START':
      // 로딩 시작할 때
      return { ...state, loading: true };
    case 'FETCH_SUCCESS':
      // 데이터 가져오는 것이 성공했을 때
      return { ...state, loading: false, data: action.payload };
    case 'FETCH_ERROR':
      // 에러 발생했을 때
      return { ...state, loading: false, error: action.payload };
    default:
      return state;
  }
};

export default dataReducer;

store/reducers/index.js: rootReducer 작성

src/store/reducers/index.js 파일을 만들고 다음과 같이 작성해.

import { combineReducers } from 'redux';
import dataReducer from './dataReducer';
// 여러 개의 리듀서를 하나로 합친다.
const rootReducer = combineReducers({
  data: dataReducer,
});

export default rootReducer;

store/index.js: 스토어 설정

src/store/index.js 파일을 생성하고 아래와 같이 작성한다.

// 스토어 생성. 미들웨어로 thunk 사용.
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

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

export default store;

store/actions/fetchData.js: 액션 생성자

src/store/actions/fetchData.js 파일을 생성하고 다음과 같이 작성한다.

// 비동기 작업을 위한 액션 생성자다.
export const fetchData = () => {
  return (dispatch) => {
    // 데이터를 가져오기 시작하면 'FETCH_START' 액션을 디스패치한다.
    dispatch({ type: 'FETCH_START' });

    fetch('<https://jsonplaceholder.typicode.com/todos/1>')
     .then((response) => {
        if (!response.ok) {
          // 에러가 발생하면 'FETCH_ERROR' 액션을 디스패치한다.
          throw new Error(`${response.status}`);
        }
        return response.json();
      })
      .then(data => {
        // 성공하면 'FETCH_SUCCESS' 액션을 디스패치한다.
        dispatch({ type: 'FETCH_SUCCESS', payload: data });
      })
      .catch(error => {
        // 에러 발생시 'FETCH_ERROR' 액션을 디스패치한다.
        dispatch({ type: 'FETCH_ERROR', payload: error });
      });
  };
};

App.js: 앱의 메인 코드

src/App.js 파일에서는 다음과 같이 작성한다.

// useDispatch와 useSelector로 리덕스 스토어와 상호작용한다.
import { useDispatch, useSelector } from "react-redux";
import { fetchData } from "./store/actions/fetchData";

const App = () => {
  const dispatch = useDispatch();
  // 스토어의 data 상태를 가져온다.
  const { loading, data, error } = useSelector((state) => state.data);

  const handleClick = () => {
    // 버튼 클릭하면 fetchData 액션을 디스패치한다.
    dispatch(fetchData());
  };

  return (
    <div>
      <button onClick={handleClick}>데이터 가져오기</button>
      {/* 상태에 따른 UI 렌더링 */}
      {loading ? (
        <h1>Loading...</h1>
      ) : error ? (
        <h1>Error: {error.message}</h1>
      ) : (
        data && <h1>{data.title}</h1>
      )}
    </div>
  );
};

index.js: 앱 실행

src/index.js 파일에서는 다음과 같이 작성한다.

//Provider으로 감싸서 리덕스 스토어를 제공한다.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { Provider } from "react-redux";
import store from "./store";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

이제 store 폴더 안에서 액션 생성자와 리듀서, 그리고 스토어 설정까지 모두 관리하고 있다. 이 구조를 따르면 프로젝트 관리가 더 쉬워질 것이다.

결론

Redux-Thunk는 Redux와 함께 사용되어 비동기 작업을 간단하게 처리할 수 있게 해주는 미들웨어다. 이를 통해 액션 생성자는 비동기 로직을 캡슐화하며, 리듀서는 순수 함수를 유지할 수 있다. 비동기 처리가 필요한 경우 Redux-Thunk를 활용해보면 좋을 것이다.

profile
FE 리버

0개의 댓글