티스토리 뷰

1. Redux Middleware

2. redux-thunk

3. redux-saga


1. Redux Middleware

[그림 1] Middleware가 포함된 redux 흐름도

middleware는 소프트웨어 각 분야에서 세부적으로 다르게 뜻한다. 위키백과에서는 OS와 소프트웨어 중간에서 조정과 중개 역할을 하는 중간 소프트웨어라고 지칭한다. 소프트웨어 분야에서는 같은 이름을 가지고 있지만 다른 것을 의미하는 것들이 많아서 유의해야 한다.

 

Redux의 동작 순서는 action이 dispatch된 후 reducer를 호출하여 기존에 있던 state를 dispatch한 action으로 바꾼다. 이때 reducer가 호출되어 state를 바꾸기 전에 동작하는 것이 middleware이다.

 

보통 Redux에서 middleware를 사용하는 주된 이유는 비동기 작업을 처리하기 위함이다.

 

Redux Thunk, Redux Saga, Redux Promise 등은 Redux에서 동작하는 middleware이다. Redux의 핵심 기능 중 하나가 middleware를 사용할 수 있다는 점이다. Context API나 Mobx의 경우에는 middleware를 지원하지 않는다.

 

 

2. redux-thunk

redux-thunk는 Redux 창시자인 댄 아브라모프가 만든 가장 많이 사용되는 middleware 라이브러리이다.

const { createStore, compose, applyMiddleware } = require('redux');
const rootReducer = require('./reducers');
const { ADD_REQUEST, ADD_SUCCESS, ADD_FAILURE } = require('./reducers/comment.js');
const { composeWithDevTools } = require('redux-devtools-extension');

const thunk = ({dispatch, getState}) => (next) => (action) => {
    typeof action === "function" ? action(dispatch) : next(action);
};

const middleware = [thunk];
const enhancer = process.env.NODE_ENV !== "production" 
? compose(applyMiddleware(...middleware)) 
: composeWithDevTools(applyMiddleware(...middleware));

const store = createStore(rootReducer, enhancer);
store.dispatch({type:'hello'});

const a = () => {
    const b = dispatch => {
        dispatch({type:ADD_SUCCESS});
    };
    console.log('hello');
    return b;
};

const aa = () => async (dispatch) => {
    dispatch({type:'login login'});
    try {
        console.log('hello2');
        const result = await axios.get(url, {
            credential:true
        });
        dispatch({type:'success', payload: true});
    } catch (e) {
        dispatch({type:'failure', payload: false});
    }
};

store.dispatch(a()); // hello
store.dispatch(aa()); // hello2

위의 코드는 redux-thunk 라이브러리를 설치하지 않고 직접 구현한 것이다.

 

thunk는 고차 함수(Higher-Order Function)로, action의 data type이 함수일 때와 객체일 때에 대해 처리할 부분을 삼항 연산자로 나타내었다. action이 함수일 때에는 함수를 실행하고, 객체일 때는 다음 middleware로 넘기거나 reducer로 action 값을 전달한다.

 

store.dispatch({type: 'hello'}); 의 경우 action이 객체이므로 middleware에서 아무런 일도 일어나지 않고 reducer에 action 값이 전달된다. store.dispatch(a()); 의 경우 action이 함수이므로 middleware에서 함수를 실행하여 콘솔창에 'hello'라는 값이 찍히게 된다.

 

enhancer는 배포 단계인지 혹은 개발 단계인지에 따라 compose()를 쓸지 composeWithDevTools()를 쓸지 결정된다.

 

 

3. redux-saga

redux-saga는 2번째로 많이 사용되는 middleware 라이브러리로, 특정 action이 dispatch되었을 때 정해진 로직에 따라 다른 action을 dispatch시키는 규칙을 작성하여 비동기 작업을 처리할 수 있게 해준다.

 

먼저, 아래 명령어로 패키지를 설치해준다.

$ npm i redux-saga

 

redux-saga 패키지가 설치되었다면, 아래와 같이 코드를 작성한다.

const { createStore, compose, applyMiddleware } = require('redux');
const rootReducer = require('./reducers');
const reduxSaga = require('redux-saga');
const { takeEvery, takeLatest, call, put } = require('redux-saga/effects');

function loginAPI (id, pw) {
    return axios.post('http://localhost:3000/');
}

function* change(action) {
    const {payload: {id, pw}} = action;
    console.log(id, pw); // leejy 1234
    try {
        const result = yield call(loginAPI, id, pw);
        yield put({type:'success'});
    } catch (e) {
        yield put({type:'failure'});
    }
}

function* rootSaga() {
    yield takeEvery('jenny', change);
}

const sagaMiddleware = reduxSaga.default();
const middleware = [sagaMiddleware];
const enhancer = process.env.NODE_ENV !== 'production' 
? compose(applyMiddleware(...middleware)) 
: composeWithDevTools(applyMiddleware(...middleware));

const store = createStore(rootReducer, enhancer);
sagaMiddleware.run(rootSaga);

store.dispatch({type:'jenny', payload:{id:'leejy', pw:1234}});

redux-saga를 사용할 때에는 function* (generator function)을 쓴다. generator function은 사용자의 요구에 따라 다른 시간 간격으로 여러 값을 반환할 수 있으며, 내부 상태를 관리할 수 있다. 단 한 번의 실행으로 함수의 끝까지 실행이 완료되는 일반 함수와는 달리, generator 함수는 사용자의 요구에 따라 (yield와 next를 통해) 일시적으로 정지될 수도 있고, 다시 시작될 수도 있다. 또한, generator 함수의 return 값으로 generator가 반환된다.

 

redux-saga 주요 함수

  • delay : 설정된 시간 이후에 resolve하는 Promise 객체를 리턴 (ex. delay(1000))
  • put : 특정 action을 dispatch하도록 함 (ex. put({type: 'INCREASE'}))
  • takeEvery : 들어오는 모든 action에 대해 특정 작업을 처리해 줌 (ex. takeEvery(INCREASE, increaseSaga))
  • takeLatest : 기존에 진행 중이던 작업이 있다면 취소 처리하고 가장 마지막으로 실행된 작업만 수행함 (ex. takeLatest(DECREASE, decreaseSaga))
  • call : 첫 번째 파라미터는 함수, 나머지 파라미터는 해당 함수에 넣을 인수 (ex. call(delay, 1000)) call과 put의 다른 점은 put은 store에 인자로 들어온 action을 dispatch하고, call의 경우에는 주어진 함수를 실행하는 것
  • all : all 함수를 사용해서 generator 함수를 배열의 형태로 인자로 넣어주면, generator 함수들이 병행적으로 동시에 실행되고, 전부 resolve될 때까지 기다림 (ex. yield all([testSaga1(), testSaga2()]) → testSaga1()과 testSaga2()가 동시에 실행되고, 모두 resolve될 때까지 기다림)

'React' 카테고리의 다른 글

[React] MetaMask 연동 (feat. web3)  (0) 2022.06.29
[React] CRA 커스터마이징  (0) 2022.06.29
[React] React Router  (0) 2022.05.12
[React] Redux 사용 방법  (0) 2022.05.10
[React] 추가 Hooks (useReducer, useCallback, useMemo)  (0) 2022.05.02
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함