[React] redux-saga

개발자_범·2023년 8월 20일
0
post-thumbnail

redux-saga는 Redux Saga는 Redux와 함께 사용되는 미들웨어 라이브러리로, 비동기 작업을 관리

Redux Saga에서 사용되는 제너레이터(Generator) 함수를 사용하여 비동기 작업을 동기적으로 다룰 수 있는 방식을 제공, 액션들 간의 순서를 조절하고 상태 변화를 관리할 수 있다.
즉, ⇒ axios요청같이 데이터를 가져오는 비동기 동작이나 브라우저의 캐시에 접근하는 그런 사이드 이펙트들을 관리할 수 있게 해주는 도구

이러한 원리로 액션을 감지하고, 특정 액션이 발생했을때 우리가 원하는 자바스크립트 코드를 실행시켜줌.

import { createStore, combineReducers, compose, applyMiddleware } from "redux";

import { persistStore, persistReducer } from "redux-persist";

import storage from "redux-persist/lib/storage";

import { reducer as dataReducer } from "./reducer";
import createSagaMiddleware from "redux-saga";
import rootSaga from "./sagas/rootSaga";

const persistConfig = {
  key: "",
  storage,
  whitelist: [],
};

const persistedReducer = persistReducer(persistConfig, dataReducer);

const devTools =
  process.env.NODE_ENV !== "production" &&
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    : null;

const composeEnhancers = devTools || compose;

const appReducer = combineReducers({
  data: persistedReducer,
});

// sagaMiddleware 선언(미들웨어로 사용)
export const sagaMiddleware = createSagaMiddleware();
const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware));

// redux의 store 생성, 리듀서와 미들웨어 사용
export const store = createStore(appReducer, enhancer);
export const persistor = persistStore(store);

sagaMiddleware.run(rootSaga);

1. 사가미들웨어 생성 -> sagaMiddleware

  • 리덕스 사가(Redux Saga)에서 제공하는 createSagaMiddleware API를 사용하면 사가 미들웨어(Saga Middleware)를 생성.

  • 그리고 생성된 사가 미들웨어는 리덕스(Redux)에서 제공는 applyMiddleware API를 호출할 때 인자로 넘겨 리덕스 미들웨어(Redux Middleware)로 추가

2. 미들웨어 실행 -> sagaMiddleware.run(rootSaga)

사가 미들웨어(Saga Middleware)의 run 메서드를 통해 사가(Saga)를 실행
rootSaga는 제네레이터 함수(function *)을 담은 사가!

function* 무엇일까?
함수의 코드 블록을 한 번에 실행하지 않고 함수 코드 블록의 실행을 일시 중지했다가 필요한 시점에 재시작할 수 있는 함수이고 yield 키워드를 사용하여 제너레이터 함수 내에서 값을 생성하거나 값을 반환하며 함수의 실행을 일시 중단할 수 있다.

yeild는 무엇일까?
yield를 사용하여 비동기 작업(예: API 호출 등)을 순차적으로 실행할 수 있다. 제너레이터 함수 내에서 yield 키워드를 만나면 해당 작업이 완료될 때까지 함수 실행이 중단되며, 그 후에 다음 작업으로 이동한다.

Redux-Saga에는 function* 사용되는 헬퍼함수(effect)가 있다.

  • fork
  • call
    call()과 fork()는 단순히 함수를 호출하는 용도로 사용한다. 사용방법은 동일하다. call()과 fork()의 차이는 call()은 동기적으로 함수를 호출할 때, fork()는 비동기적으로 함수를 호출할 때 사용한다.

다들 아시겠지만, 비유를 들자면

call의 경우

당신은 그룹 내에서 주방에서 요리를 준비하고 있다.. 각 사람이 식사를 기다리고 있으며, 요리가 완성되기를 기다린다. 요리가 완료되면 각 사람에게 식사를 제공하고, 그 다음 사람들은 식사를 마칠 때까지 기다림. 함수 호출(call)이 완료될 때까지 기다리며, 다음 코드 실행은 블록.

fork의 경우

당신은 그룹 내에서 여러 요리를 동시에 준비하고 있다. 각 요리사는 독립적으로 요리를 하며, 한 요리가 완료되어도 나머지 요리는 계속 진행된다. 각 요리가 완료되면 각자의 식사를 받고 먹기 시작함. 함수 호출(fork)이 블록되지 않고 바로 다음 코드로 진행. 호출된 함수의 실행은 독립적으로 진행된다.

  • take : 액션의 발생을 감시하며, 해당 액션이 발생할 때까지 제너레이터 함수의 실행을 블록하는 역할을 합니다. 이를 통해 비동기 작업을 조율하고 액션과 상태를 효과적으로 관리할 수 있습니다.
  • put: 특정 action을 dispatch 시켜준다. (이를 통해 reducer를 통한 상태변화가 일어난다.)
yield put({
        type: 'LOG_IN_SUCCESS',
        data: result.data
    });

이런식으로 말이다.

  • takeevery : 모든 요청 실행
  • takelatest : 마지막 요청 실행
  • select
ex) const A = yield select(state ⇒ state.~~)
(redux useselct와 같음)
  • cancel : 작업 요청 취소
  • all : 여러 개의 비동기 작업을 병행적으로 동시에 실행하고 그 결과를 기다리는 역할 → 제네레이터 함수 내에서 사용된다는 점을 제외하면 promise.all과 비슷함
import axios from 'axios';
import {all, call, fork, put, takeLatest} from 'redux-saga/effects';
import { SwpLogRes } from '../actions/LogAction';
import { LogType } from '../constants/actionType';
import React from 'react';

axios.defaults.baseURL = 'http://localhost:8070';

function logReq() {
  const result = axios
    .get('/api/logs/log.log')
    .then((res) => {
      console.log('[LOG] Log 통신 성공');
      return res.data;
    })
    .catch((err) => {
      console.log('[LOG] Log 통신 에러');
      return err;
    });
  return result;
}

function* postSwpLogReq() {
  try {
    const logs = yield call(logReq);    
    yield put(SwpLogRes(logs + <br />));
  } catch (e) {
    console.log(e);
  }
}

function* watchAlert() {
  yield takeLatest(LogType.LOG_REQ, postSwpLogReq);
}

다음은 제네레이터 함수를 사용한 saga통신이다.

  • LogType.LOG_REQ 액션이 감지되면 postSwpLogReq를 실행.
  • postSwpLogReq()를 실행하여 logReq()라는 함수를 call()한다.
  • call()값을 logs에 담고 SwpLogRes 액션을 실행.

장점

  1. 비동기적으로 API를 호출하여 데이터를 가져오는 일과 같은 Side Effect를 쉽게 처리하기 위해 사용하는 라이브러리이다. 때에 따라 기존 요청을 취소 처리해야 한다거나 여러 개의 API를 순차적으로 호출해야 하는 등의 좀 더 까다로운 비동기 작업을 다루는 상황에 유용

단점

  1. 적어야 코드가 너무 많다. action, saga, reducer, action type, generator 함수 등등
  2. props를 많이 써서 컴포넌트를 줄이려고하는데 → redux같은 무거운 전역상태관리 라이브러리가 필요할까? 라는 생각을 하게 되었다.
  3. 일부 복잡한 비동기 상황에서 redux-saga를 사용하면 코드의 복잡성이 증가.
  4. 러닝커브.. (난 어려웠던 것 같음)
profile
GanziMan 입니다.

1개의 댓글

comment-user-thumbnail
2023년 12월 5일

Such a good post.

답글 달기