본문으로 바로가기

redux-thunk와 함께 비동기 redux 처리 미들웨어의 양대산맥을 이루고 있습니다. redux-thunk가 제일 배우기 쉽다고 하고, saga는 어려운 대신 기능이 강력합니다.

 

일반 redux는 동기적인 동작만 가능합니다. 예를 들어 signUpAction 액션을 취한 후 10초 후에 signUpSuccess 액션을 하고 싶다면, Redux 단에서는 취해줄 수 있는 것이 없습니다.

export const signUpSuccess = {
  type: SIGN_UP_SUCCESS,
};

export const signUpAction = (data) => {
  return {
    type: SIGN_UP,
    data: data,
  };
};

 

 

설치

npm i redux-saga

 

redux-saga/effect 중에서는

all, call, fork, put, takeLatest, takeEvery, take, delay, throttle, cancel, race, select, debounce 등이 주로 사용됩니다.

 

yield는 일반 generator에서 사용하는 중단점입니다. (이건 saga의 이펙트가 아니죠)

 

call은 함수의 동기적 호출 (예를 들어 로그인 시도시 비동기적으로 처리하면 안되겠죠.)

fork는 함수의 비동기적 호출 (비동기적으로 처리하고 싶으면 fork를 씁시다)

put은 액션의 dispatch를 의미합니다.

 

takeLatest, takeEvery

 

보통 generator에서 while(true)를 통해 무한의 개념을 구현하고, saga에서도 동일하게 사용해도 되지만, 이를 단축시켜주는 takeLatest와 takeEvery를 saga에서 제공해줍니다.

 

takeEvery는 while(true)의 기능을 대체할 수 있습니다.

 

takeLatest는 거의 동시에 여러번 액션이 가해졌다면 그 중 마지막의 액션한 적용하는 것입니다. 로그인 버튼이나 로그아웃 버튼을 여러분 눌렀을 때 누를 때마다 요청이 갈텐데 해당 요청을 모두 리덕스로 처리할 필요 없이 마지막 요청한 처리하면 됩니다. 참고로, 이전 요청이 끝나지 않은 게 있다면 이전 요청을 취소합니다.

 

 

루트 사가

import { all, call } from "redux-saga/effects";
import user from "./user";
import post from "./post";

export default function* rootSaga() {
  yield all([call(user), call(post)]);
}

 

./user 부분의 saga

import { all, delay, fork, put, takeLatest } from 'redux-saga/effects';
import axios from 'axios';

import {
  LOG_IN_FAILURE,
  LOG_IN_REQUEST,
  LOG_IN_SUCCESS,
} from '../reducers/user';

function logInAPI(data) {
  return axios.post('/api/login', data);
}

function* logIn(action) {
  try {
    console.log('saga logIn');
    // const result = yield call(logInAPI);
    yield delay(1000);
    yield put({
      type: LOG_IN_SUCCESS,
      data: action.data,
    });
  } catch (err) {
    console.error(err);
    yield put({
      type: LOG_IN_FAILURE,
      error: err.response.data,
    });
  }
}

function logOutAPI() {
  return axios.post('/api/logout');
}

function* logOut() {
  try {
    // const result = yield call(logOutAPI);
    yield delay(1000);
    yield put({
      type: LOG_OUT_SUCCESS,
    });
  } catch (err) {
    console.error(err);
    yield put({
      type: LOG_OUT_FAILURE,
      error: err.response.data,
    });
  }
}
function* watchLogIn() {
  yield takeLatest(LOG_IN_REQUEST, logIn);
}


export default function* userSaga() {
  yield all([
    fork(watchLogIn),
  ]);
}

 

 

 

 

 

그리고 saga를 사용하기 위해서 기존 redux의 미들웨어로 붙이는 방법은 다음과 같습니다. Next-redux-wrapper와 함께 사용했습니다. 순수 React를 사용하는 경우도 이와 비슷하게 구성하시면 됩니다.

import { createStore, applyMiddleware, compose } from "redux";
import { createWrapper } from "next-redux-wrapper";
import { createLogger } from "redux-logger";
import { composeWithDevTools } from "redux-devtools-extension";

// 루트 디듀서
import rootReducer from "./index";

// saga 덧붙이기. createSagaMiddleware 메서드와 따로 정의한 rootSaga를 불러옵니다.
import createSagaMiddleware from "redux-saga";
import rootSaga from "../sagas";

const configureStore = () => {
  const logger = createLogger();
  
  // 미들웨어 생성
  const sagaMiddleware = createSagaMiddleware();

  // 미들웨어 덧붙이기
  const enhancer = compose(composeWithDevTools(applyMiddleware(logger, sagaMiddleware)));

  const store = createStore(rootReducer, enhancer);
  
  // store에 sagaTask 붙이기
  store.sagaTask = sagaMiddleware.run(rootSaga);

  return store;
};

const wrapper = createWrapper(configureStore, { debug: true });

export default wrapper;

 

 

 

generator에 대한 내용은 아래를 살펴봅시다.

 

https://darrengwon.tistory.com/739

 

ES6 제너레이터, 무한 개념 구현하기

ES6에서 도입된 제너레이터(Generator) 함수는 iterable을 생성하는 함수이다. 다음은 제너레이터를 생성하고, yield로 중단점을 설정하며, next()를 통해 실행한 예이다. function* generator() { console.log(1)..

darrengwon.tistory.com

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체