Vanilla JS + Redux 입문: counter

2023. 10. 10. 01:16Trip to Redux

1. Redux를 왜 배우는가?

> 이 내용은 노마드 코더 Redux 강의를 기반하고 있다. 무료니까 시간나면 다들 한 번 보시길.

 

> 사실 나는 상태 관리를 위해서 리코일을 배웠다. 하지만, 아직 리덕스가 여전히 상태 관리 라이브러리로서 큰 파이를 차지하고 있기에 리덕스를 알아둘 필요가 있다고 생각했다. 게다가 만약 기업에서 상태 관리 라이브러리를 변경을 위해 나중에 정말 편한 주스타드로 넘어가더라도 리덕스를 알아둬야  알고 넘어갈 수 있기 때문이다. 강의는 아주 쉬운 코드로 시작한다. 

2. Redux는 왜 필요한가?

> 상태관리를 위해서 필요하다. 어플리케이션의 복잡도가 올라가면 올라갈수록 전역에 퍼져있는 상태를 찾아서 관리하기 어렵기에 한 곳에 모아서 관리한다는 것이 상태 관리 라이브러리의 존재 목적이다.

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");

let count = 0;

const updateText = () => {
  number.innerText = count;
};

const handleAdd = () => {
  count = count + 1;
  updateText();
};

const handleMinus = () => {
  count--;
  updateText();
};

add.addEventListener("click", handleAdd);
minus.addEventListener("click", handleMinus);

2-1. createStore 메서드 deprecated?

개발 처음 배우는 코드다. 여기서 포인트는 화면을 업데이트 하기 또 다른 함수를 작성하였다. 리덕스를 설치한다.

store는 data 즉, state를 넣는 곳이라고 한다. 상태를 관리할 저장소를 만들어보자 createStore를 import 하자마자 취소선이 그어진다. 확인해보니,

 

createStore를 쓰지말고 툴킷의 configureStore를 쓰라고 한다. 나에게서 레거시를 배울 기회를 빼았았다. 리덕스 코드로 들어가보니 상세하게 설명되어 있다. 

정 쓰고 싶은면 Legacy_createStore as createStore로 import해서 쓰라고 한다. 그래서 그렇게 했다.

 

그리고 Live Server를 켜니 

 

Uncaught TypeError: Failed to resolve module specifier "redux". Relative references must start with either "/", "./", or "../".

 

redux 라이브러리를 읽지 못하고 있다. npm run dev로 돌아가니 패스.

3. Redux는 어떻게 사용하는가?

3-1. store와 reducer 함수

import { legacy_createStore as createStore } from "redux";

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");

const reducer = () => {};

const store = createStore(reducer);

> store라는 데이터를 저장할 수 있는 변수를 만들어주고 reducer는 이 데이터를 변경하는 역할을 한다. 역할대로 이름을 변경해보면 아래와 같다.

import { legacy_createStore as createStore } from "redux";

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");

const countModifier = () => {
  return "hello";
};

const countStore = createStore(countModifier);

console.log(countStore);

> 스토어를 콘솔 찍어보면 여러 함수가 들어가 있다.

> 여기서 getState함수를 찍어보면 hello를 출력한다. reducer 함수가 반환하는 값은 상태, 즉 우리가 관리하는 데이터 값이다.

3-2. action

import { legacy_createStore as createStore } from "redux";

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");

const countModifier = (count = 0, action) => {
  console.log(count, action);
  return count;
};

const countStore = createStore(countModifier);

console.log(countStore.getState());

> action을 콘솔을 찍어보니 이상한 문자 투성이다.

> 우리가 원하는건 숫자 증감이다. action이라는 것에 요청을 해야하는데 요청 방법은 아래와 같다.

> 반드시 지켜야하는 룰이 있는데 dispatch에서 객체로 보내야하고 type이라는 키값은 변경할 수 없다.

import { legacy_createStore as createStore } from "redux";

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");

const countModifier = (count = 0, action) => {
  if (action.type === "ADD") {
    console.log("they are telling me to add one");
  }
  return count;
};

const countStore = createStore(countModifier);

countStore.dispatch({ type: "ADD" });

> 결과적으로 카운터는 아래와 같다. 

import { legacy_createStore as createStore } from "redux";

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");

const countModifier = (count = 0, action) => {
  if (action.type === "ADD") {
    return count++;
  } else if (action.type === "MINUS") {
    return count--;
  } else {
    return count;
  }
};

const countStore = createStore(countModifier);

countStore.dispatch({ type: "ADD" });

3-3. Subscription

> 이벤트로 달아서 확인하면 작동을 안한다.화면에서 출력이 안되는데 subscribe가 이 부분을 담당한다.

import { legacy_createStore as createStore } from "redux";

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");

const countModifier = (count = 0, action) => {
  if (action.type === "ADD") {
    return count + 1;
  } else if (action.type === "MINUS") {
    return count - 1;
  } else {
    return count;
  }
};

const countStore = createStore(countModifier);

add.addEventListener("click", () => countStore.dispatch({ type: "ADD" }));
minus.addEventListener("click", () => countStore.dispatch({ type: "MINUS" }));

> 이 코드에서 subscribe를 추가해보자

import { legacy_createStore as createStore } from "redux";

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");

const countModifier = (count = 0, action) => {
  if (action.type === "ADD") {
    return count + 1;
  } else if (action.type === "MINUS") {
    return count - 1;
  } else {
    return count;
  }
};

const onChange = () => {
  console.log(countStore.getState());
  number.innerText = countStore.getState();
};

const countStore = createStore(countModifier);

countStore.subscribe(onChange);

add.addEventListener("click", () => countStore.dispatch({ type: "ADD" }));
minus.addEventListener("click", () => countStore.dispatch({ type: "MINUS" }));

 

> redux를 알면 알수록 왜 다른 상태 라이브러리를 쓰는지 느끼고 있다. 

3-4. Refactor

> 현재는 if/else if가 되지만 안될 때가 더 많기에 리덕스에는 switch문을 쓴다. 공식문서 예제로 switch문으로 작성

import { legacy_createStore as createStore } from "redux";

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");
number.innerText = 0;
const countModifier = (count = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return count+ 1
    case 'DECREMENT':
      return count - 1
    default:
      return count
  }
};

const onChange = () => {
  number.innerText = countStore.getState();
};

const countStore = createStore(countModifier);

countStore.subscribe(onChange);

add.addEventListener("click", () => countStore.dispatch({ type: "INCREMENT" }));
minus.addEventListener("click", () => countStore.dispatch({ type: "DECREMENT" }));

> 언제나 철자는 틀릴 수 있기에 변수에 담아준다.

import { legacy_createStore as createStore } from "redux";

const add = document.querySelector(".add");
const minus = document.querySelector(".minus");
const number = document.querySelector(".number");
number.innerText = 0;

const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'


const countModifier = (count = 0, action) => {
  switch (action.type) {
    case INCREMENT:
      return count + 1;
    case DECREMENT:
      return count - 1;
    default:
      return count;
  }
};

const onChange = () => {
  number.innerText = countStore.getState();
};

const countStore = createStore(countModifier);

countStore.subscribe(onChange);

add.addEventListener("click", () => countStore.dispatch({ type: INCREMENT }));
minus.addEventListener("click", () =>
  countStore.dispatch({ type: DECREMENT })
);