HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
📎
운영진을 위한 문서 모음
/
🤧
주차별 액션 안내
/
🌮
14~18주차
/
💌
18주차 액션 포인트
/임효성/
📝
TodoList 만들기의 사본
📝

TodoList 만들기의 사본

👉 todolist 체험하기
👉 깃허브 코드보기
notion image

🚩 목표

Function

✅ 날짜, 시간
✅ 할 일 잔여개수
✅ 일정 추가, 수정, 삭제
✅ 일정 토글

Point

✅ new 방어 코드 작성
✅ 컴포넌트 코드 작성
✅ 글자수 제한, 반응형, scroll bar
TodoList 프로토타입
TodoList 프로토타입
 

🔎 기본 동작 원리

  • 상태를 기반으로 그림.
  • 상태는 1) 바꾼다, 2) 상태가 바뀔 때 그릴 때 관리함.
  • 상태는 event, method를 통해 상태를 바꿀 타이밍을 정함.
  • 여기서의 상태 주체는 component임.
    • js 컴포넌트 구성방식
      js 컴포넌트 구성방식
 

🗓️ 날짜, 시간 구현하기

  • Date()객체는 1970년 1월 1일 UTC(협정 세계시) 자정과의 시간 차이를 밀리초로 나타내는 정수 값을 담음.
  • 사용한 인스턴스 메서드
    • Date.prototype.getFullYear()
    • Date.prototype.getMonth() : 축약형을 적기 위해선 short 키워드를 사용해야 함.
    • Date.prototype.getDate()
    • Date.prototype.getDay()
    • Date.prototype.getHours() : 현지 시간 기준 시(0–23)를 반환를 반환함. %12로 12시간 단위로 표시 가능.
    • Date.prototype.getMinutes() : 현지 시간 기준 분(0–59)을 반환함. 10이 넘지 않을 경우 0을 붙여줘야 간격이 어색해지지 않음.
  • setIntervals() 로 1초마다 갱신할 수 있도록 함.
  • innerHTML로 현재 날짜 및 시간을 업데이트 함.
