본문 바로가기
Frontend/ReactJS(완)

React - 18장 : 리덕스 미들웨어를 통한 비동기 작업 관리

by 리키이 2023. 3. 1.

<리액트를 다루는 기술>

React - 1장 : React 이해

React - 2장 : JSX

React - 3장 : 컴포넌트

React - 4장 : 이벤트 핸들링

React - 5장 : ref. DOM에 이름 달기 

React - 6장 : 컴포넌트 반복
React - 7장 : 컴포넌트의 LifeCycle
React - 8장 : React Hooks 총정리

React - 9장 : 컴포넌트의 스타일링

React - 10장 : 빠르게 TODO앱 실습

React - 11장 : 컴포넌트 성능 최적화

React - 12장 :  immer를 사용하여 더 쉽게 불변성 유지하기

React - 13장 : 리액트 라우터로 SPA 개발하기

React - 14장 : 뉴스 뷰어 예제프로젝트

React - 15장 : ContextAPI

React - 16장 : 리덕스 라이브러리의 이해

React - 17장 : 리덕스 실습

React - 18장 : 리덕스 미들웨어를 통한 비동기 작업 관리


18-1. 작업 환경 준비

- 새 리액트 프로젝트를 생성

 

// 프로젝트 생성
yarn create react-app learn-redux-middleware

// 라이브러리 설치
yarn add redux react-redux redux-actions

 

- 바로 전에 리덕스파트에서 counter 리덕스 모듈 작성했던 것 처럼 코드 작성

 

// 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);

const initialState = 0; 

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

export default counter;

 

-  루트 리듀서를 생성

 

// modules/index.js

import { combineReducers } from 'redux';
import counter from './counter';

const rootReducer = combineReducers({
  counter
});

export default rootReducer;

 

- src 디렉터리의 index.js에서 스토어를 생성한 후, Provider로 리액트 프로젝트에 리덕스를 적용

 

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

const store = createStore(rootReducer);

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

 

- 카운터 컴포넌트와 카운터 컨테이너 컴포넌트를 만든다.  프레젠테이셔널 컴포넌트는 components 디렉터리에 저장하고, 컨테이너 컴포넌트는 containers 디렉터리에 저장

 

// components/Counter.js

import React from 'react';

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

export default Counter;

 

// containers/CounterContainer.js

import React from 'react';
import { connect } from 'react-redux';
import { increase, decrease } from '../modules/counter';
import Counter from '../components/Counter';

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

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

 

- App에서 CounterContainer를 렌더링

 

import React from 'react';
import CounterContainer from './containers/CounterContainer';

const App = () => {
  return (
    <div>
      <CounterContainer />
    </div>
  );
};

export default App;

- 그럼 이제 작업환경세팅은 다 끝났고 미들웨어부분으로 들어가보자

 

18-2. 미들웨어란?

- 리덕스 미들웨어는 액션을 디스패치했을 때 리듀서에서 이를 처리하기에 앞서 사전에 지정된 작업들을 실행한다. 미들웨어는 액션과 리듀서 사이의 중간자라고 볼 수 있다.

- 액션이 디스패치될 때마다 액션의 정보와 액션이 디스패치되기 전후의 상태를 콘솔에 보여 주는 로깅 미들웨어를 예시로 만들어 보겠다.

- src 디렉터리에 lib 디렉터리를 생성하고, 그 안에 loggerMiddleware.js 파일을 생성

 

// lib/loggerMiddleware.js

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

export default loggerMiddleware;


// 일반 function 키워드로 풀어서 쓴다면 다음과 같은 구조

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

 

- 미들웨어는 결국 함수를 반환하는 함수를 반환하는 함수

- 위의 코드를 보면 여기에 있는 함수에서 파라미터로 받아 오는 store는 리덕스 스토어 인스턴스를, 

   action은 디스패치된 액션을 가리킨다. 

- next 파라미터는 함수 형태이며, store.dispatch와 비슷한 역할을 합니다.

-  next(action)을 호출하면 그다음 처리해야 할 미들웨어에게 액션을 넘겨주고,

   만약 그다음 미들웨어가 없다면 리듀서에게 액션을 넘겨준다는 것이다.

 

 

store.dispatch vs. next

 

- 이번에 만들 미들웨어는 다음 정보를 순차적으로 콘솔에 보여 준다..

   - 이전 상태

   - 액션 정보

   - 새로워진 상태

 

//  loggerMiddleware.js

const loggerMiddleware = store => next => action => {
  console.group(action && action.type); // 액션 타입으로 log를 그룹화함
  console.log('이전 상태', store.getState());
  console.log('액션', action);
  next(action); // 다음 미들웨어 혹은 리듀서에게 전달
  console.log('다음 상태', store.getState()); // 업데이트된 상태
  console.groupEnd(); // 그룹 끝
};

export default loggerMiddleware;

 

- 미들웨어를 스토어(index.js)에 적용

 

//index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import rootReducer from './modules';
import loggerMiddleware from './lib/loggerMiddleware';

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

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
serviceWorker.unregister();

 

 

 

 

 

 

 

 

 

 

 

 

댓글