리덕스는 액션 -> 미들웨어 -> 리듀서 -> 스토어 순으로 동작하며
리덕스의 middleware는 클로저를 이용한 구조로 작동한다. 클로저로 store , next를 외부에서 접근할 수 없고 오로지 action으로만 제어할 수 있도록 하는 구조이다.
const middleware1 = store = next = action =>{
return next(action);
}
or
const middleware1 = store =>{
return next=>{
return action=>{
return next(action);
}
}
}
미들웨어는 store , action을 기반으로 동작하며 next에 들어가는 매개변수는 다음 middleware이다.
리덕스의 사용법은 다음과 같다.
middleware1...
middleware2...
reducer...
const store = createStore(reducer,applyMiddlewares(middleware1,middleware2));
use
리덕스의 applyMiddleware 함수는 다음과 비슷한 함수를 가지고 있다.
const applyMiddleware = (...middlewares)=> createStore => (...args)=>{
const store = createStore(...args);
const funcWithStore = middlewares.map(middleware => middleware(store));
const chainedFunc = funcsWithStore.reduce((acc,cur)=>next=>acc(cur(next)));
return {
...store,
dispatch:chaindeFunc(store.dispatch);
};
}
createStore의 2번째 인자에 applyMiddlewares를 넣음으로써 미들웨어가 작동하게된다. 하지만 저 복잡한 3개의 체인이 어떻게 작동하는지는 코드만 보아서는 이해할 수 없다.
applyMiddleware를 이해해야 왜 store=>next=>action 순의 함수를 넣어야 하는지 이해할 수 있다.
applyMiddleware는 인자로 여러개의 인자를 받아 이를 middlewares라는 배열에 담는다.
또한 applyMiddlware가 리턴하는 2번째 함수는 createStore를 인자로 받으며
3번째 인자에서는 ...args를 받는다. 이 ...args는 createStore을 통해서 store를 생성하는데 사용된다.
첫번째 줄에서 createStore을 통해서 store를 생성했다.
2번째 줄에서는 middlewares들을 map 함수로 돌려 next=>action=>{...} 으로 이루어진 funcWithStore를 만들어낸다.
funcWithStore는 store가 클로저에 들어간 형태의 middlewares라고 보면 된다.
chainedFunc는 next를 실행시키며 클로저에 넣음과 동시에 미들웨어를 체이닝 거는 형태이다.
미들웨어가 a b c d e 5개라면
a(b(c(d(e(next))))) 가 될것이다.
chainedFunc에 의해서 체이닝된 함수는 action이 입력으로 들어오면 체이닝 되어서 실행되게 된다.
const m1=(next)=>input=>{
console.log('start m1');
console.log('get',input);
next(input+1);
console.log('end m1');
}
const m2=(next)=>input=>{
console.log('start m2');
console.log('get',input);
next(input+2);
console.log('end m2');
}
const m3=(next)=>input=>{
console.log('start m3');
console.log('get',input);
next(input+3);
console.log('end m3');
}
const m4=(next)=>input=>{
console.log('start m4');
console.log('get',input);
next(input+4);
console.log('end m4');
}
const start = next=>input=>{
console.log('start');
return input;
}
// 1. 일단 m 들을 next을 넣어 체이닝한후
// 2. 단일 인자를 받아 next를 순차적으로 처리하는 함수를 만들어야 한다.
//여기서 next가 하는 일은 내가 원하는건 m1 m2 m3 m4 껍질을 벗긴후
//리턴되는 input값을 체이닝해줌.
//acc(cur)로 바로 체이닝을 걸면 input함수만 남아 나머지 체이닝이 불가능 하기 때문에
//(next) 함수로 클로저로 체이닝을 시도함
//미들웨어의 끝은 항상 next를 호출하지 않는 middlewares로 마무리를 지어줌.
const middleware_chain = (...middlewares) => {
let chain = middlewares.reduce((acc, cur) => {
return (next)=>{
return acc(cur(next));
}
});
const endOfChain=next=>()=>{};
return chain(endOfChain);
};
let chain = middleware_chain(
m1,m2,m3,m4
);
//start는 next를 호출하지 않는 미들웨어. next를 호출하지 않아 미들웨어 체이닝이 끊기게됨
let no_next_chain = middleware_chain(m1,m2,start,m3,m4);
console.log("chain", chain(1));
console.log("no_next_chain",no_next_chain(1));