applyMiddleware 이해하기.

라코마코·2020년 4월 9일
1

JS

목록 보기
5/6

리덕스는 액션 -> 미들웨어 -> 리듀서 -> 스토어 순으로 동작하며

리덕스의 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));

0개의 댓글