Recent Posts
Recent Comments
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Today
Total
관리 메뉴

DH의 개발 공부로그

[Redux] Redux Example - 바닐라 자바스크립트 투두리스트 만들어보기! 본문

Redux

[Redux] Redux Example - 바닐라 자바스크립트 투두리스트 만들어보기!

DeveloperDH 2023. 3. 9. 20:38
728x90

프로젝트 설명

리덕스를 공부하는 이유가 리덕스를 이용해서 리액트에서의 상태관리를 보다 편하게 작업을 하기 위해서였는데,
공부를 해보니 꼭 리덕스가 리액트와 함께 사용이 안되어도 가능하다는 것을 알게 되었습니다.
그래서 우선 자바스크립트에서 사용하는 법을 먼저 연습해보고 리액트에서 사용법을 공부해야겠다 생각을 하여
자바스크립트 투두리스트를 만들어 보았습니다.

폴더구조

yarnvite로 작업을 하였고 폴더 구조는 다음과 같이 간단하게 작업을 했습니다.

index.html - body부분

<body>
  <h1>To Dos</h1>
  <form>
    <input type="text" name="toDo" placeholder="할 일을 작성 해주세요.">
    <button>Add</button>
  </form>
  <ul></ul>
  <script type="module" src="/src/index.js"></script>
</body>

index.js

1. 변수 선언 및 form 이벤트 추가

우선 변수들을 선언하고 onSubmit함수를 만들어 주었습니다.

const $form = document.querySelector('form');
const $input = document.querySelector('input);
const $toDolist = document.querySelector('ul');

const onSubmit = (e) => {
  e.preventDefault()
  const toDoValue = $input.value
  $input.value = ''
  addToDo(toDoValue) // 입력한 값을 추가해주는 함수
}

$form.addEventListener('submit', onSubmit)

2. Redux 추가

저는 yarn을 이용해서 작업을 했기 때문에 다음의 코드로 리덕스를 설치했습니다.

$ yarn add redux

그리고 우선 스토어와 리듀서 함수를 만들어 주었습니다.


const reducer = (state, action) => {
  console.log(state, action)
  if (state === undefined) {
    return { toDoList: [] }
  }
}
const store = createStore(reducer)

스토어를 생성하면서 reduce함수도 실행이 되기 때문에 초기 state값 설정을 조건문을 이용해서
undefined일 시 초기값을 리턴하도록 했습니다.

3. 리스트 추가하기

const ADD_TODO = "ADD TODO";

const reducer = (state, action) => {
  if (state === undefined) {
    return {
      toDoList: []
    }
  }
  switch(action.type) {
    case ADD_TODO: {
      const newToDoList = [{ text: action.text, id: Date.now() }, ...state.toDoList]
      const newState = Object.assign({}, state, {
        toDoList: newToDoList
      })
      return newState
    }
    default: {
      return state
    }
  }
}

const addToDo = (text) => {
  store.dispatch({ type: ADD_TODO, text })
}

위에 onSubmit함수에서 호출한 addToDo함수로 스토어에 디스패치하도록 하였으며,
이 디스패치를 받은 리듀서에서 switch문으로 action.type을 확인하도록 하였습니다.

타입이 ADD_TODO일 경우 우선 불변성을 지키기 위하여 기존의 toDoList 배열을 spread 연산자로 복사하고,
Object.assign로 기존의 값인 객체를 복사하여 추가한 후 복사한 값을 리턴해주었습니다.

4. UI 그리기

const paintTodo = () => {
  const state = store.getState();
  $toDolist.innerHTML = '';
  // 두번째로 추가할 때 기존에 있던 태그가 반복이 되기 때문에 초기화하기
  state.toDoList.map((toDo) => {
    const li = document.createElement('li'); // <li>태그 만들기
    const btn = document.createElement('button') // <button>태그 만들기
    btn.innerText = 'Del'; 
    btn.addEventListener('click', deleteTodo) // 삭제 이벤트 추가
    li.id = toDo.id; // 삭제를 위해 추가하는 iD 값
    li.innerText = toDo.text;
    li.appendChild(btn)
    $toDolist.appendChild(li);
  });
}
store.subscribe(paintTodo)

getState() 함수로 state값을 가져와서 반복문을 통해 li 태그를 만들어 주었고,
리덕스의 subscribe() 내장함수를 이용해서 state가 바뀌면 다시 함수를 호출 하도록 해주었습니다.

5. 리스트 삭제

// reducer에서 action.type이 DELETE_TODO인 경우
case DELETE_TODO : {
  const newToDoList = state.toDoList.filter((toDo) => {
    return toDo.id !== action.id
  })
  const newState = Object.assign({}, state, {
    toDoList: newToDoList
  })
  return newState
}

const deleteTodo = (e) => {
  const id = e.target.parentNode.id
  store.dispatch({ type: DELETE_TODO, id: parseInt(id) })
}

삭제 역시 마찬가지로 li 태그에 추가해둔 id 값을 디스패치로 보내주고,
filter를 이용해서 id 값을 비교하여 새로운 배열을 만들어 주었습니다.
그후 역시나 객체를 복사해 새로운 객체를 리턴하였습니다.

6. 로컬스토리지에 추가해 저장하기

const setLocalStorage = (toDos) => {
  const toDoList = JSON.stringify(toDos)
  window.localStorage.setItem('ToDos', toDoList)
}

const paintTodo = () => {
  // const state = store.getState();
  const localToDosList = JSON.parse(window.localStorage.getItem('ToDos'))
  $toDolist.innerHTML = '';
  localToDosList.map((toDo) => {
    const li = document.createElement('li');
    const btn = document.createElement('button')
    btn.innerText = 'Del';
    btn.addEventListener('click', deleteTodo)
    li.id = toDo.id;
    li.innerText = toDo.text;
    li.appendChild(btn)
    $toDolist.appendChild(li);
  });
}

setLocalStorage 함수를 만들어서 각각의 case에 추가를 해주었고,
paintTodo에서 JSON.parse(window.localStorage.getItem('ToDos'))로 로컬스토리지의 값을 불러와서 map함수를 돌리도록 하였습니다.

결과

728x90
Comments