Redux-saga 사용법

성민개발로그·2022년 1월 2일
0

redux

목록 보기
3/4

Redux-saga란?

기존에 Redux는 dispatch가 동기적으로 바께 수행이안된다. 백엔드에서 데이터를 store로 불러올때 동기적으로 불러오면 문제가 생길수가있다. 그래서 비동기적으로 불러올수 있는 방법이 필요했다 그래서 필요했던게 redux-saga 이다 redux-saga는 redux의 middleware이다 dispatch를 비동기적으로 수행할수있도록 도와준다. 또한 기존에 redux에서 보완하지 못한 부득이하게 dispatch를 하번해야할 수행을 두번 실행하는경우 두번다 실행이되서 문제가 생기는 경우가 있는데 이런점을 보완하기위해 saga에서 각종 내부 메소드를 활용하여, 사용자의 부주의로 인하여 동일한 api를 여러번 req할 경우 가장 최근 or 가장 마지막(takeleast) req의 res만 받아오도록 하는 기능도 있습니다.

선행지식(제네레이터)

본격적으로 사용법을 알아보기 전, saga에서 사용하는 자바스크립트 문법 제너레이터를 알아야 이해가 갈것이다 먼저 꼭 제네레이터 를 어느정도 익히고 오는걸 권장한다.

1. 설치및추가

설치

npm install redux-saga

저는 store폴더에 따로 store에 대한 기본 설정을 따로 configureStore.js에다가 작성을 합니다.

// store/configureStore.js

import { createWrapper } from "next-redux-wrapper";
import { applyMiddleware, compose, createStore } from "redux";
import {composeWithDevTools} from 'redux-devtools-extension';
import createSagaMiddleware from 'redux-saga';// 1.createSagaMiddleware 를 redux-saga에서 추가를 합니다.
import reducer from '../reducers';
import rootSaga from '../sagas'; // 2.sagas폴더를 만들어 rootSaga.js를 만들어 saga에 대한 내용을 안에다가 넣습니다.

const configureStore = () =>{
    const sagaMiddleware = createSagaMiddleware();//3. createSagaMiddleware 활성화.

    const middlewares = [sagaMiddleware]; //4.middlewares배열에 추가.

    const enhancer = process.env.NODE_ENV === 'production' ?
    compose(applyMiddleware(...middlewares)): 
    composeWithDevTools(applyMiddleware(...middlewares)); //기본 redux tool 활성화 설정
  
    const store = createStore(reducer,enhancer);

    store.sagaTask = sagaMiddleware.run(rootSaga);//5.rootSaga와 sagaMiddleware 바인딩해준다.

    return store;
};

const wrapper = createWrapper(configureStore,{
    debug:process.env.NODE_ENV === 'development',
});

export default wrapper;

2.rootSaga 만들기.

리덕스 rootReducer 처럼 각자 saga파일 컴바인역할을 해준다.

import {all,fork} from 'redux-saga/effects';
import userSaga from './user'; //유저정보를 담은 userSaga
import postSaga from './post'; //포스트정보 담은 postSaga


export default function* rootSaga() {//1
    yield all([ //all: 배열을 받고 받은 이펙트들을 등록해주는 역할
        fork(userSaga),
        fork(postSaga),
    ]);
}

all은 배열을 받고, 받은 이펙트를 등록.
fork 함수 실행할때 쓴다.

3.각자 saga 만들기.

여기서는 로그인 액션만 예제로 작성해보겠다.
작성할때 주석에 작성한 숫자 흐름으로 작성하는것이 이해하는데 편할것이다.

import { all,fork,takeLatest,put,delay} from "@redux-saga/core/effects";
import { LOG_IN_SUCCESS,LOG_IN_FAILURE,LOG_IN_REQUEST} from "../reducers/user";
import axios from "axios";

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

function* logIn(action){//3
    try{
    const result = yield call(logInAPI,action.data);//call: 비동기에서 await 같은 개념이다.

    //yield delay(1000);  백엔드 구축 안했을때 비동기 느낌 나기 위해서 1초딜레이 하고 실행.
    yield put({
        type:LOG_IN_SUCCESS,
        data:action.data
    })

    }catch(err){
        yield put({ // redux 액션으로 보내줌. put:dispatch라고 생각하면 편하다.
            type:LOG_IN_FAILURE,
            data:err.response.data,
        })
    }
}
}

function* watchLogIn(){//2.
    yield takeLatest(LOG_IN_REQUEST,logIn); //take 한번만 실행되고 이벤트 삭제된다.
    // 이벤트 리스너 느낌을 준다.
}

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

call 비동기 개념에서는 await 같은 개념이다 실행이 끝나야 다음 코드로 진행할수 있다.
takeLatest 클릭 실수로 2번 했을때, 앞 이벤트 무시 마지막 이벤트 실행(보통 이거 많이씀)
이미 완료됬다면 실행해줌 -> 둘다 팬딩이면 뒤에꺼만
주의! front -> back으로 2번 req를 보내긴함 -> 그러나 back->front로 res는 1번 보냄 (즉, 서버단에 저장 2번됬는지 확인 필요)
즉 : 새로고침하면 2개가 반영될수있음
위에꺼를 막기위해 throttle가 있음
put redux 에서 dispatch와 비슷하다고 생각하면된다.
take한번만 실행되고 이벤트 삭제됨
takeEvery 한번 실행되도, 이벤트 계속 리슨

4.reducer 에서 user 구현.

export const initialized ={
    logInLoading:false,//로그인 시도중
    logInDone: false,
    logInError:null,
    me:null,
    signUpData:{},
    loginData:{}
}

export const LOG_IN_REQUEST = 'LOG_IN_REQUEST';
export const LOG_IN_SUCCESS = 'LOG_IN_SUCCESS';
export const LOG_IN_FAILURE = 'LOG_IN_FAILURE';


export const loginRequestAction =(data)=>{

    return  {
        type:LOG_IN_REQUEST,
        data
    }
}


const dummyUser = (data) =>({
    ...data,
    nickname:'sungmin',
    id:1,
    Posts:[],
    Followings:[],
    Followers:[],
})

const reducer = (state=initialized,action)=>{
    switch(action.type){
        case LOG_IN_REQUEST:
            return {
                ...state,
                logInLoading:true,
                logOutError:null,
                logInDone:false
            }
        case LOG_IN_SUCCESS:
            return {
                    ...state,
                    logInLoading:false,
                    logInDone:true,
                    me:dummyUser(action.data),
            }
        case LOG_IN_FAILURE:
            return {
                ...state,
                logInDone:false,
                logInLoading:false,
                logInError:action.data
            }
        default:
            return state;
    }

}


export default reducer;

0개의 댓글