Redux - Redux saga (and thunk)

ryan·2022년 6월 25일
0

redux thunk

redux thunk

  • 비동기로 redux 작업(action)을 처리(dispatch)할 수 있도록 해주는 middleware(기능 확장).
    - thunk : '지연된 함수'를 의미
  • 하나의 액션에 dispatch를 여러번 구현할 수 있고, 하나의 비동기 액션에 여러 동기 액션을 넣을 수 있음.
  • pending, fullfilled, rejected에 대한 action을 작성해야 함.

코드 예시

thunk은 비동기 액션 크리에이터만 추가되고, 
  dispatch를 여러번 실행할 수 있다는 것이 유일한 기능이자 장점.
  
export const loginAction = (data) =>{
    return (dispatch,getState) =>{
        // getState initialState 받을 수 있음.
        const state= getState();
        dispatch(loginRequestAction());
        axios.post('api/login')
        .then((res)=>{
            dispatch(loginSuccessAction(res.data));
        })
        .catch((err)=>{
            dispatch(loginFailureAction(err))

        })
    }
}


// pending, fullfilled, rejected에 대한 action을 작성.
export const loginRequestAction = (data) => {
  return {
    type: 'LOG_IN',
    data,
  };
};

export const loginSuccessAction = (data) => {
  return {
    type: 'LOG_IN',
    data,
  };
};

export const loginFailureAction = (data) => {
  return {
    type: 'LOG_IN',
    data,
  };
};

redux saga

redux saga

  • redux saga도 thunk와 동일하게 비동기로 redux 작업을 처리하는 미들웨어.
  • redux thunk와의 차이점은 기능이 훨씬 더 다양함.
  • generator function 기능을 활용함.

generator function 예시

// 중단점이 있는 함수. yield를 넣으면 그 부분에서 멈춤
// 중간에 함수를 멈출 수 있다는 장점이 있음.

const generator = function* rootSaga() {
  console.log(1);
  yield;
  console.log(2);
  yield;
  console.log(3);
  yield 4;
};

generator.next(); // 1 {value:undefined,done:false}
generator.next(); // 2 {value:undefined,done:false}
generator.next(); // 3 {value:4,done:false}
generator.next(); // {value:undefined,done:true}

--------------

// 무한반복문을 활용해서 원할 때 같은 함수(또는 객체)를 계속해서 호출할 수 있음
const gen = function* () {
  while (true) {
    yield 'infinite';
  }
};

const g = gen();
g.next(); // {value:'infinite',done:false}

프로젝트 적용

폴더 구조

  • reducers의 파일 분리 구조에 맞게 saga 파일도 동일하게 분류하였음.

1. saga 세팅

  • sagas/index.js 에서 rootSaga 내부에 분리한 saga를 넣고 all로 동시 실행.

    dispatch 처리 흐름
    컴포넌트에서 'LOG_IN_REQUEST' 요청
    saga watchLogin - login generator함수 호출
    비동기 작성 성공 / 실패에 따라 dispatch 실행.

sagas/index.js
import {all, fork} from 'redux-saga/effects';
import postSaga from './post';
import userSaga from './user';

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

------------------
------------------

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

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

// logIn generator function 
function* logIn(action) {
  try {
    const result = yield call(logInAPI, action.data); 
 	// const result = yield fork(logInAPI, action.data); 
    // call : 매개변수로 주어진 함수를 동기적으로 호출함.
    // fork : 매개변수로 주어진 함수를 비동기적으로 호출함.
    yield put({type: 'LOG_IN_SUCCESS', data: result.data});
    // put : dispatch와 동일한 용도로 사용됨.
  } catch (err) {
    yield put({
      type: 'LOG_IN_FAILURE',
      data: err.response.data,
    });
  }
}


function* watchLogin() {
  yield takeLatest('LOG_IN_REQUEST', logIn); 
  // take- API는 'LOG_IN_REQUEST' 액션이 동기적으로 실행될 수 있도록 함.
  // takeLatest : 연속해서(동시에) 여러 요청이 발생했을 때 마지막 요청만 실행됨. (백엔드에서 중복 데이터에 대한 관리를 해주어야 함.)
}

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

2. store 세팅

import {createWrapper} from 'next-redux-wrapper';
import reducer from '../reducers';
import {configureStore} from "@reduxjs/toolkit"
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../sagas';

const sagaMiddleware = createSagaMiddleware();

const configureStore = () => {
  
const store = configureStore({
  reducer: {reducer},
  middleware:[sagaMiddleware]
});  
  
  store.sagaTask = sagaMiddleware.run(rootSaga);
  return store;
};
profile
프론트엔드 개발자

0개의 댓글