Todo App v0

연성·2022년 3월 3일
0

Todo App

목록 보기
1/2
post-thumbnail

React를 배우기 전 HTML, JS를 복습하기 위해 Todo App을 만들었다.

Version 0

  • version 0는 HTML, CSS, Javascript만을 이용하여 만들었다.

HTML 구조

<body>
    <h1 class="title">Todo App</h1>
    <div class="todos">
      <input class="toggle-all" type="checkbox" />
      <input class="todo-input" type="text" placeholder="Write todo..." />
      <ul class="todo-list">
        <!-- <li data-id="1" class="todo-item">
          <input class="toggle" type="checkbox" />
          <span class="content">html</span>
          <input class="content-edit" type="text" />
          <button class="remove" type="button">x</button>
        </li> -->
      </ul>
      <span class="count">1 item left</span>
    </div>
  </body>

주석으로 처리한 <li> ~ </li>는 자바스크립트로 동적으로 생성하여 주었다.

Todo 데이터 구조

todos = todo[]
todo: {
  id: number,
  content: string,
  completed: boolean
}

todos = [
  { id: 0, content: 'HTML', completed: false },
  { id: 1, content: 'CSS', completed: true },
  { id: 2, content: 'JS', completed: false },
];

todo 객체의 배열 todos로 어플리케이션을 컨트롤
todo는 id, 내용, 완료 여부로 구성된 객체

Todo App Interaction

  • Todo 목록 받아오기
  • 새로운 할 일 추가
  • 할 일 내용 변경
  • 완료한 일 체크
  • 할 일 삭제

Todo 목록 받아오기

  • version 0에서는 app.js 파일 안에 todos 배열을 만들어 관리하였다.
  • 페이지가 리로드 되면 기존 데이터가 사라진다...
  • todos 배열을 이용하여 화면에 할 일 목록을 그려준다.
  • todos 배열의 길이를 이용하여 count를 화면에 보여준다.
// render.js

const render = () => {
  $todoList.innerHTML = todos
    .map(
      ({ id, content, compelted }) => `
    <li data-id="${id}" class="todo-item">
      <input class="toggle" type="checkbox" ${compelted ? 'checked' : ''} />
      <span class="content">${content}</span>
      <input class="content-edit hidden" type="text" />
      <button class="remove" type="button">x</button>
    </li>
  `
    )
    .join('');

  $count.textContent = `${todos.length} item${
    todos.length > 1 ? 's' : ''
  } left`;
};
  • input.content-edit은 처음 렌더링 될 때는 숨겼다가 유저가 더블 클릭을 하면 나타나게 구현하였다.
  • 더블 클릭을 하는 경우 span.content는 숨겨진다.
  • render 함수는 todos가 변경될 때마다 호출된다.
  • todos가 변경되었는데 화면에 반영되지 않는 오류 발생 가능성을 줄이기 위해 모든 변경 후에는 setTodo를 호출하였고 setTodorender를 호출한다.
const setTodo = _todos => {
  todos = [..._todos];
  render();
};

새로운 할 일 추가

  • 유저가 새로운 할 일을 제출하면 todos가 업데이트 된다.
const getNextId = () => Math.max(...todos.map(todo => todo.id), 0) + 1;

const addTodo = content => {
  const _todos = [{ id: getNextId(), content, compelted: false }, ...todos];
  setTodo(_todos);
};

$todoInput.onkeyup = e => {
  const { code } = e;
  const todo = e.target.value.trim();
  if (code !== 'Enter' || todo === '') return;

  e.target.value = '';
  addTodo(todo);
};
  • 새로운 아이디는 (todos의 가장 큰 아이디 || 0) + 1로 생성하였다.
  • 아무것도 입력하지 않거나 공백만 입력한 경우 새로운 할 일을 추가하지 않는다(addTodo를 호출하지 않는다).
  • addTodotodos 가장 앞에 새로운 todo를 삽입한 새로운 _todossetTodo를 호출한다.

할 일 변경

  • 할 일을 더블 클릭하면 할 일을 변경할 수 있는 input창이 나온다.
  • input.value는 할 일로 초기화한다.
$todoList.ondblclick = ({ target }) => {
  if (!target.matches('.content')) return;
  target.classList.add('hidden');

  const contentEdit = target
    .closest('.todo-item')
    .querySelector('.content-edit');
  contentEdit.value = target.textContent;
  contentEdit.classList.remove('hidden');
};
  • 사용자로부터 입력 받은 할 일을 갱신하여 준다.
const editTodo = (id, content) => {
  const _todos = todos.map(todo =>
    todo.id === +id ? { ...todo, content } : todo
  );
  setTodo(_todos);
};
  • _todos는 파라미터로 전달 받은 id와 같은 todo만 content를 변경하고 나머지 todo는 그대로 반환된 배열이다.

완료한 일 체크

  • todo에 해당하는 체크박스를 클릭하면 완료 여부가 변경된다.
const toggle = id => {
  const _todos = todos.map(todo =>
    todo.id === +id ? { ...todo, compelted: !todo.compelted } : todo
  );
  setTodo(_todos);
};

$todoList.onclick = ({ target }) => {
  if (!target.matches('.toggle, .remove')) return;
  const { id } = target.closest('.todo-item').dataset;
  target.matches('.toggle') ? toggle(id) : removeTodo(id);
};
  • 파라미터로 전달 받은 id와 같은 todo만 반전시켜준다.
  • 파라미터로 전달 받은 id는 html의 data-id 속성에서 가져왔기 때문에 숫자 타입으로 형 변환을 해주었다.
  • 체크 박스와 삭제 버튼의 이벤트 핸들러의 동작이 유사하기 때문에 부모 요소에 버블링해주었다.

할 일 삭제

  • 삭제 버튼을 클릭하면 todos 배열에서 해당 todo를 삭제한다.
const removeTodo = id => {
  const _todos = todos.filter(todo => todo.id !== +id);
  setTodo(_todos);
};
  • 파라미터로 전달 받은 id와 동일한 todo를 필터링한 새로운 _todossetTodo에 전달한다

개선하고 싶은 점

  • 리로드 되어도 데이터가 유지되는 어플리케이션!
  • 데이터를 어딘가에 저장해야 한다.
  • 예쁘게 꾸며야 한다.
  • 최종적으로 리액트로 만들고 싶다.

0개의 댓글