React, Next

[REACT] Redux, Redux-Thunk, Redux-Saga

재원쓰 2022. 8. 14. 01:33

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

 

GitHub - reduxjs/redux-thunk: Thunk middleware for Redux

Thunk middleware for Redux. Contribute to reduxjs/redux-thunk development by creating an account on GitHub.

github.com

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 등이 있다.