[우아한 테크러닝 4기] redux 구현해보기

2021. 7. 2. 19:07Web_Programming

 

안녕하세요. 이번 포스팅에서는 제가 지금 참여하고 있는 우아한 테크러닝 4기에서 들었던 강의 내용을 다뤄보려 합니다!

이번 강의에서는 redux를 javaScript를 이용하여 간단하게 구현하면서, redux에 대한 개념을 깊이 배워볼 수 있었습니다.


💻 리덕스(redux)란?

제가 생각한 리덕스는 간단하게 정의해보면 "외부에서도 전역적으로 상태관리를 가능하게 해주는 라이브러리"라고 볼 수 있을 것 같아요.

 

아래 그림처럼,

하나의 리덕스 store라는 곳에 상태 값들을 저장해 두고 외부 컴포넌트에서 접근할 수 있도록 하는 시스템이죠.

 

간단하게 리덕스에 대해 정리를 하고, 구현으로 넘어가 보겠습니다.

먼저 리덕스에서 사용되는 주 용어는 다음과 같습니다.

 

🔸 스토어 (Store) 

상태 값들을 저장해두는 공간

 

🔸 액션 (Action)

"상태 변화를 위한 수행을 나타내는 객체"

아래와 같이 필수적으로 요구되는 type값으로 액션들이 구분됩니다.

거기에 추가적으로 액션에 다루고 싶은 data들도 추가할 수 있습니다. 

{
  type: "ACTION", //필수항목
  data: {
    id: 0,
    text: "리덕스 배우기"
  }
}

 

🔸 리듀서 (Reducer)

"스토어의 액션들을 수행하도록 해주는 함수"

위에서 말한 action값의 type으로 구분하여 state를 변경하고 이를 반환해주는 함수입니다.

function reducer(state, action) {
  switch (action.type) {
        case SET_DIFF:
          return {
            ...state,
            diff: action.diff
          };
        case INCREASE:
          return {
            ...state,
            number: state.number + state.diff
          };
        default:
          return state;
    }
}

 

🔸 디스패치 (Dispatch)

스토어의 내장 함수로, "수행할 액션을 인자로 받고 리듀서를 실행시켜 액션을 수행"해주는 역할을 합니다.

 

 

+ 추가적으로 강의를 들으면서 리덕스에 사용되어 같이 설명이 되었던 것이 closure, 커링 함수인데요.

이에 대한 내용은 아래 포스팅에서 확인해 볼 수 있습니다.

https://keeper.tistory.com/26

 

[자바스크립트] 클로저 (Closure) 함수&커링

우아한 테크러닝에서 리덕스에 대한 강의를 수강하면서, 같이 언급되었던 것이 클로저 함수와 커링 함수입니다. 리덕스에 이 클로저함수가 사용이 되는데요, 이번 포스팅은 클로저함수가 무엇

keeper.tistory.com

 


💻리덕스 구현하기

 

구현 전체 코드는 아래에 작성하겠습니다 :-)

 

리덕스는 강의해주셨던 민태 님도 이렇게 간단한 코드로 이렇게 히트 친 것이 정말 드문 케이스라고 하시더라고요,

실제로도 구현을 해보니 몇 줄의 코드들로 구현이 가능해서 좀 놀랐습니다.

 

👉 createStore

store를 생성하는 createStore 함수를 살펴볼게요.

function createStore(reducer) {
  let state;
  let handler = [];
  reducer(state, {
    type: "@@__init__@@"
  });
  return {
    dispach: (action) => {
      state = reducer(state, action);
      handler.forEach((h) => {
        h();
      });
    },
    subscribe: (listener) => {
      handler.push(listener);
    },
    getState: () => state
  };
}

 

createStore의 지역 변수로 

  • "action으로 사용될 state"와
  • "subcribe를 수행하기 위한 handler"를 가집니다.

이 스코프(변수)들을 반환 함수에서 사용 가능하도록 앞서 말했던 클로저 함수가 사용되었는데요.

 

반환 함수로 store를 통해 사용 가능한 함수 dispatch, subscribe, getStore를 만들어줍니다.

 

🔸 dispatch

인자로 받은 action으로 reducer를 수행하여 변경된 state값(복사본)을 반환받고, 이를 state(원본)에 담아줍니다.

또한, dispatch로 action이 수행될 때마다

handler 리스트에 들어있는 subscribe handler들을 forEach문을 통해 수행해줍니다.

 

🔸 subscribe

subscribe 호출 시 받은 인자 listener를 handler에 넣어 dispatch에서 수행가능하도록 push 해줍니다.

 

🔸 getStore는 간단하게 state값을 반환시켜주는 함수입니다.

 

 

