안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[리액트를 다루는 기술]의 책을 참고하여 포스팅한 개인 공부 내용입니다.
리덕스 미들웨어를 통한 비동기 작업 관리
리액트 웹 애플리케이션에서 API 서버를 연동할 때는 상태 관리를 잘 해줘야 합니다.
요청이 시작되었을 때는 로딩 중임을, 요청이 성공/실패했을 때는 로딩이 끝났음을 명시해주어야 합니다.
요청이 성공하면 서버에서 받아 온 응답에 대한 상태를 관리하고, 요청이 실패하면 서버에서 반환한 에러 상태를 관리합니다.
리덕스 사용 시 이 같은 비동기 작업을 관리해야 한다면, 미들웨어를 사용해 효율적으로 관리할 수 있습니다.
작업 환경 준비
CRA 사용해 리액트 프로젝트 생성
$ yarn create react-app learn-redux-middleware
리덕스 설치
$ yarn add redux react-redux redux-actions
카운터를 구현할 예정이므로, counter 리덕스 모듈을 작성해주겠습니다.
modules/counter.js
import { createAction, handleActions } from 'react-actions';
// 액션 타입 정의
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
// 액션 생성 함수
export const increase = createAction(INCREASE);
export const decrease = createAction(DECREASE);
// 초기 상태 정의
const initialState = 0; // 상태를 꼭 객체일 필요 X
// 리듀서 작성
// handleActions 첫번째 파라미터: 각 액션에 대한 업데이트 함수, 두번째 파라미터: 초기값
const counter = handleActions(
{
[INCREASE]: state => state + 1,
[DECREASE]: state => state - 1
},
initialState
);
// 만일 handleActions 사용하지 않는다면?
// function counter(state = initialState, action) {
// switch(action.type) {
// case INCREASE: state + 1;
// case DECREASE: state - 1;
// default: return state;
// }
// }
export default counter;
루트 리듀서 생성
modules/index.js
import counter from "./counter";
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
counter
});
export default rootReducer;
스토어 생성 후 리액트 프로젝트에 리덕스 적용
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { createStore } from 'redux';
import { Provider } from 'react';
import rootReducer from './modules';
const store = createStore(rootReducer);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
카운터 컴포넌트 생성
components/Counter.js
const Counter = ({ number, onIncrease, onDecrease }) => {
return(
<div>
<h1>{number}</h1>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
)
}
export default Counter;
카운터 컨테이너 컴포넌트 생성
containers/CounterContainer.js
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.js
import CounterContainer from './containers/CounterContainer';
function App() {
return (
<div>
<CounterContainer />
</div>
);
}
export default App;
잘 렌더링되는 것을 확인할 수 있습니다.
리덕스 미들웨어란?
- 액션을 디스패치했을 때 리듀서에서 이를 처리하기에 앞서 사전에 지정된 작업들을 실행
- 액션과 리듀서 사이의 중간자
리듀서가 액션을 처리하기 전에 미들웨어가 할 수 있는 작업은?
- 전달받은 액션을 단순히 콘솔에 기록
- 전달받은 액션 정보를 기반으로 액션을 취소
- 전달받은 액션 정보를 기반으로 다른 종류의 액션을 추가로 디스패치 등
미들웨어 만들기
실제 프로젝트에서는 직접 미들웨어를 만들기 보단, 이미 만들어져 있는 미들웨어를 사용한다고 합니다.
액션이 디스패치될 때마다 액션의 정보와 액션이 디스패치되기 전후의 상태를 콘솔에 보여주는 로깅 미들웨어를 작성해보겠습니다.
src/lib/loggerMiddleware.js
const loggerMiddleware = store => next => action => {
// 미들웨어 기본 구조
};
export default loggerMiddleware;
// 일반 function 키워드 사용 시
const loggerMiddleware = function LoggerMiddleware(store) {
return function(next) {
return function(action) {
// 미들웨어 기본 구조
};
};
};
미들웨어는 함수를 반환하는 함수를 반환하는 함수입니다.
파라미터 의미
- store
- 리덕스 스토어 인스턴스
- dispatch, getState, subscribe 내장 함수를 지님 - next
- store.dispatch와 비슷한 역할을 하는 함수 형태
- next(action) 형태로 호출
- 액션을 다음 미들웨어에게 넘겨줌
- 미들웨어가 없다면 리듀서에게 액션을 넘겨줌
- next를 호출하지 않으면 액션이 리듀서에 전달되지 않음 (액션 무시)
- store.dispatch 사용 시 첫 번째 미들웨어부터 다시 처리 - action
- 디스패치된 액션 (현재 처리하고 있는 액션 객체)
생성할 미들웨어는 다음 정보를 순차적으로 콘솔에 보여줍니다.
- 이전 상태
- 액션 정보
- 새로워진 상태
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
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './modules';
import { Provider } from 'react-redux';
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>
);
생성하였던 loggerMiddleware 미들웨어를 스토어에 적용시켜 주었습니다.
미들웨어는 스토어를 생성하는 과정에서 적용하므로, createStore에 같이 적용해줍니다.
액션 정보 및 업데이트 전후 상태가 잘 찍히는 것을 확인할 수 있습니다.
redux-logger 사용하기
오픈 소스 커뮤니티에 올라와 있는 redux-logger 미들웨어를 설치하여 사용해보겠습니다.
$ yarn add redux-logger
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './modules';
import { Provider } from 'react-redux';
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>
);
콘솔에 색상도 입혀지고, 액션 디스패치 시간도 나타나는 것을 확인할 수 있습니다.
'JavaScript > React' 카테고리의 다른 글
React) 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 ④ - Hooks를 사용하여 컨테이너 컴포넌트 만들기 (0) | 2022.12.16 |
---|---|
React) 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 ③ - 리덕스 더 편하게 사용하기 (0) | 2022.12.16 |
React) 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 ② - 리액트 애플리케이션에 리덕스 적용하기 (0) | 2022.12.15 |
React) 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 ① - 준비 과정 (0) | 2022.12.09 |
React) 리덕스 라이브러리 이해하기 - 리덕스의 세 가지 규칙 (2) | 2022.11.29 |