과제 설명
요구사항
- 변수명을 최대한 명확히 작성, 함수도 최소한의 단위로 나누어서 사용
- 페이지를 새로고침해도 데이터가 지속될 수 있도록 하기
↪ localStorage 사용
제공 예시 이미지

HTML
<head>
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
</head>
JS
addEventListener vs onclick
addEventListener는 동일한 이벤트에 대해 여러 개의 리스너를 추가할 수 있어서 같은 버튼에 여러 개의 클릭 이벤트를 설정할 수 있는 반면, onclick은 해당 이벤트에 대해 한 개의 함수만 설정할 수 있어서 새로운 함수를 할당하면 이전의 함수는 덮어쓰여짐
↪ 유연성을 위해 addEventListener가 더 권장되는 방식임
- 그러나 함수가 하나일 때는 다음 두 표현은 같은 결과를 가져옴
버튼.addEventListener('click', 함수) == 버튼.onclick=function(){...}
- 브라우저의
localStorage 에 데이터 저장

const list = document.getElementById("list");
const createBtn = document.getElementById("create-btn");
let todos = [];
createBtn.addEventListener('click', createNewTodo);
function createNewTodo() {
const item = {
id: new Date().getTime(),
text: "",
complete: false
}
todos.unshift(item);
const { itemEl, inputEl } = createTodoElement(item);
list.prepend(itemEl);
inputEl.removeAttribute("disabled");
inputEl.focus();
saveToLocalStorage();
}
function createTodoElement(item) {
const itemEl = document.createElement("div");
itemEl.classList.add("item");
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.checked = item.complete;
if (item.complete) {
itemEl.classList.add("complete");
}
const inputEl = document.createElement("input");
inputEl.type = "text";
inputEl.value = item.text;
inputEl.setAttribute("disabled", "");
const actionsEl = document.createElement("div");
actionsEl.classList.add("actions");
const editBtnEl = document.createElement("button");
editBtnEl.classList.add("material-icons");
editBtnEl.innerText = "edit";
const removeBtnEl = document.createElement("button");
removeBtnEl.classList.add("material-icons", "remove-btn");
removeBtnEl.innerText = "remove_circle";
actionsEl.append(editBtnEl);
actionsEl.append(removeBtnEl);
itemEl.append(checkbox);
itemEl.append(inputEl);
itemEl.append(actionsEl);
checkbox.addEventListener("change", () => {
item.complete = checkbox.checked;
if (item.complete) {
itemEl.classList.add("complete");
} else {
itemEl.classList.remove("complete");
}
saveToLocalStorage();
});
inputEl.addEventListener("input", () => {
item.text = inputEl.value;
});
inputEl.addEventListener("blur", () => {
inputEl.setAttribute("disabled", "");
saveToLocalStorage();
});
editBtnEl.addEventListener("click", () => {
inputEl.removeAttribute("disabled");
inputEl.focus();
});
removeBtnEl.addEventListener("click", () => {
todos = todos.filter(t => t.id != item.id);
itemEl.remove();
saveToLocalStorage();
});
return { itemEl, inputEl, editBtnEl, removeBtnEl }
}
function displayTodos() {
loadFromLocalStorage();
for (let i = 0; i < todos.length; i++) {
const item = todos[i];
const { itemEl } = createTodoElement(item);
list.append(itemEl);
}
}
displayTodos();
function saveToLocalStorage() {
const data = JSON.stringify(todos);
localStorage.setItem("my_todos", data);
}
function loadFromLocalStorage() {
const data = localStorage.getItem("my_todos");
if (data) {
todos = JSON.parse(data);
}
}