[REACT] Redux, Redux-Thunk, Redux-Saga
Redux 세팅 방법
store > configureStore.js
import { createWrapper } from "next-redux-wrapper"
import { applyMiddleware, compose, createStore } from "redux"
import { composeWithDevTools } from "redux-devtools-extension"
import reducer from "../reducers"
const configureStore = () => {
const middlewares = []
const enhancer =
process.env.NODE_ENV === "production"
? compose(applyMiddleware(...middlewares))
: composeWithDevTools(applyMiddleware(...middlewares)) // 개발자모드에서 DevTools 사용 세팅한다.
const store = createStore(reducer, enhancer)
return store
}
const wrapper = createWrapper(configureStore, {
debug: process.env.NODE_ENV === "development",
})
export default wrapper
middlewares
리덕스에 없는 기능을 추가해서 성능을 확장시켜주는 역할을 한다. 리덕스 세팅에 middlewares 안에 Thunk나 Saga를 추가적용 할 수 있다.
Redux-Thunk / Redux-Saga
둘 다 비동기 action을 dispatch할 수 있도록 도와주는 역할을 한다.
아래 코드를 보면 setTimeout 함수 안에 dispatch를 한 것을 볼 수 있다.
const INCREMENT_COUNTER = 'INCREMENT_COUNTER'
function increment() {
return {
type: INCREMENT_COUNTER
}
}
function incrementAsync() {
return dispatch => {
setTimeout(() => {
// Yay! Can invoke sync or async actions with `dispatch`
dispatch(increment())
}, 1000)
}
}
비동기 action을 dispatch 하게되면 장점은 무엇일까?
하나의 비동기 액션 안에 여러개의 동기 액션을 할 수 있다
예를 들면 axios 요청을 보낼 때 LoadPostRequest 액션을 dispatch하고, 성공했을 땐 LoadPostSuccess를, 실패했을 땐 LoadPostFailer dispatch하는 식으로 여러번의 액션을 보낼 수가 있다.
Redux-Thunk 사용방법
우선 패키지를 설치한다.
npm i redux-thunk
import 해준 모듈을 middlewares 배열에 넣어주면 된다.
...
import thunkMiddleware from 'redux-thunk';
...
const configureStore = () => {
const middlewares = [thunkMiddleware]
...
}
...
export default wrapper
Redux-Thunk가 제공하는 기능 외에 내맘대로 로직을 커스텀 하고싶다면?
https://github.com/reduxjs/redux-thunk/blob/master/src/index.ts
Redux-Thunk 깃허브에서 코드를 타고 들어간뒤
위 코드가 Redux-Thunk의 핵심이다. 아래 코드를 통해 자세히 살펴보자.
({ dispatch, getState }) => next => action => {
// action이 원래는 객체인데 redux-thunk에서는 action을 function으로 둘 수 있다. action이 function이면 지연함수이기 떄문에 그 액션을 나중에 실행시켜줄 수 있다.
// 이 action이 실제로 함수라면 호출하고 결과를 반환한다.
if (typeof action === 'function') {
return action(dispatch, getState)
}
// 그렇지 않으면 평소와 같이 미들웨어 체인 아래로 작업을 전달한다.
return next(action)
}
하지만 위 코드를 수정하면서 내 마음대로 dispatch가 일어나기 전에 로직을 추가/수정 할 수 있다.
const loggerMiddleware = ({ dispatch, getState }) => next => action => {
console.log(action)
return next(action)
}
const configureStore = () => {
const middlewares = [thunkMiddleware, loggerMiddleware]
...
}
고차함수로 이루어져 있는 저 loggerMiddleware 라는 함수에서 next, action과 같은 매개변수를 가져오기 때문에, 이것을 활용해 dispatch가 이루어지기 전에 먼저 실행할 로직을 넣어줄 수 있다. 위 코드의 경우 dispatch가 이루어지기 전에 action을 콘솔로 찍어준다.
실제로 로그인, 로그아웃 action을 요청한 뒤 개발자도구를 열어보니 콘솔에 찍힌 것을 확인할 수 있다.
Redux-Saga
Redux-Thunk보다 더 많은 기능을 제공해준다. 예를들어 클릭을 짧은시간에 여러번 클릭했을 땐 어떻게 처리할 것인지? 이런 부분을 Thunk에서는 직접 구현해야 하지만 Saga에서는 takeLatest, trottle 등 한번에 해결해주는 여러가지 메소드가 존재한다.
Redux-Saga 사용방법
npm i redux-saga
리덕스 사가의 경우에는 여기저기 세팅할 부분들이 좀 있다.
1. 우선 import를 해준 뒤 sagaMiddleware라는 변수에 createSagaMiddleware를 호출하여 넣어준다.
2. middlewares 배열에 sagaMiddleware를 넣어준다.
3. store 변수에는 sagaTask라는 메서드가 있는데, 아래 코드에 '핵심' 이라고 쓰여있는 부분의 세팅을 하자.
import createSagaMiddleware from "redux-saga"
import rootSaga from "../sagas"
const configureStore = () => {
const sagaMiddleware = createSagaMiddleware()
const middlewares = [sagaMiddleware, loggerMiddleware]
...
const store = createStore(reducer, enhancer)
store.sagaTask = sagaMiddleware.run(rootSaga) 🔥핵심
return store
}
...
Generate 함수
설명 넣기
Saga의 다양한 Effect
- all : fork나 call로 실행하는 것들을 동시에 전부 실행할 수 있게 해준다.
- take : 지정한 action이 실행될 때까지 기다리겠다. 지정된 action이 실행되면 지정해둔 generate 함수를 호출한다.
- fork : 함수를 실행하는 것. 비동기 함수 호출. API가 반환되는 것을 받아오기 전에 다음 코드가 실행되어버린다.
- call : 함수를 실행하는 것. 동기함수 호출. API가 반환해준 것을 받아올 수 있다. async await 같은 느낌이라고 보면 된다.
- put : dispatch라고 보면 된다. 액션을 실행시키는 놈인데, Rest API를 통해 반환받은 값을 함께 전달하며 실행시킬 수 있다.
- takeEvery : take로 만든 함수는 한번 실행되면 사라져버리는데, 이건 그 함수를 무한하게 사용할 수 있다. 마치 while(true)로 감싸는 느낌이다.
- takeLatest🔥 : 클릭을 실수로 2번 눌렀을 때 앞에 누른건 무시되고 마지막 클릭만 실행시킨다.
주로 이거 쓴다. - takeLeading : takeLatest과 비슷하지만 이건 맨 첫번째 클릭만 실행되고 나머지를 무시한다.
- throttle🔥 : 시간을 설정해두고 그 시간동안에는 클릭을 여러개 해도 하나만 보내지도록 한다.
이것도 중요 - debounce : 검색창에 타이핑할 때 자동완성 데이터를 불러오는 부분에서 주로 사용한다. 어떤 단어가 완성될 때 요청보내는 식으로 정할 수 있다. throttle과 기능적으로 비슷한데 미묘한 차이가 있다고 하니 사용할 때 찾아보도록 하자!
- delay : 정해준 시간이 지난 뒤에 다음 코드를 실행시키게 한다.
이외에도 takeMaybe 등이 있다.