[Redux] 미들웨어(Middleware)
🙃 Redux 너란 녀석...
Redux의 장점이자 단점이... 깊은 역사와 엄청난 생태계인 것 같다.
그만큼 많은 부가기능들과 트랜디한 업데이트들이 지원되고 있으며, 대체되는 기술들이 등장하지만 바로 넘어가기엔 부담이 있다.
분명, 소규모 프로젝트에까지 도입하기엔 다소 무게감이 있는 친구지만, Context API에 비해 완성도가 있다고 느껴진다.
오늘은 그 이유 중 하나인, 위에서 말한 '부가기능'에 해당하는 미들웨어의 개념을 간단히 공부해보았다.
💜 미들웨어(Middleware) 란?
미들웨어란 말이 낯설지는 않다. Node.js 서버 프레임워크인 Express.js 에서도 미들웨어라는 개념이 있었다.
Request-Response 사이에 작동하는 함수들을 일컫는 말이었다. 즉, 미들웨어는 특정한 시점 사이에서 작동하는 기능이라고 생각된다.
- 개념과 역할
미들웨어는, 액션이 디스패치되어 리듀서에서 처리하기 전에 사전에 지정된 작업들을 설정하는 역할을 담당한다.
즉, 미들웨어는 "Action 과 Reducer 사이의 중간 역할자" 라고 할 수 있겠다. 중간작업을 통해, 우리는 좀 더 효율적으로 상태를 요청하거나 관리할 수 있다.
미들웨어가 할 수 있는 작업들은 대표적으로 아래와 같이 있다.
- 전달받은 Action을 로깅할 수 있다. 즉, Console 에 기록하거나 서버에 로깅할 수도 있다.
- 전달받은 Action을 취소하거나, 다른 종류의 Action을 추가적으로 디스패치 할 수 있다.
- Action의 정보를 미리 확인하고 가공해서 리듀서로 전달시켜 줄 수도 있다. (비동기 처리에 유용할 듯)
💜 미들웨어(Middleware) 만들기
실제 프로젝트 시에는, 미들웨어를 직접 커스텀하는 경우가 많지는 않다. 대부분, 기성 미들웨어를 사용하면 되기 때문이다.
하지만, 미들웨어의 작동방식을 이해하거나, 상황에 따라 필요한 미들웨어를 만들거나 커스터마이징 하기 위해 이 과정을 권장한다.
- 미들웨어 템플릿
const middleware = store => next => action => {
// 하고 싶은 작업...
}
// function style
function middleware(store) {
return function (next) {
return function (action) {
// 하고 싶은 작업...
};
};
};
우선 미들웨어는, 위와 같은 템플릿으로 제작한다. 즉, 미들웨어는 일종의 함수이면서, 두 개의 내부함수를 반환하는 녀석이다.
여기에 사용된 파라미터들을 확인해보았다.
- store: Redux의 스토어이다. 앱의 상태와 우리가 흔히 사용하는 dispatch, getState, subscribe 등 내장함수를 담고 있다.
- next: 액션을 다음 미들웨어에게 전달하는 함수다. next(action)의 형태로, 다음 미들웨어로 액션을 전달하는 함수이다.
- action: 현재 처리하는 액션 객체이다.
- 로거(logger) 미들웨어 만들기
const loggerMiddleware = store => next => action => {
console.log('현재 상태', store.getState());
console.log('액션', action);
const result = next(action);
console.log('다음 상태', store.getState());
console.log('\n'); // 기록 구분을 위한 비어있는 줄
return result;
}
export default loggerMiddleware;
- 먼저, 현재 상태를 getState() 로 가져오고, 현재 액션까지 순서대로 콘솔에 기록한다.
- next(action) 형태로, 다음 미들웨어로 전달한다.(미들웨어가 없다면 리듀서로) 이렇게 처리된 결과물이 result 변수에 담긴다.
- 액션 처리 후의 상태를 getState() 로 역시 가져와서 콘솔에 기록한다.
- 마지막으로, result(=next(action)) 를 반환하면 된다. 이 값은, store.dispatch(action) 를 진행한 결과물이 된다.
- 미들웨어 적용하기
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducer';
import loggerMiddleware from './loggerMiddleware';
const store = createStore(modules, applyMiddleware(loggerMiddleware))
export default store;
미들웨어는 store에 설정해주면 된다. 'redux' 라이브러리의 applyMiddleware() 메서드를 통해 설정할 수 있다.
💜 Redux-logger
앞서 언급했듯이, Redux 오픈소스 커뮤니티에는 많은 미들웨어가 존재하고, 이는 로거 역시 마찬가지이다.
redux-logger 가 바로 그 미들웨어이며, Redux DevTool을 사용하지 못하는 환경에서 매우 유용한 미들웨어이다.
- 설치
npm i redux-logger
- 미들웨어 적용하기
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducer';
import { createLogger } from 'redux-logger';
/* 로그 미들웨어를 생성 할 때 설정을 커스터마이징 할 수 있습니다.
https://github.com/evgenyrodionov/redux-logger#options
*/
const logger = createLogger();
const store = createStore(modules, applyMiddleware(logger))
export default store;
applyMiddleware() 메서드는 동일하게 사용하고, createLogger() 를 'redux-logger' 에서 가져온다는 차이만 있다.
🧐 끝나지 않은 미들웨어(Middleware)
상대적으로 짧은 포스팅으로 끝났지만, 결코 미들웨어 공부가 끝난 것은 아니다. (오히려, 이제 시작이랄까? 🤯🤯)
미들웨어가 가장 선호되는 경우는, 바로 '비동기 처리' 에 있다.
Redux 와 함께 널리 사용되는 스택인, redux-thunk, redux-saga 역시 여기에 해당된다.
- Redux-thunk: 함수 형태의 액션 생성자를 반환. 특정 액션을 일정시간 뒤에 실행하거나, 현재 상태에 따라 취소할 수 있음.
- Redux-saga: 액션에 대한 리스너. 액션 객체를 반환한다.
다음엔, 이 미들웨어의 원리와 활용법을 공부해서, 다음 프로젝트의 데이터 패칭시에 적용해보아야 겠다.