동기(sync) 방식에서는 리듀서에서 액션에 대한 처리가 모두 끝나고,
리덕스 스토어에 들어있는 State가 업데이트 된 이후에 프로그램의 흐름이 이어진다.
반면, 비동기(Async) 방식에서는 리듀서에서 액션에 대한 처리가 모두 끝나기 전에,
프로그램의 흐름은 계속 이어지고, 리듀서에서 액션에 대한 처리가 끝난 이후에 프로그램에 알려주는 형태이다.
리덕스에서는 기본적으로 비동기 로직을 허용하지 않지만,
미들웨어(Middleware)를 사용하여 비동기 작업(예: 네트워크 요청)을 처리할 수 있다.
비동기 로직을 처리해주는 미들웨어 중에서 대표적인 것이 redux-thunk
와 redux-saga
이다.
리덕스의 Flux 패턴에서 맨 처음 액션을 디스패치 하게 되면
리듀서에서 해당 액션에 대한 정보를 바탕으로 스토어의 상태값을 바꾸게 되는데,
이 때 미들웨어를 사용하면 액션이 스토어에서 상태값을 바꾸기 전에 특정 작업들을 수행할 수 있다.
예를 들면, 다음과 같은 역할을 수행한다.
실제 프로젝트를 작업할 때, 대부분의 경우에는 이미 만들어져있는 미들웨어를 사용하지만
미들웨어가 실제로 어떻게 작동하는지 이해하기 위해서 미들웨어를 직접 만들어보자.
src/lib
디렉토리에 loggerMiddleware.js
파일을 생성한다.src/lib/loggerMiddleware.js
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; // 여기서 반환하는 값은 store.dispatch(ACTION_TYPE)했을 때의 결과로 설정
}
export default loggerMiddleware; // 불러와서 사용할 수 있도록 내보내기 한다.
여기서 next
는 store.dispatch
와 비슷한 역할을 한다.
차이점은 next(action)
의 경우, 바로 리듀서로 넘기거나 혹은 다음 미들웨어가 처리되도록 진행되지만,
store.dispatch
의 경우, 처음부터 다시 액션이 디스패치되기 때문에 현재 미들웨어를 다시 한번 처리한다.
applyMiddleware
를 사용하여 설정할 수 있다.src/store.js
import { createStore, applyMiddleware } from 'redux';
import modules from './modules';
import loggerMiddleware from './lib/loggerMiddleware';
// 미들웨어가 여러 개인 경우에는 파라미터로 여러 개를 전달해준다. 예: applyMiddleware(a,b,c)
// 미들웨어의 순서는 여기서 전달한 파라미터의 순서대로 지정된다.
const store = createStore(modules, applyMiddleware(loggerMiddleware))
export default store;