redux-saga

2021년 02월 21일 by Xion

    redux-saga 목차

| Redux-saga란?

redux-saga 는 리액트/리덕스 애플리케이션의 부수효과(Side-Effect) 라고 합니다.
특정 액션들이 스토어에 보내질 때, 여러 작업들을 함께 실행하기 위한 내부적인 함수를 감싸는 몇몇 helper effect 를 제공하며,
function* 문법을 사용합니다.

eX) fetching 이나 브라우저 캐시에 접근하는 순수하지 않은 비동기 동작들을 더 쉽고 좋게 만드는 것을 목접으로 하는 라이브러리입니다.

  1. Ajax 콜
  2. 비동기 타이머
  3. 애니메이션 후 콜백
  4. 요청 중 취소
  5. 스로틀링
  6. 디바운싱
  7. 페이지 이동

구성

Saga 는 액션을 구독하는 Watcher 와 실제 작업을 수행하는 Worker 의 구성을 따릅니다.

  • Watcher
    • watcher 함수
  • Worker
    • exmapleWorker 1
    • 2
    • ...

Watcher는 action의 상태를 subscribe 하는 역할을 합니다.
Worker는 exapleWorker1, 2, .. 등과 같이 실제 작업을 수행하는 부분이며

먼저, Saga를 실행하기 위해 redux-saga 미들웨어를 리덕스 스토어에 연결하는 방법을 알아봅시다.

모듈 설치

yarn add redux-saga

SagaObject 들을 redux-saga 미들웨어에 yield 하는 제너레이터 함수로 구현되었습니다.

yield된 Object 들은 미들웨어에 의해 해석되는 명령의 한 종류입니다.

put : Saga에선는 Saga-effect 라고 부르는 예 중 하나입니다. 이펙트는 미들웨어에 의해 수행되는 명령을 담고 있는 javascript 객체입니다.
putredux store에 dispatch 하는 역할 을 의미합니다.
(= Redux의 dispatch와 동일합니다.)

takeEvery : 가장 흔히 사용되는 함수입니다. takeEvery는 동시에 시작되는 여러 개의 fecthData instance들을 허용합니다.
액션이 dispatch될 때 마다 특정 함수를 실행합니다.

all : Saga가 여러개 묶어서 사용하고 싶으면 allEffect를 사용해서 아래와 같이 만들어 줄 수 있습니다.

import {all} from 'redux-saga/effects';

//all 함수를 통해 Saga들을 하나로 묶어줄 수 있습니다.
export default funtion* rootSaga(){
    yield all([
        example1()
        ,example2()
    ])
}

그 후 아래와 같이 sagaMiddleware.run()에 넣어주면 됩니다.

sagaMiddleware.run(rootSaga)

| Saga-Effect

Saga는 이러한 부수효과를 처리하는 이펙트들을 지원합니다.
앞의 설명에서는 puttakeEvery가 있습니다.
여기서 중요한 점은 모든 effect 들은 반드시 yield 와 함께 사용 해야 합니다.

take

take 는 특정 액션을 감시하는 용도로 사용합니다.
다음 코드는 REQUEST_ORDER 액션이 디스패치될 때까지 기다린 후 Api.requestOrder 를 호출하는 코드입니다.

function* watchOrdeRequest(){
    const action = yield take('REQUEST_ORDER');
    const result = yield call(Api.requestOrder,action.orderId);
    // ... process ... 
}

블럭된다는 성질을 이용해서 다음과 같이 매번 action에 대해 반응하는 saga를 만들 수 있습니다.

function* watchOrderRequest(){
    //무한루프
    while(true){
        //하지만 해당 라인에서 블럭된다.
        const action = yield take("REQUEST_ORDER");
        const result = yield call ("APi.requestOrder", action.orderId);
        //process ... 
    }
}

위와 같은 saga 를 만들 일이 많아서 공식적으로 이런 동작의 helpertakeEvery, takeLatest, takeLeading 등을 제공하고 있습니다.

put

put effect는 단순히 redux의 dispatch 함수와 동일합니다.
해당 effect는 블럭이 되지 않기에 조심해야합니다.

function* watchOrderRequest(){
    // 무한 루프
    while(true){
        const action = yield take("REQUEST_ORDER");
        const result = yield call(Api.requestOrder, action.orderId);

        //결과를 스토어에 디스패치(put)한다.
        yield put({ type : "RESPONSE_ORDER", result });
    }
}

fork

새로운 하위 saga 작업를 생성하는 effect입니다.

fork블럭되지 않기에 (non-block) 호출 시점호출자부모 task 가 되고 forksaga자식 task 가 됩니다.
부모 task가 취소되면, 자식 task도 취소됩니다.

명시적으로 특정 자식 task만 취소시킬 수 있습니다.

function* parentTask(){
    const task1 = yield fork(자식task1);
    const task2 = yield fork(자식task2);

    // do something ...

    //아직 동작중이면 취소시킨다.
    if(task2 && task.isRunning() ) {
        task2.cancel();
    }
}

call

callblock 되는 fork 라고 보면 됩니다.
인자함수saga task 를 받을 수 있습니다.

두 번째부터는 실행 될 함수나 saga 인자로 들어갑니다.

보통 Promise 등의 실행 (보통 Ajax Call)에 사용됩니다.
Promiseresolve 될 때까지 블럭됩니다.

//resolve될 때 까지 기다립니다.
const result = call("Api.requestOrder",action.orderId);

select

reduxstate 에서 특정 상태가져올 때 사용하는 블럭 effect 입니다.

redux-thunk 의 getState 와 비슷하지만, 인자로 selector 를 줄 수 있습니다.

const activeUserSelector = state =>{
    return state.user.activeUser;
};

const getUSerData = userId => ajax(`/user/data/${userId}`);

function* parentTask(){
    const activeUser = yield select(activeUserSelector);
    const activeUserData = yield call(getUserData, activeUser.userId);
}

'React' 카테고리의 다른 글

리덕스 reselect 사용하기  (0) 2021.02.22
react-redux의 shallowEqual 사용하기  (0) 2021.02.22
CORS와 Webpack DevSercer Proxy  (0) 2021.02.18
redux-thunk란?  (0) 2021.02.18
리덕스 미들웨어  (0) 2021.02.17