[Redux] Javascript + Redux로 만든 To Do List

이찬형·2020년 3월 17일
1
post-thumbnail

Redux


Redux, 리덕스는 상태 관리 라이브러리입니다.

리덕스를 사용하면 상태 관련 로직을 다른 파일로 분리시킬 수 있고, 상태를 공유할 때 여러 컴포넌트를 거치지 않아도 된다는 장점이 생겨요.

이 친구를 어떻게 사용하는지 Todo 앱을 만들어보면서 배우도록 하겠습니다.

자바스크립트를 처음 배울 때는, Todo 리스트를 만들기 위해 전역변수를 선언했어요.

그 때 사용했던 로직과 비슷하게 흘러가겠지만 리덕스를 통해 Todo 리스트를 관리한다는 차이점을 기억하며 시작해봐요!!

Setup

우선 create-react-app으로 프로젝트를 하나 만들었어요.
여기에 npm install redux로 리덕스 라이브러리를 설치했습니다.

지금 당장은 리액트를 사용할 것이 아니니 index.htmlindex.js를 제외한 파일을 지워줬습니다.

이제 index.html에 폼과 리스트를 작성할게요.

<body>
    <h1 class="todoTitle">To Do</h1>
    <form class="todoForm">
      <input type="text" class="todoInput" placeholder="Write To do" />
    </form>
    <ul class="todoList"></ul>
  </body>

기본적인 틀을 만들었습니다. 이제 js로 넘어가요!

index.js

HTML에서 작성한 태그들을 먼저 불러옵시다. 폼에 이벤트리스너도 열게요.

const todoForm = document.querySelector(".todoForm");
const todoInput = document.querySelector(".todoInput");
const todoList = document.querySelector(".todoList");

todoForm.addEventListener("submit", handleSubmit);

createStore

그리고 리덕스를 이용하여 저장소를 만들거에요!! 아래와 같이 추가합니다.

import { createStore } from "redux";

// 변수들..

const store = createStore(reducer);

createStore()를 이용하여 store 저장소를 만들었습니다.
이 함수는 reducer()라는 함수를 인자로 받아요.

reducer

리덕스에서 reducer는 정말 중요합니다!!
이 함수가 리턴하는 값이 곧 어플리케이션의 데이터가 돼요.

const reducer = (count = 0) => {
  return count;
}

이런 식으로 작성합니다.
인자에 count = 0을 넣어줘서 초기값을 설정한 것이고 리턴되는 값에 따라 이 변수는 바뀌어요.

이제 어떤 조건일 때 값을 바꿔야 하는 지 알려줘야겠죠?
reducer()의 두 번째 인자인 action을 사용하면 됩니다.

