본문 바로가기

Computer Programming/[리액트를 다루는 기술]

[리액트를 다루는 기술] 18. 미들웨어비동기 작업 1 (react-actions | redux-logger | thunk 기본 사용법)

1. redux-actions 사용하기

1) createAction

import { createAction } from '@reduxjs/toolkit'

const increment = createAction('counter/increment')

let action = increment()
// { type: 'counter/increment' }

action = increment(3)
// returns { type: 'counter/increment', payload: 3 }

return 값을 참고하면 createAction을 통한 액션함수 생성이 훨씬 간결하다.

 

 

2) handleActions

const initialState = 0; //꼭 객체일 필요는 없음. ({num : 0}도 상관없음)

//reducers
const counter = handleActions(
  {
    [INCREASE]: (state) => state + 1,
    [DECREASE]: (state) => state - 1,
  },
  initialState,
);

export default counter;

리듀서 함수도 매우매우 간단해진다.

 

payload를 사용하고 싶은 경우에는

const counter = handleActions({
  [INCREASE]: (state, action) => state + action.payload,
  [DECREASE]: (state, action) => state - action.payload,
}, initialState);

이렇게 작성하면 편리하다.

 

 

기본 카운터 컨테이너의 구조이다. 

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

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

  const onClickAddHandler = () => {
    dispatch(increase());
  };

  const onClickMinusHandler = () => {
    dispatch(decrease());
  };

  return (
    <Counter
      number={number}
      onClickAddHandler={onClickAddHandler}
      onClickMinusHandler={onClickMinusHandler}
    />
  );
};

export default CounterContainer;

 

이제 미들웨어를 사용해 액션이 디스패치 될 때 마다 액션의 정보와 상태를 콘솔에 보여주는 로깅 미들웨어를 만들어보자

 

 

 

const loggerMiddleware = (store) => (next) => (action) => {
  //미들웨어 기본 구조
};

//store은 redux store 인스턴스, action은 디스패치 된 액션

export default loggerMiddleware;

미들웨어의 기본 구조이다. 여기서 next를 호출하면 파라미터의 함수에 액션을 넘겨준다. 만약, 그 다음의 미들웨어가 없다면 리듀서에게 액션을 넘겨주는 구조이다. 

 

 

 

const loggerMiddleware = (store) => (next) => (action) => {
  console.group(action && action.type); //
  console.log("이전 상태: ", store.getState());
  console.log("액션 : ", action);
  next(action); //다음 미들웨어에 전달 또는 없다면 리듀서에게 전달
  console.log("다음 상태: ", store.getState()); //업뎃된 상태
  console.groupEnd(); //그룹 끝
};

//store은 redux store 인스턴스, action은 디스패치 된 액션

export default loggerMiddleware;

 

그리고 적용은 index.js의 createStore의 두번째 인자의 applyMiddleware의 매개변수로 전달하면 된다.

 

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

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

 

 

 

그러면 콘솔에서 action 의 type에 따라 그룹화되어 콘솔에 state의 변화가 출력되는 것을 볼 수 있다. 그리고 미들웨어를 통해 action의 타입을 출력한다. (신기방기)

 

 

방금 만든 logger를 오픈 소스인 redux-logger을 이용해 편리하게 사용할 수도 있다.

 

react-logger 사용시 index.js 코드

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

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

 

콘솔 출력

미들웨어를 이해하는 것이 물론 중요하지만, 미들웨어를 직접 작성하기 보다는 나중에 현업에서는 이미 있는 미들웨어를 이해해서 사용하거나 업데이트하고, 이미 오픈소스인 미들웨어를 사용하는 경우가 많다고 한다.

 

 

그 중 가장 많이 다뤄지는 것이 바로 이 두개다.

 

1) redux-thunk : 객체가 아닌 함수 형태의 액션을 디스패치 할 수 있게 해줌

2) redux-saga : 특정 액션이 디스패치 되었을 때 정해진 로직에 따라 다른 액션을 디스패치 키시는 규칙을 정할 수 있음

 

 

 

thunk 라이브러리를 사용해보자

 

우선 index.js에 스토어를 만들 때 redux-thunk를 아래와 같이 적용한다. (import thunk, middleware에 apply)

import thunk from "redux-thunk";

const logger = createLogger();
const store = createStore(rootReducer, applyMiddleware(logger, thunk));

 

 

 

Thunk 의 액션 생성 함수는 일반 객체를 반환하는 것이 아니라 함수를 반환한다.

export const increaseAsync = () => (dispatch) => {
  setTimeout(() => {
    dispatch(increase());
  }, 1000);
}; // thunk 를 사용하면 액션 생성 함수에서 일반 액션 객체를 반환하는 대신에 함수를 반환한다.

export const decreaseAsync = () => (dispatch) => {
    setTimeout(() => {
        dispatch(decrease());
    }, 1000)
}

 

import React from "react";
import { connect } from "react-redux";
import Counter from "../components/Counter";
import { increaseAsync, decreaseAsync } from "../redux/modules/counter";

const CounterContainer = ({ number, increaseAsync, decreaseAsync }) => {
  return (
    <>
      <Counter
        number={number}
        onIncrease={increaseAsync}
        onDecrease={decreaseAsync}
      />
    </>
  );
};

export default connect(
  (state) => ({
    number: state.counter,
  }),
  {
    increaseAsync,
    decreaseAsync,
  },
)(CounterContainer);

 

연결해주면 1초뒤에 숫자가 증가하거나 감소한다.