
🚩 목표

🔎 기본 동작 원리
- 상태를 기반으로 그림.
- 상태는 1) 바꾼다, 2) 상태가 바뀔 때 그릴 때 관리함.
- 상태는 event, method를 통해 상태를 바꿀 타이밍을 정함.
- 여기서의 상태 주체는 component임.

🗓️ 날짜, 시간 구현하기
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>
🔼 할 일 개수
- 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++; } })
✏️ 일정 추가, 토글, 수정, 삭제
추가
- TodoForm에서 submit 이벤트가 발생할 때
addTodo
함수 실행. - text를 파라미터로 전달하고
-
createTodoItem
으로 id, isCompleted 등이 담긴 todo 배열을 만들고 - 이들을 합친 새로운 todo를 그림.
this.addTodo = (text) => { const newTodos = [...this.state.todos, createTodoItem(text)]; this.setState({ todos: newTodos }); counterComp.setState({ todoCount: newTodos.length }); };
토글
- 토글 아이콘을 클릭하면
toggleTodo
함수 실행. - id를 파라미터로 전달하여
- 해당 id의 isCompleted 값을 true로 바꾸고
- TodoList.js의 변경된 값으로 새로운 innerHTML을 구성함.
<li class="todo ${isCompleted ? "line" : ""}"> ${text} </li>
- todolist가 todoItem을 관리하는 구조.
- 메시지로 전달할 값이 뭐가 있는지를 먼저 생각하기.
ex) 토글을 위해 완료됐다라는 정보가 필요하겠다! → isCompleted, 특정 todo를 가리키기 위해선 id 값도 필요하겠네!?
- 상태라는 건 여러 개의 값을 가질 확률이 높음. 필요한 형식이 배열인지, 객체인지 판단하기.
- 차례대로 생각하기. Counter는 배열의 길이랑 관련된거니 토글에서 생각할 단계가 아님.
- app만의 state등 각자의 상태가 있음을 항상 기억하기.
수정
- 수정 아이콘을 클릭하면
editTodo
함수 실행. - id와 text를 파라미터로 전달하여
- id 값으로 일치하는 text를 전달받은 새로운 text로 교체하고
- 이를 반영한 새로운 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
함수 실행. - target을 파라미터로 전달하여
- todo 배열의 id와 전달받은 target의 id가 일치하지 않는 것들로 구성된 새로운 todo배열을 반환함.
filter()
사용. - 전달받은 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값들도 전부 삭제가 되기 때문에 투두리스트와 같이 하나씩 삭제가 필요할 땐 적합하지 않음.
✉️ 컴포넌트끼리 협력하는 법
- App.js를 기반으로 다른 component를 그림.
- 각 component는 고유한 상태 값을 가지기 때문에 자체적으로 render도 가능함.
- ex) counter가 그리는 것은 counter가 책임 져야 함.

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가 될 수 있도록 구조를 다시 짜봐야 함.