이번 글은 리액트를 다루는 기술 chapter 18을 기반으로 작성되었다.
미들웨어란 함수를 반환하는 함수를 반환하는 함수이다.
리덕스 미들웨어는 액션을 디스페치했을 때 리듀서에서 이를 처리하기에 앞서 사전에 지정된 작업들을 실행한다.
액션 -> 미들웨어 -> 리듀서 -> 스토어
const loggerMiddleware = (store) => (next) => (action) => {
//미들웨어의 기본 구조
}
// 이전상태, 액션정보, 새로워진 상태를 콘솔에 찍는 예제
const loggerMiddleware = (store) => (next) => (action) => {
console.log(action && action.type);
console.log('이전 상태', store.getState());
console.log('액션', action);
next(action);
console.log('다음상태', store.getState());
console.groupEnd();
}
export default loggerMiddleware;
미들웨어의 장점
1. 비동기 작업 관리
2. 액션 로깅 및 디버깅
3. 액션 변형
4. 코드의 재사용성과 모듈화
5. 확장성
미들웨어의 고려사항
1. 복잡성
2. 테스트의 어려움
3. 학습이 어려움
redux-logger를 사용하면 loggerMiddelware보다 훨씬 더 보기 쉬운 형태가 된다. (콘솔에 색상, 액션 디스페치의 시간이 나타남)
import { createLogger } from 'redux-logger';
const logger = createLogger()
const store = createStore(reducer, applyMiddleware(logger))
redux-thunk란 리덕스를 사용하는 프로젝트에서 비동기 작업을 처리할 때 가장 기본으로 사용하는 미들웨어이다.
Thunk란?
Thunk는 특정 작업을 나중에 할 수 있도록 미루기 위해 함수 형태로 감싼 것을 의미한다.
redux-thunk로 비동기 카운터 페이지 만들기
//redux-thunk 미들웨어 적용하기
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import './index.css';
import App from './App';
import rootReducer from './modules';
import { createLogger } from 'redux-logger';
import ReduxThunk from 'redux-thunk';
const logger = createLogger();
const store = createStore(rootReducer, composeWithDevTools());
const root = ReactDOM.createRoot(document.getElementById(logger, ReduxThunk));
root.render(
<Provider store={store}>
<App />
</Provider>
);
Thunk 생성 함수 만들기
redux-thunk 에서는 액션 생성 함수에서 일반 액션 객체를 반환하는 대신에 함수를 반환한다.
import { createAction,handleActions } from 'react-actions';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
export const increase = () => (INCREASE);
export const decrease = () => (DECREASE);
export const increaseAsync = () => dispatch => {
setTimeout(() => {
dispatch(increase());
}, 1000);
}
export const decreaseAsync = () => dispatch => {
setTimeout(() => {
dispatch(decrease());
}, 1000);
}
const initialState = 0;
const counter = handleActions(
{
[INCREASE]: state => state + 1,
[DECREASE]: state => state - 1
},
initialState
);
export default counter;
redux-saga 에서 사용되는 문법이다.
이 문법의 핵심 기능은 함수를 작성할 때 함수를 특정 구간에 멈춰 놓을 수도 있고, 원할 때 다시 돌아가게 할 수도 있다.
function* generatorFunction() {
console.log('하이');
yield 1;
console.log('파이');
yield 2;
console.log('브');
yield 3;
return 3;
}
제네레이터 함수에서는 function* 를 사용해야한다.
redux-saga 란 함수 형태의 액션을 디스패치하여 미들웨어에서 해당 함수에 스토어의 dispatch 와 getState를 파라미터로 넣어서 사용하는 방식이다.
redux-saga 는 redux-thunk 보다 조금 더 까다로운 상황에 유용하게 사용된다.
redux-saga로 비동기 카운터 페이지 만들기
import { createAction, handleActions } from 'redux-actions';
import { delay, put, takeEvery, takeLatest } from 'redux-saga/effects';
const INCREASE_ASYNC = 'counter/INCREASE_ASYNC';
const DECREASE_ASYNC = 'counter/DECREASE_ASYNC';
const increaseAsync = createAction(INCREASE_ASYNC, () => undefined);
const decreaseAsync = createAction(DECREASE_ASYNC, () => undefined);
function* increaseSaga() {
yield delay(1000);
yield put(increase());
}
function* decreaseSaga() {
yield delay(1000);
yield put(decrease());
}
function* counterSaga() {
yield takeEvery(INCREASE_ASYNC, increaseSaga);
yield takeLatest(DECREASE_ASYNC, decreaseSaga);
}
const initialState = 0; // 상태는 꼭 객체일 필요가 없다.
const counter = handleActions(
{
[INCREASE]: state => state + 1,
[DECREASE]: state => state - 1
},
initialState
)
export default counter;
redux-saga 와 redux-thunk 의 진입장벽이 매우 높은것 같다는 생각이 들었다..
언어 공부를 하면서 이렇게 안 와닿았던적이 있었나 싶을 정도로 이해가 쉽게 되지 않는 부분이였어서 앞으로도 쭉 리덕스 미들웨어에 대한 공부를 해야될것 같다는 생각이든다.
인터뤠스튕~