👉reducer

action을 수행해주는 함수 reducer를 살펴보겠습니다.

const InitState = {
  type: "",
  counter: 0,
  porfile: {
    id: "",
    imageUrl: ""
  }
};
function reducer(state = InitState, action) {
  switch (action.type) {
    case "counter":
      return { ...state, counter: action.counter };
    case "action":
      return { ...state, type: action.action };
    default:
      return { ...state };
  }
}

reducer는 인자로 state, action을 받습니다.

초기 state값으로 InitState로 선언하여 reducer의 state로 선언해줍니다.

 

인자로 받은 aciton의 type을 switch case를 이용하여 action을 구분하고, action에 따라 변경된 state값을 반환해줍니다.

 

🔸 여기서 중요한 것은 state 원본 값의 변경은 제어 가능하도록 createStore내에서만 변경하도록 해야 하기 때문에,

외부에서는(reducer) state를 복사하여 값을 변경하고 그 값을 반환해 createStore에서 원본 값을 변경하도록 하는 것입니다.

 

 

👉 actionCreator

actionCreator는 type과 data를 인자로 받아 action을 반환하여 dispatch에 넘겨주도록 해주는 함수입니다.

function actionCreator(type, data) {
  return {
    type: type,
    ...data
  };
}

type, data를 선언하여 직접 dispatch로 넣어줘도 되지만, 간략화하고 가독성을 높여주기 위해 별도의 함수로 작성합니다.


마지막으로 구현한 redux들을 사용한 코드입니다.

const store = createStore(reducer);

store.subscribe(() => {
  console.log("알림", store.getState());
});
function foo() {
  store.dispach(actionCreator("counter", { counter: 1 }));
}
function zoo() {
  store.dispach(actionCreator("action", { action: "fetch" }));
}
foo();
zoo();

🔹 store를 createStore와 구현한 reducer를 이용하여 생성해줍니다.

🔹 store의 subscribe에 수행할 handler를 인자로 넣어줍니다.

+getStore를 통해 변경된 state값을 반환해주는 handler

 

🔹 임의의 foo, zoo함수에서

- store.dispach(actionCreator("counter", { counter: 1 })); 와 같이

- actionCreator로 { type: "counter", counter : 1 }의 action을 생성하여 dispatch에 넣어줍니다.

- reducer를 통해 counter값을 0->1로 변경한 state를 반환받아 createStore에서 state값을 변경해줍니다.

 

🔹 이렇게 foo함수와 zoo함수를 호출하게 되면,

state가 변경되고, 콘솔에 다음과 같이 찍히며 subscribe가 수행됩니다.

알림 {type"", counter1, porfileObject}

알림 {type"fetch", counter1, porfileObject}

 

이렇게 해서 redux를 구현할 수 있었습니다.

 

전체 코드는 아래와 같습니다.

function createStore(reducer) {
  let state;
  let handler = [];
  reducer(state, {
    type: "@@__init__@@"
  });
  return {
    dispach: (action) => {
      state = reducer(state, action);
      handler.forEach((h) => {
        h();
      });
    },
    subscribe: (listener) => {
      handler.push(listener);
    },
    getState: () => state
  };
}
const InitState = {
  type: "",
  counter: 0,
  porfile: {
    id: "",
    imageUrl: ""
  }
};
function reducer(state = InitState, action) {
  switch (action.type) {
    case "counter":
      return { ...state, counter: action.counter };
    case "action":
      return { ...state, type: action.action };
    default:
      return { ...state };
  }
}
const store = createStore(reducer);
function actionCreator(type, data) {
  return {
    type: type,
    ...data
  };
}
store.subscribe(() => {
  console.log("알림", store.getState());
});
function foo() {
  store.dispach(actionCreator("counter", { counter: 1 }));
}
function zoo() {
  store.dispach(actionCreator("action", { action: "fetch" }));
}
foo();
zoo();

 


리덕스는 많은 분들이 러닝 커브가 높다고들 말합니다. 저 또한 개념은 간단하지만 처음 사용할 때 꽤 버벅 거렸습니다..

물론 지금도 리덕스 마스터는 못했지만, 이렇게 직접 코드를 구현해보니 함수를 통해 데이터가 변경되고 넘어가는 흐름이 깊게 이해가 된 것 같습니다. 들으면서도 재밌다는 느낌이 들었습니다 🧡💛

이렇게 이번 redux구현 포스팅을 마치겠습니다. 

반응형

'Web_Programming' 카테고리의 다른 글

[자바스크립트] 클로저 (Closure) 함수&커링  (0) 2021.06.28
d3.js csv파일로 table만들기  (0) 2021.03.30