리덕스 미들웨어
dispatch(action) --- > middleware --- > reducer(state, action) --- > store 저장
액션이 디스패치 된 다음 리듀서에서 액션을 받아 업데이트 하기전에 추가적인 작업을 미들웨어를 통해 수행할 수 있다.
특정 조건에 따라 액션 무시 및 추가 작업, 수정, 로깅, 트리거 액션, 함수 호출 등을 수행하도록 할 수 있다.
주로 사용하는 용도는 비동기 작업을 처리할 때이다. (redux-thunk, redux-sage 등이 비동기 관련 미들웨어 라이브러리)
실제로 미들웨어를 직접 만들어서 쓸 일은 거의 없지만, 간단한 미들웨어를 만들어 동작하는 순서를 확인해본다.
이러한 형태로 미들웨어를 작성하게된다.
const middleware = store => next => action => {
//미들웨어 수행내용
}
이러한 의미를 가진다.
function meddleware(store){
return function (next) {
return function(action) {
//미들웨어 수행내용
}
}
}
미들웨어 기능 꺼내기
const { createStore, applyMiddleware, compose } = Redux;
const consoleLoggingMiddleware = store => next => action => {
console.log(action); // 액션 객체를 출력하는 콘솔로그 기능
const result = next(action); // 다음 미들웨어 또는 리듀서에게 액션을 전달
return result; // 반환하는 result는 dispatch(action)의 결과물
}
사용하려는 미들웨어를 두 번째 인자로 applyMiddleware 함수의 인자 형태로 전달한다.
const store = createStore(reducer, applyMiddleware(consoleLoggingMiddleware));
페이지 랜더링 시
아래 이미지와 같은 로그를 보여준다.
미리 만들어져 있는 미들웨어 사용해보기 (여러개의 미들웨어 사용하기)
로깅 미들웨어로 가장 많이 사용되는 미들웨어는 redux-logger 미들웨어이다.
redux-logger를 사용하기 위한 스크립트 추가
<script src="https://www.unpkg.com/redux-logger@3.0.6/dist/redux-logger.js"></script>
react-logger 미들웨어
const logger = reduxLogger.createLogger();
커스텀 미들웨어 - 액션을 출력하는 로그 기능을 가지는 간단한 미들웨어
const consoleLoggingMiddleware = store => next => action => {
console.log(action); // 액션 객체를 출력하는 콘솔로그 기능
const result = next(action); // 다음 미들웨어 또는 리듀서에게 액션을 전달
return result; // 반환하는 result는 dispatch(action)의 결과물
}
const { Provider } = ReactRedux;
const { createStore, applyMiddleware } = Redux;
/* 사용하려는 미들웨어를 두 번째 인자로 applyMiddleware 함수의 인자 형태로 전달한다. */
const store = createStore(reducer, applyMiddleware(consoleLoggingMiddleware, logger));
middleware를 사용하는 이유
문제 상황을 확인해본다.
Promise를 이용한 비동기 방식인 fetch 함수를 실행하게되면 async await 키워드를 이용해서 처리한다.
(비동기 요청에 대한 순서를 반드시 정해서 수행)
response 결과를 얻어오는데까지는 문제지만,
response를 반환할 때(return 구문) 비동기작업은 아직 실행 중이고 결과가 만들어지지 않은 상태인데 값을 리턴하려하기 때문에 Promise 객체 자체를 반환하게된다.
즉, 리턴은 위의 비동기 흐름과 상관없이 함수 자체를 종료시켜버리며 컨트롤할 수 없다.
/* 액션 */
const FETCH_DATA = 'FETCH_DATA';
/* 언더스코어로 연결된 단어는 카멜케이스로 치환되어 함수 이름으로 처리된다. */
const { fetchData } = createActions ({
[FETCH_DATA] : async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users').then(res=>res.json());
console.log('response:', response);
return [...response]; //액션객체의 payload가된다.
}
});
payload 자체가 Promise 객체가되어 스프레드 연산을 이용할 수 없다는 오류가 발생한다.
/* 리듀서 */
const reducer = handleActions ({
[FETCH_DATA] : (state, { payload }) => {
/* payload 자체가 Promise 객체가되어 스프레드 연산을 이용할 수 없다는 오류가 발생한다. */
return [...payload];
}
}, initialState);
위의 문제 상황을 해결한다.
action을 매개변수로 하는 콜백 함수를 async로 하여 api 호출 후 next에 action 객체를 다시 생성해서 호출한다.
next
: 액션객체를 다음 동작으로 넘기는 작업.(다음 미들웨어가 있다면 다음 미들웨어로, 없다면 리듀서 함수로 넘긴다.)
const fetchUser = store => next => async (action) => {
console.log('action', action);
const response = await fetch('https://jsonplaceholder.typicode.com/users').then(res=>res.json());
console.log('response : ', response);
next({ ...action, payload : response });
}
const { createStore, applyMiddleware } = Redux;
const { Provider } = ReactRedux;
const store = createStore(reducer, applyMiddleware(fetchUser));
미들웨어를 등록한 순서대로 동작하게된다.
미들웨어 로그 확인을 위해 3개의 미들웨어 작성
const firstMiddleware = store => next => action => {
console.log('first middleware');
const result = next(action);
return result;
}
const secondMiddleware = store => next => action => {
console.log('second Middleware');
const result = next(action);
return result;
}
const thirdMiddleware = store => next => action => {
console.log('third middleware');
const result = next(action);
return result;
}
스토어 설정
const store = createStore(reducer, applyMiddleware(secondMiddleware, firstMiddleware, thirdMiddleware));