this.$target.innerHTML = ` <div class="date"> <span>${date}</span> <div class="date__middle"> <span>${month}</span> <span>${year}</span> </div> <span>${day}</span> </div> <div class="time">${hours}:${minutes} ${hours > 12 ? "AM" : "PM"}</div>
innerHTML로 브라우저에 그리는 방식.
 

🔼 할 일 개수

  • todos의 길이을 todoCnt로 저장하여 Counter.js에 전달.
  • todoCnt는 일정을 추가하거나 삭제하는 메서드로 전달함.
  • Counter.js는 전달받은 todoCnt를 기반으로 규칙에 따라 innerHTML 로 요소를 그림.
  • toggle 시 todoCnt에 영향을 줘야 함 → completeCnt를 변수로 만들어 isComleted의 true 개수를 세고 전체 길이에서 빼줌.
let completeCnt = 0; [...this.state.todos].filter((n) => { if (n.isCompleted === true) { completeCnt++; } })
filter로 true일 때의 count를 측정.
 

✏️ 일정 추가, 토글, 수정, 삭제

추가

  • TodoForm에서 submit 이벤트가 발생할 때 addTodo 함수 실행.
      1. text를 파라미터로 전달하고
      1. createTodoItem 으로 id, isCompleted 등이 담긴 todo 배열을 만들고
      1. 이들을 합친 새로운 todo를 그림.
this.addTodo = (text) => { const newTodos = [...this.state.todos, createTodoItem(text)]; this.setState({ todos: newTodos }); counterComp.setState({ todoCount: newTodos.length }); };

토글

  • 토글 아이콘을 클릭하면 toggleTodo 함수 실행.
      1. id를 파라미터로 전달하여
      1. 해당 id의 isCompleted 값을 true로 바꾸고
      1. TodoList.js의 변경된 값으로 새로운 innerHTML을 구성함.
<li class="todo ${isCompleted ? "line" : ""}"> ${text} </li>
💡
- todolist가 todoItem을 관리하는 구조. - 메시지로 전달할 값이 뭐가 있는지를 먼저 생각하기. ex) 토글을 위해 완료됐다라는 정보가 필요하겠다! → isCompleted, 특정 todo를 가리키기 위해선 id 값도 필요하겠네!? - 상태라는 건 여러 개의 값을 가질 확률이 높음. 필요한 형식이 배열인지, 객체인지 판단하기. - 차례대로 생각하기. Counter는 배열의 길이랑 관련된거니 토글에서 생각할 단계가 아님. - app만의 state등 각자의 상태가 있음을 항상 기억하기.

수정

  • 수정 아이콘을 클릭하면 editTodo 함수 실행.
      1. id와 text를 파라미터로 전달하여
      1. id 값으로 일치하는 text를 전달받은 새로운 text로 교체하고
      1. 이를 반영한 새로운 todo를 그림.
this.editTodo = (id, text) => { const todo = this.state.todos.find((t) => t.id === id); todo.text = text; this.setState({ todos: [...this.state.todos] }); };
🤔
처음에 수정 아이콘을 클릭하면 토글 전에 li의 text 값을 토글 된 input value에 전달하려고 시도했으나, 토글되면서 값이 손실되는 문제 발생.

삭제

  • 삭제 아이콘을 클릭하면 deleteTodo 함수 실행.
      1. target을 파라미터로 전달하여
      1. todo 배열의 id와 전달받은 target의 id가 일치하지 않는 것들로 구성된 새로운 todo배열을 반환함. filter() 사용.
      1. 전달받은 target은 remove() 함수로 삭제.
this.deleteTodo = (target) => { const newTodos = [...this.state.todos].filter(function (todo) { return todo.id !== parseInt(target.dataset.id); }); target.remove(); this.setState({ todos: newTodos });
 

🗂️ 기록 저장하기

  • Storage 인터페이스는 특정 도메인을 위한 세션 저장소 또는 로컬 저장소의 접근 경로로서 데이터를 추가하고 수정하거나 삭제 가능.
  • 사용한 속성
    • Storage.setItem()
    • Storage.getItem()
  • localStorage는 js의 오브젝트를 저장할 수 없기 때문에 JSON.stringify() 을 사용해 string으로 변환해야 함.
⚠️
localStorage.removeItem()는 key에 속한 value값들도 전부 삭제가 되기 때문에 투두리스트와 같이 하나씩 삭제가 필요할 땐 적합하지 않음.
 

✉️ 컴포넌트끼리 협력하는 법

  1. App.js를 기반으로 다른 component를 그림.
  1. 각 component는 고유한 상태 값을 가지기 때문에 자체적으로 render도 가능함.
  1. ex) counter가 그리는 것은 counter가 책임 져야 함.
notion image
💡
component는 UI 조각을 의미하며 각자 가진 상태를 가지고 스스로 그림.
 

🛡️ type 유효성 검사

this.setState = (nextState) => { this.validationState(nextState); this.state = { ...nextState }; this.render(); }; this.validationState = (state) => { if (typeof state?.todoCount !== "number") { throw new Error("State must have a type of number"); } };
  • typeof를 이용하여 원하는 조건만 통과할 수 있도록 함.
  • JS는 타입 검사가 없기 때문에 일일히 해줘야 한다는 단점이 있음.
 

📌 간단한 QA

  • @media 를 사용하여 모바일 반응형 제작.
  • todo를 30자 이상 작성하면 반영하지 않고 알림을 띄움.
  • todo 개수를 화면에 벗어날만큼 추가할 경우 overflow-y:auto 로 스크롤바를 보여줌.
🤔
스크롤의 범위를 제한하기 위해 height 값을 강제하였는데 body 크기에 맞춰 콘텐츠 스크롤이 auto가 될 수 있도록 구조를 다시 짜봐야 함.