Redux Saga를 이용한 API 요청 상태 관리

dongwon·2021년 3월 25일
0

Redux

목록 보기
4/4

Redux Saga를 이용한 API 요청 상태 관리

Redux Saga를 이용해 비동기적으로 api를 호출할 때 요청에 대한 5가지 상태 ( 요청 시작, 로딩 중, 요청 성공, 요청 실패, 로딩 끝 ) 관리를 다루는 글입니다.

1. 파일 구성

  • 네트워크 요청을 위한 /lib/api.js
  • loading 상태를 관리할 reducers 파일 /reducers/loading.js
  • reducer, saga를 작성할 reducers 파일 /reducers/data.js
  • useSelector, useDispatch를 사용할 component

2. 네트워크 요청 함수 작성

// lib/api.js
import axios from "axios";

export const getCategories = () =>
  export const getStores = () => axios.get('http://localhost:4000/stores');

json-server를 이용하여 만든 json 파일 🔗 json-server 사용

3. loading 관리 reducer 작성

// 액션 타입 정의
const START_LOADING = "loading/START_LOADING";
const FINISH_LOADING = "loading/FINISH_LOADING";

// 액션 생성 함수
export const startLoading = (requestType) => ({
  type: START_LOADING,
  requestType,
});
export const finishLoding = (requestType) => ({
  type: FINISH_LOADING,
  requestType,
});

// 초기화
const initialStete = {};

// 리듀서 작성
function loading(state = initialStete, action) {
  switch (action.type) {
    case START_LOADING:
      return {
        ...state,
        // action.payload = requestType
        // 즉, requestTtype: true
        [action.payload]: true,
      };
    case FINISH_LOADING:
      return {
        ...state,
        [action.payload]: false,
      };
    default:
      return state;
  }
}

export default loading;

코드 설명

  • loading start / finish 두 가지 경우의 action 타입을 정의
  • 액션 생성 함수의 인자로 requestType 받아온다.
    (requestType은 saga가 실행될 때 saga의 액션 타입이 들어가게 됨)
  • loading reducer 작성.
    action.payload는 requestType을 의미.
    requestType이 data/GET_STORES 라면, data/GET_STORES : true

4. 기본적인 reducer, saga 작성

// reducers/data.js
import * as api from "../lib/api";
import { call, put, takeLatest } from "redux-saga/effects";

// loading 객체 생성 함수 가져오기
import { finishLoding, startLoading } from "./loading";

// 액션 타입 정의
const GET_STORES = "data/GET_STORES";
const GET_STORES_SUCCESS = "data/GET_STORES_SUCCESS";
const GET_STORES_FAILURE = "data/GET_STORES_FAILURE";

// 액션 생성 함수
export const getStores = () => ({ type: GET_STORES });

// SAGA 작성
function* getStoresSaga() {
  yield put(startLoading(GET_STORES));
  try {
    const stores = yield call(api.getStores);

    yield put({
      type: GET_STORES_SUCCESS,
      payload: stores.data,
    });
  } catch (e) {
    yield put({
      type: GET_STORES_FAILURE,
      payload: e,
      error: true,
    });
  }
  yield put(finishLoding(GET_STORES));
}

// SAGA 통합
export function* dataSaga() {
  yield takeLatest(GET_STORES, getStoresSaga);
}

// 초기값 설정
const initialStete = {
  stores: null,
};

// 리듀서 작성
function data(state = initialStete, action) {
  switch (action.type) {
    case GET_STORES_SUCCESS:
      return {
        ...state,
        stores: action.payload,
      };
    case GET_STORES_FAILURE:
    default:
      return state;
  }
}

export default data;

코드 설명

  • 3가지 aciton 타입 정의. 각각 요청, 요청 성공, 요청 실패(GET_STORES, GET_STORES_SUCCESS, GET_STORES_FAILURE)
  • saga 작성. dispatch로 인해 요청 액션 getStores가 호출 되면서 saga 실행
  • saga가 실행되면서 첫 번째 startLoading(GET_STORES)를 통해 store에는 data/GET_STORES: true 인 상태. 즉, 로딩 중 상태.
  • call을 통해 네트워크 요청, data를 저장하고, 요청에 성공했다면 GET_STORES_SUCCESS action, 실패했다면 GET_STORES_FAILURE action 실행

5. useSelector, useDispatch를 사용할 component

// components/allStores.js
// 개인적으로 사이드 프로젝트에서 사용하는 컴포넌트 입니다.
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getStores } from '../reducers/data';
const AllStores = () => {
  const stores = useSelector((state) => state.data.stores);
  const loadingStores = useSelector((state) => state.data.GET_STORES);

  const storesDispatch = useDispatch();
  useEffect(() => {
    storesDispatch(getStores());
  }, [getStores, storesDispatch]);

  return (
    <div className="allStores">
      {loadingStores && '로딩 중'}
      {!loadingStores &&
        ....
      ))}
    </div>
  );
};

export default React.memo(AllStores);

코드 설명

  • useSelector Hooks를 사용하여 store(리덕스)에 접근, stores와 loading 상태를 저장.

    • state.data.GET_STORES는 loading reducer에서 정의한 data.GET_STORES: true ( 로딩 중 ) 혹은 data.GET_STORES: flase ( 로딩 끝 ) 정보
  • useDispatch를 이용해 네트워크 요청 action getStores dispatch

  • && 연산자를 loadingStores의 상태에 따라 하나의 상태만을 위한 렌더링

profile
데이원컴퍼니 프론트엔드 개발자입니다.

0개의 댓글