본문 바로가기
프로그래밍/react

[220504] 미들웨어) redux-thunk

by 한코코 2022. 5. 10.

디렉토리 구조

 


미들웨어란?

dispatch가 reducer를 실행시켜서 state를 반환하려고할때 그 중간을 가로막는 함수라고 생각하면 된다.

교수님이 그려주신 구조도

 

 

 

미들웨어의 종류   -  1.  thunk : 함수를 디스패치 해주는 미들웨어

핵심은 10줄짜리인 짧은 코드. 그만큼 사용하기 쉽고 잘못짜면 터짐.

짧은 코드인만큼 기능이 없으면 내가 만들어서 사용해야함.

action 객체가 아닌 함수를 dispatch할 수 있음.

action에 응답을 줄 수 없다.

 

 

미들웨어의 종류   -  2.  saga : 특정 작업이 이루어지는 액션에 대한 리스너

thunk보다 길고 규칙에 따라 사용해야해서 자유성이 적다.

하지만 기능이 thunk보다 많고 규칙을 지켜야해서 이슈가 적다.

액션을 모니터링하고 있다가 특정 액션이 발생했을 때, 미리 정해둔 로직에 따라 특정 작업이 이루어지는 액션에 대한 리스너이다.

 

 

 

 


thunk 세팅하기

redux를 사용하기위한 사전준비를 끝내놓는다.

store의 첫번째 인자로 reducer를 넣고, 두번째로 미들웨어를 사용하기 위한 enhancer를 넣는다.

미들웨어를 여러개 쓰기 위한 compose 안에 단일 미들웨어만 사용할 수 있는 applyMiddleware을 넣는다.

지금은 thunk를 사용할거니까 applyMiddleware의 인자값으로 thunk를 넣어준다.

 

const {createStore, compose, applyMiddleware} = require('redux')
const rootReducer = require('./reducers')
const redux = require('redux')

const enhancer = compose(applyMiddleware(thunk))
const store = createStore(rootReducer, enhancer)

 

만일 여러개의 미들웨어를 사용하고 싶다면 다음과 같이 사용하면 된다.

const middleware = [thunk,여러개의 미들웨어]
const enhancer = compose(applyMiddleware(...middleware))
const store = createStore(rootReducer, enhancer)

 

리액트 사이트에 나온 enhancer 코드 설명

composeWithDevtool는 개발할때만 사용해서 지금 모드가 개발모드인지 확인해서 실행하는 버젼을 관리할 수 있는 코드.

// const enhancer = compose(applyMiddleware(...middleware))
// 지금 이 코드를 실행하는게 production이니?
const enhancer = process.env.NODE_ENV === 'production'
? compose(applyMiddleware(...middleware)) // 맞으면 이거 사용
: composeWithDevtool(applyMiddleware(...middleware)) // 다르면 이거 사용

 

 

 

 


thunk 사용하기 - 기본형

다음과 같이 콜백함수가 여러개 엮여있는것처럼 보이는건 함수를 인자로 전달받는 고차함수다.

다른 말로는 함수의 실행결과를 함수로 반환하는 함수라고 한다.

next는 다음으로 넘어갈지말지 결정하는 메서드다.

next를 호출하면 미들웨어를 뛰어넘어 다음으로 넘어간다.

const thunk = (store) => (next) =>(action) => {
  if(typeof action === 'function'){
    action(dispatch)
  } else {
    return next(action)
  }
}

store.dispatch({type:'asdfasdf'})
// 결과 >> { type: 'asdfasdf' }

 

 

 

 

고차함수 이해하기

호출한 action이 함수형태일때 함수라는 문자와 action을 출력하는 코드와 고차함수 aa를 작성했다.

이때 action을 출력했을때 함수aa가 나온다.

const thunk = (store) => (next) => (action) => {
  if(typeof action === 'function'){
    console.log('함수', action)
  } else {
    return next(action)
  }

const aa = () => {
  const bb = (i) => { console.log(i) }
  return bb
}

store.dispatch(aa)
//결과 함수aa가 나옴

 

이때 dispatch(aa())를 사용한다고해보자.

dispatch 안에서 호출된 aa()는 bb를 반환하므로 결론적으로는 dispatch 안에 함수bb가 들어가게 된다.

미들웨어로 불려간 함수bb는 action 안에 들어가게 된다.

store.dispatch(aa()) = store.dispatch(bb)
action = 함수 bb

 

thunk 내용이 이렇게 바뀐다면 어떨까.

action은 함수bb와 같으므로 결국 action('abc')은 bb('abc')와 같게된다.

함수 bb가 인자값으로 받은 'abc'를 출력할것이다.

const thunk = (store) => (next) => (action) => {
  if(typeof action === 'function'){
    action('abc') // = bb('abc') = console.log('abc')
  }
  else{...}
}

 

thunk는 상태값을 바꾸기위해서 사용하는 미들웨어기때문에 action에 인자값으로 dispatch를 넣어준다.

store 안에는 state와 dispatch가 들어있으므로 store자리에 다음과 같이 코드를 작성하기도 한다.

const thunk = ({dispatch, getState}) => (next) => (action) => {
  if(typeof action === 'function'){
    action(dispatch) // = bb(dispatch) = dispatch({type:'ADD'}) => reducer에게 달려감
  }
  else{...}
}

const aa = () => {
  // const bb = (i) => {i({type:'ADD'})}
  // i 자리에 action인자값으로 들어간 dispatch를 넣으면 다음과 같이 된다.
  const bb = (dispatch) => {dispatch({type:'ADD'})}
  return bb
}

 

이 코드를 정리하면 아까 작성했던 기본형이 나온다.

const thunk = ({dispatch, getState}) => (next) =>(action) => {
  if(typeof action === 'function'){
    action(dispatch)
  } else {
    return next(action)
  }
}

 

조금 줄여적어보자면 다음과 같이 간단해진다.

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

댓글