Front-End

vsoghlv@naver.com

React.js 리덕스 미들웨어 만들어보기

먼저 리덕스 연습을 위해 프로젝트를 만들고 틀을 만들었다.

//App.js
import CounterContainers from "./containers/CounterContainers";

function App() {
  return <CounterContainers></CounterContainers>;
}

export default App;

//index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import { createStore } from "redux";
import rootReducer from "./modules";

const store = createStore(rootReducer);

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById("root")
);

//modules/counter.js
//액션타입
const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";

//액션 생성 함수
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });

//초기 상태
const initialState = 0;

//리듀서
export default function counter(state = initialState, action) {
  switch (action.type) {
    case INCREASE:
      return state + 1;
    case DECREASE:
      return state - 1;
    default:
      return state;
  }
}

//modules/index.js
import { combineReducers } from "redux";
import counter from "./counter";

const rootReducer = combineReducers({ counter });

export default rootReducer;

//containers/CounterContainers.js
import React from "react";
import Counter from "../components/Counter";
import { useSelector, useDispatch } from "react-redux";
import { increase, decrease } from "../modules/counter";

function CounterContainer() {
  const number = useSelector((state) => state.counter);
  const dispatch = useDispatch();

  const onIncrease = () => {
    dispatch(increase());
  };
  const onDecrease = () => {
    dispatch(decrease());
  };

  return (
    <Counter number={number} onIncrease={onIncrease} onDecrease={onDecrease} />
  );
}

export default CounterContainer;

//components/Counter.js
import React from "react";

function Counter({ number, onIncrease, onDecrease }) {
  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

리덕스 작동 원리를 이해하기 위해 직접 작성해보자. 리덕스 미들웨어는 하나의 함수인데 리덕스 미들웨어를 작성할 때는 아래와 같은 템플릿을 사용한다.

const middleware = store =>next=>action=>{
  //하고자 하는 작업
}

function middleware(store){
    return function(next){
      return function (action){
        //하고자 하는 작업
      }
    }
  }  

미들웨어는 스토어를 파라미터로 받아와 함수를 반환하는데 그 함수가 반환하는 건 또 다른 함수이다.

store 는 말 그대로 리덕스 스토어의 스토이고 , 두번째 next 함수는 액션을 다음 미들웨어에 전달하는 함수인데 만약 다음 미들웨어가 없다면 리듀서에 액션을 전달해준다. 여기서 next 를 호출하지 않는다면 액션이 무시처리되 리듀서에게 전달되지 않는다. 세번째 action 은 현재 처리 중인 액션 객체이다.

솔직히 잘 이해가 되지 않는다. 일단 만들어보면서 다시 이해해 봐야겠다.

//middlewares/myLogger.js
const myLogger = (store) => (next) => (action) => {
  //액션이 디스패치 될 때 콘솔 출력
  console.log(action);
  //액션이 디스패치 되기전 상황
  console.log("\tPrev", store.getState());
  //액션을 다음 미들웨어로 전달, 없으면 리듀서에게 전달
  const result = next(action);
  //액션이 리듀서에서 처리 된 후 콘솔 출력,'\t' 는 콘솔창에서 쉼표를 뜻함
  console.log("\tNext", store.getState());
  //컨테이너에서 디스패치 됐을 때 반환하는 결과물
  return result;
};

export default myLogger;

미들웨어를 적용시키기 위해서는 index.js 에서 applyMiddleware 를 불러오고 createStore() 의 두번째 파라미터로 넣어주면 된다.

이때 applyMiddleware 에 위에서 작성한 myLogger 를 파라미터로 넣어주면 된다.

//index.js
import { createStore, applyMiddleware } from "redux";

const store = createStore(rootReducer, applyMiddleware(myLogger));

// {type: "counter/INCREASE"}
//  	Prev {counter: 0}
//  	Next {counter: 1}
// {type: "counter/DECREASE"}
//  	Prev {counter: 1}
//	    Next {counter: 0}

이와 같이 미들웨어 안에서는 하고 싶은 작업들을 모두 할 수있다.

redux-logger 와 DevTools

미들웨어 기본을 공부하기 위해 만들었던 myLogger 을 redux-logger 로 대체해 보자. redux-logger 와 devTools 를 사용해 보기 위해 먼저 라이브러리를 설치해주고 아래와 같이 사용하면 된다.

yarn add redux-logger redux-devtools-extension
///index.js
import logger from "redux-logger";
import { composeWithDevTools } from "redux-devtools-extension";

const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(logger))
);