const reducer = (count = 0, action) => {
  switch (action.type) {
    case "ADD":
      return count + 1;
    case "MINUS":
      return count - 1;
    default:
      return count;
}

이렇게요.

그렇다면 이 함수에게 어떻게 action을 줄까요?
dispatch() 함수가 그 역할을 해줘요.

dispatch

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

dispatch() 함수는 reducer()에게 메시지를 보냅니다.

dispatch() : action 객체를 줄게! type은 "ADD"야!

reducer()는 이 메시지를 받고 switch 문에 의해 알맞는 값을 반환해요.
이 값이 결국 우리의 상태가 되는 것입니다.

이걸 응용해서 투두 리스트에 적용해봐요!!

const reducer = (toDos = [], action) => {
  switch (action.type) {
    case "ADD":
      return [];
    case "DELETE":
      return [];
    default:
      return toDos;
  }
};

리턴값은 아직 정하지 않았어요. 천천히 만들어 볼게요!

우선 폼에서 submit이 왔을 때 처리하는 함수인 handleSubmit()을 작성하겠습니다.

const handleSubmit = event => {
  event.preventDefault();
  const toDo = todoInput.value;
  todoInput.value = "";
  store.dispatch({ type: "ADD", text: toDo });
};

dispatch()를 통해 action 객체를 전달했어요,
이제 reducer()가 이 메시지를 받아서 state를 업데이트합니다.

const reducer = (toDos = [], action) => {
  switch (action.type) {
    case "ADD":
      return [{text: action.text, id: Date.now()}, ...toDos];

reducer() 부분의 리턴값을 바꿨어요.
새로운 배열을 리턴하는 코드인데, action으로 받은 문자열을 text에 저장하고 id값도 줬습니다.
전개연산자로 기존에 존재하는 toDos 배열을 풀어서 하나의 새로운 배열로 만든거에요.

이렇게 reducer()에서 상태가 업데이트되면 그걸 감지하는 함수가 또 필요하겠죠?
바로 subscribe()입니다. 인자로 받은 함수를 업데이트 시 실행해요.

subscribe

store.subscribe(() => console.log(store.getState()));

handleSubmit()에서 dispatch()가 발생, reducer()에서 상태가 업데이트됐으니 콘솔에 state가 찍힙니다.

따라서 이 함수에 리스트를 출력하는 함수를 담는다면 state가 바뀔 때마다 리스트가 생성되겠죠?

작성해봅시다.

const printToDos = () => {
  const toDos = store.getState();
  todoList.innerHTML = "";
  toDos.forEach(todo => {
    const li = document.createElement("li");
    const btn = document.createElement("button");
    btn.addEventListener("click", handleClick);
    li.innerText = todo.text;
    li.id = todo.id;
    btn.innerText = "X";
    li.appendChild(btn);
    todoList.appendChild(li);
  });
};

store.subscribe(printToDos);

submit이 실행될 때마다 dispatch()가 state를 바꾸니 printToDos()가 계속 호출돼요.

얘는 리스트를 HTML에 뿌려줍니다.

이제 거의 다 왔어요!! X 버튼을 누르면 리스트가 지워지도록 만들어봅시다.

우선 버튼에서 열린 이벤트 리스너가 처리할 함수를 작성할게요.

const handleClick = event => {
  const id = event.target.parentNode.id;
  store.dispatch({type: "DELETE", id});
};

이번 dispatch()는 이런 메시지를 보내요.

dispatch() : action 객체를 줄게! type은 "DELETE"야!

이걸 받은 reducer()는 DELETE를 찾아 들어갑니다.

const reducer = (toDos = [], action) => {
  switch (action.type) {
    case "ADD":
      return [{ text: action.text, id: Date.now() }, ...toDos];
    case "DELETE":
      return toDos.filter(todo => todo.id !== parseInt(action.id));

항상 새로운 배열을 리턴해야된다는 것!! 진짜 중요해요.

따라서 filter로 조건에 맞는 친구들만 있는 배열을 만들어서 리턴했습니다.

완성!

이렇게 Redux를 사용해서 상태관리를 해보고, 투두 리스트를 만들었어요.

전체적인 흐름을 정리하자면,

  • createStore에 의해 저장소 생성, reducer를 인자로 받음
  • reducer에서만 상태 변경 가능!! 얘가 리턴한 값이 곧 상태가 됨
  • reducer는 두 개의 인자를 받음, 관리할 상태 변수와 action
  • dispatch를 통해 action을 설정, 이걸 reducer에게 전달
  • subscribe는 dispatch에 의해 상태가 업데이트되면 인자로 받은 함수 실행

이 정도겠네요.

아직 개념이 어렵고 익숙하지 않다 보니깐 헷갈립니다 ㅠㅠ 여러 번 코딩을 다시 해봐야겠어요.

조금 친숙해지면 리액트에도 적용해보겠습니다!! 감사합니다 😊

profile
WEB / Security

1개의 댓글

comment-user-thumbnail
2021년 5월 17일

좋은글 감사합니다 :) 예제 따라 해보니 리덕스의 흐름은 조금 보이는것 같아요

답글 달기