끄적끄적 코딩
article thumbnail
Published 2023. 5. 30. 04:40
[React] 리덕스 미들웨어 React

리덕스 미들웨어

액션을 패치 했을 때 리듀서에서 이를 처리하기에 앞서 사전에 지정된 작업들을 실행합니다.
액션과 리듀서 사이의 중간자라고 볼 수 있습니다.

리듀서가 액션을 처리하기 전에 미들웨어가 할 수 있는 작업은 여러가지가 있습니다.
- 전달받은 액션을 단순히 콘솔에 기록
- 전달받은 액션 정보를 기반으로 액션을 취소
- 다른 종류의 액션을 추가로 디스패치

*실제 미들웨어를 직접 만들어 사용할 일은 많지 않습니다. => 다른 개발자가 만들어 놓은 미들웨어를 사용하기 때문

const loggerMiddleware = (store) => (next) => (action) => {};

export default loggerMiddleware;

미들웨어 만들어 보기

// lib/loggerMiddleware.js
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();
};

export default loggerMiddleware;

 

// index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import rootReducer from "./modules";
import loggerMiddleware from "./lib/loggerMiddleware";

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

 

redux-logger

logger 미들웨어

npm add redux-logger
// index .js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import rootReducer from "./modules";
import { createLogger } from "redux-logger";

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

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

 

비동기 작업을 처리하는 미들웨어

- redux-thunk
- redux-saga


redux-thunk

리덕스를 사용하는 프로젝트에서 비동기 작업을 처리할 때 가장 기본적으로 사용하는 미들웨어
액션 객체가 아닌 함수를 디스패치 가능

함수 형태의 액션을 디스패치하여 미들웨어에서 해당 함수에 스토어의 dispatch와 getState를 파라미터로 넣어서 사용하는 원리
구현한 thunk 함수 내부에서 원하는 API 요청도 하고, 다른 액션을 디스패치하거나 현재 상태를 조회하기도 함

Thunk? 

특정 작업을 나중에 할 수 있도록 미루기 위해 함수 형태로 감싼 것을 의미

npm add redux-thunk

 

// modules/counter.js
import { createAction, handleActions } from "redux-actions";

const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";

export const increase = createAction(INCREASE);
export const decrease = createAction(DECREASE);

export const increaseAsync = () => (dispatch) => {
  setTimeout(() => {
    dispatch(increase());
  }, 1000);
};

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

const initialState = 0;

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

export default counter;
// container/CounterContainer.js
import { connect } from "react-redux";
import { increaseAsync, decreaseAsync } from "../modules/conter";
import Counter from "./Counter";

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

export default connect(
  (state) => ({
    number: state.counter,
  }),
  {
    increaseAsync,
    decreaseAsync,
  }
)(CounterContainer);
// index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import rootReducer from "./modules";
import { createLogger } from "redux-logger";
import ReduxThunk from "redux-thunk";

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

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


redux-thunk를 사용하지 않고 함수를 디스패치에 사용했을 경우

 

redux-saga

대부분의 경우 redux-thunk로도 충분히 기능을 구현할 수 있습니다.
redux-saga는 아래와 같은 상황에서 유용합니다.

- 기존 요청을 취소 처리해야 할 때(불필요한 중복 요청 방지)
- 특정 액션이 발생했을 때 다른 액션을 발생시키거나, API 요청 등 리덕스와 관계없는 코드를 실행할 때
- 웹소켓을 사용할 때
- API 요청 실패 시 재요청해야 할 때

제너레이터 함수
redux-saga는 ES6의 제너레이터 함수 문법을 사용합니다.
일반적인 상황에서 많이 사용되지 않기 때문에 진입 장벽이 있을 수 있습니다.

function weirdFunction() {
    return 1;
    return 2;
    return 3;
}

위와 같은 코드는 맨 위에 있는 1만 반환됩니다.

제너레이트 함수를 사용하면 함수에서 값을 순차적으로 반환할 수 있습니다.

function* generatorFunction() {
    console.log("Hi1");
    yield 1;
    console.log("Hi2");
    yield 2;
    console.log("Hi3");
    yield 3;
    return 4;
}

const generator = generatorFunction();

generator.next();
// Hi1
// {value: 1, done: false}
generator.next();
// Hi2
// {value: 2, done: false}
generator.next();
// Hi3
// {value: 3, done: false}
generator.next();
// {value: 4, done: true}
generator.next();
// {value: undefined, done: true}

 

npm add redux-saga

 

리덕스 개발자 도구

npm add redux-devtools-extension

 

정리

비동기 작업을 처리할 때 redux-thunk는 일반 함수로 이루어져 있기 때문에 간단명료하다는 장점이 있습니다.
redux-saga는 진입 장벽이 조금 있을 수 있으나 복잡한 상황에서 더욱 효율적으로 작업을 관리할 수 있다는 장점이 있습니다.

그 외에도 redux-promise-middleware, redux-pender, redux-observable 등도 있습니다.

미들웨어를 사용하지 않고 컴포넌트단에서 API를 요청하는 방법도 가능합니다.
비동기 작업을 처리할 때 리덕스 미들웨어를 사용하는 이유는 좀 더 편하게 처리하기 위해서 입니다.
그러므오 오히려 불편하게 느낀다면 사용하지 않는 편이 좋을 수도 있습니다.

'React' 카테고리의 다른 글

[React] 컴포넌트  (0) 2023.06.20
[React] 리액트란  (0) 2023.06.20
[React] 리액트 라우터  (1) 2023.05.02
[React] ToDo List 만들기  (0) 2023.04.25
[React] JSX  (0) 2023.04.18

검색 태그