-
[JavaScript] 투두리스트 만들기 - 생성한 아이템 업데이트하기 (2): 완료 상태 업데이트 기능만학도 프로젝트/JavaScript TodoList 2026. 3. 21. 22:17

지난 글에서는 실제로 업데이트하기보다는 업데이트 준비를 했었는데요.
이번엔 정말로 데이터 업데이트 로직을 작성해 봅시다.
지난 글에서 로컬스토리지에 저장할 때 수정했던 데이터 구조 기억하시나요?
타이틀만 저장하던 객체에서 id와 isCompleted를 추가하는 작업까지 마쳤었는데요.
이 두 데이터 속성을 활용해서 체크박스 완료 상태 업데이트하는 기능을 만들어봅시다.
체크박스(input type="checkbox")의 특징 알고 가기
본격적으로 기능을 만들기 전에 사실 지금까지 작성한 투두 리스트 화면을 보면
(이미 해보셨을 수도 있겠지만,) 새로 추가한 할 일의 체크박스를 클릭했을 때 딱히 이벤트를 등록하지 않았는데도 UI에서 체크 표시가 생겼다 사라졌다 하는 걸 확인할 수 있습니다.
이건 체크박스 타입을 가진 인풋 태그의 고유한 특징인데요.
별다른 자바스크립트 코드 없이도 스스로 클릭을 감지하고 상태를 변경하는 능력을 가지고 있는 거죠.
감사하게도 이 덕분에 우리가 자바스크립트로 해야 할 일은 더 간단한데요.
자바스크립트로 클릭했을 때 체크박스 상태를 변하도록 하는 코드 보다,
체크박스가 스스로 상태를 바꿀 때 그 상태를 감지해서 우리의 로컬스토리지 데이터에 저장하는 코드를 만들어야 해요.
이벤트 위임(Event Delegation)으로 변화 감지하기
그럼 이제 체크박스의 상태가 변하는 걸 자바스크립트가 감지하도록 해야 하는데요.
여기서 한 가지 고민이 생기게 됩니다.
감지하는 그 이벤트를 어디에 붙여줄 것인가.
가장 직관적으로는 각 아이템에 이벤트를 붙여주는 아이디어가 있습니다.
다만, 그렇게 하면 아이템을 생성할 때마다 이벤트를 등록해 주기 때문에 아이템을 생성할 때 필요한 동작이 하나 더 늘어나게 됩니다.
아이템이 100개라면 이벤트 리스너가 100개가 생기게 되겠죠?
이 방법이 절대 나쁘다거나 요즘은 성능상에 크게 문제가 있는 방법도 아니긴 하지만, 그래도 이것 보다 조금 더 괜찮은 방식으로 코드를 작성해보려고 하는데요.
감지 이벤트를 각각의 아이템에 아니라 리스트에 붙이는 방법이에요.
아이템들의 부모인 ul 태그에 todo-list라는 id를 달고 todoList라는 변수에 담아뒀었죠?
todoList에 이벤트를 달아두면 자식 요소들에게서 일어나는 이벤트를 확인할 수가 있는데 이 방식을 활용할 거예요.
이 방식의 장점은 아이템이 100개 1,000개가 되더라도 리스트에 붙은 이벤트 리스너 1개로 모든 처리가 가능하다는 거예요.
혹시 좀 더 궁금하시다면 "이벤트 위임"이라는 키워드로 한번 검색해 보시는 걸 추천드려요! 정말 모르겠다 하시면 '이벤트 버블링'이라는 개념도 같이 공부하시길 권장드립니다.
(언제가 될진 모르지만 나중에 저도 자바스크립트 이벤트와 관련해서 개념 정리글도 작성해 볼게요)
자 그럼 코드를 조금 작성해 봅시다!
// 리스트 내부에서 일어나는 상태 변화 감지 (이벤트 위임) todoList.addEventListener('change', (event) => { // 이벤트가 발생한 대상이 체크박스인지 확인 if (event.target.classList.contains('item-checkbox')) { const checkbox = event.target; // 클릭된 체크박스가 속한 최상위 todo-item(li 태그) 찾기 const todoItem = checkbox.closest('.todo-item'); // 아이템의 id 가져오기 const itemId = todoItem.getAttribute('id'); // 업데이트 된 현재 체크박스의 상태 (true/false) const isChecked = checkbox.checked; // ... 이제 이 데이터들을 활용해서 로컬스토리지를 업데이트해봅시다. } });위에서부터 주석을 기준으로 코드를 하나씩 보면
- // 리스트 내부에서 일어나는 상태 변화 감지 (이벤트 위임):
체크박스에서 상태가 변하면 change이벤트가 발생하기 때문에 todoList에 change이벤트리스너를 만들어준 거예요. - // 이벤트가 발생한 대상이 체크박스인지 확인:
이벤트가 일어난 대상이 체크박스가 맞는지를 확인하고 체크박스 일 때만 로직을 실행하도록 조건문을 만든 거예요. - // 클릭된 체크박스가 속한 최상위 todo-item(li 태그) 찾기:
closest라는 메서드를 활용해서 체크박스를 감싸고 있는 가장 가까운 item을 찾는 거예요. - // 아이템의 id 가져오기:
로컬스토리지의 데이터를 업데이트하려면 id를 가지고 와야겠죠? 업데이트를 위한 id를 찾는 과정입니다. - // 업데이트된 현재 체크박스의 상태 (true/false):
체크박스가 체크가 된 상태라면 true, 빈칸이라면 false값을 가지게 됩니다. 이 데이터를 로컬스토리지에 업데이트하는 거죠.
로컬스토리지 데이터 업데이트하기
이제 어떤 아이템의 체크박스가 눌렸는지도 알아냈고 체크박스의 상태도 알아낸 겁니다.
이걸 로컬 스토리지에 있는 데이터에 업데이트만 하면 되겠죠?
todoList.addEventListener('change', (event) => { if (event.target.classList.contains('item-checkbox')) { const checkbox = event.target; const todoItem = checkbox.closest('.todo-item'); const itemId = todoItem.getAttribute('id'); const isChecked = checkbox.checked; // 기존 데이터 불러오기 const todos = getTodos(); // 내가 클릭한 아이템이 배열에서 몇 번째(index) 방에 있는지 찾기 const targetIndex = todos.findIndex(todo => todo.id === itemId); // 만약 제대로 찾았다면 (-1이 아니라면) if (targetIndex !== -1) { // 그 방에 있는 객체의 isCompleted 속성만 콕 집어서 isChecked 상태로 덮어쓰기! todos[targetIndex].isCompleted = isChecked; // 상태가 수정된 기존 배열을 그대로 다시 로컬스토리지에 밀어넣기 localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)); } } });이번에도 주석을 기준으로 간단하게 설명해 보자면
- // 기존 데이터 불러오기:
로컬스토리지에 있는 원본 배열을 가져옵니다. - // 내가 클릭한 아이템이 배열에서 몇 번째(index) 방에 있는지 찾기:
업데이트하고자 하는 아이템이 배열의 어디에 있는지를 확인하는 과정입니다. - // 만약 제대로 찾았다면 (-1이 아니라면):
findIndex메서드는 index를 찾았다면 index를, 못 찾았다면 -1을 리턴하는데요. 이 특성을 활용했어요. - // 그 방에 있는 객체의 isCompleted 속성만 콕 집어서 isChecked 상태로 덮어쓰기!:
로컬스토리지에 있는 원본 배열에서 업데이트할 아이템의 상태를 업데이트해 주는 코드예요 - // 상태가 수정된 기존 배열을 그대로 다시 로컬스토리지에 밀어 넣기:
todos배열이 업데이트되긴 했지만 로컬스토리지에도 업데이트를 해줘야 업데이트가 완전히 끝이 나요!
마무리
여기까지 js 코드를 한 번에 정리하면 다음과 같습니다.
const todoForm = document.getElementById('todo-form'); const todoInput = document.getElementById('todo-input'); const todoList = document.getElementById('todo-list'); const todoTemplate = document.getElementById('todo-template'); const STORAGE_KEY = 'todos'; const getTodos = () => { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || []; }; const saveTodoItem = (todoItem) => { const todoData = { id: todoItem.getAttribute('id'), title: todoItem.querySelector('.item-title').textContent, isCompleted: todoItem.querySelector('.item-checkbox').checked }; const todos = getTodos().concat(todoData); localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)); }; const createTodoItem = (todo) => { const todoItem = todoTemplate.content .cloneNode(true) .querySelector('.todo-item'); todoItem.querySelector('.item-title').textContent = todo.title; todoItem.querySelector('.item-checkbox').checked = todo.isCompleted; todoItem.setAttribute('id', todo.id); return todoItem; }; const addTodoItem = (todo) => { const todoItem = createTodoItem(todo); todoList.appendChild(todoItem); return todoItem; }; const loadTodoItem = () => { getTodos().forEach((todo) => { addTodoItem(todo) }); }; const clearTodoInput = () => { todoInput.value = ''; }; const handleFormSubmit = (event) => { event.preventDefault(); const inputValue = todoInput.value.trim(); if (inputValue === '') return; const newTodo = { id: Date.now(), title: inputValue, isCompleted: false, }; saveTodoItem(addTodoItem(newTodo)); clearTodoInput(); }; todoList.addEventListener('change', (event) => { if (event.target.classList.contains('item-checkbox')) { const checkbox = event.target; const todoItem = checkbox.closest('.todo-item'); const itemId = todoItem.getAttribute('id'); const isChecked = checkbox.checked; const todos = getTodos(); const targetIndex = todos.findIndex(todo => todo.id === itemId); if (targetIndex !== -1) { todos[targetIndex].isCompleted = isChecked; localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)); } } }); const initTodoApp = () => { todoForm.addEventListener('submit', handleFormSubmit); loadTodoItem(); clearTodoInput(); }; document.addEventListener('DOMContentLoaded', initTodoApp);이벤트를 등록하는 부분만 생긴거라 사실상 큰 코드변화는 없죠?
이제 코드를 실행하고 브라우저로 들어가서 체크박스를 눌러볼까요? 그리고 새로고침도 한번 해보세요!

투두 아이템의 상태가 로컬스토리지와 잘 연동되어 있고, 새 로고침해도 상태가 그대로 유지되어 있는 모습을 확인하실 수 있을 거예요!
이번에 제 블로그에 정리되지 않은 이벤트 위임이나 findIndex 메서드 같은 개념이 등장하는 바람에 조금 내용이 어색하게 느껴질 수도 있을 것 같은데요.
혹시 조금 개념이 너무 생소하셨다면 해당 키워드로 꼭 개념을 익혀보시는 걸 권장드려요.
아무튼, 여기까지 할 일 완료 상태 업데이트 기능을 구현해 봤는데요.
다음 글에서는 아이템 오른쪽에 붙은 Edit 버튼! 저 edit 버튼을 통해서 타이틀을 수정하는 기능을 완성해 봅시다.
질문이나, 의문점이 드는 부분이라거나 제가 실수를 했다거나, 혹은 좀 더 좋은 방향이 있다면 댓글 부탁드립니다.더 좋은 글을 쓰고 제가 성장하는 데에도 큰 도움이 될 거예요! :)
감사합니다.
'만학도 프로젝트 > JavaScript TodoList' 카테고리의 다른 글
[JavaScript] 투두리스트 만들기 - 생성한 아이템 업데이트하기 (1): 데이터 구조 수정하기 (0) 2026.02.16 [JavaScript] 투두리스트 만들기 - 생성한 아이템 저장하고 불러오기 (2) (2) 2024.03.24 [JavaScript] 투두리스트 만들기 - 생성한 아이템 저장하고 불러오기 (1) (0) 2024.03.01 [JavaScript] 투두리스트 만들기 - 할 일 생성 기능 (2) 2024.02.24 [JavaScript] 투두리스트 만들기 - HTML 뼈대 잡기 (0) 2024.02.18 - // 리스트 내부에서 일어나는 상태 변화 감지 (이벤트 위임):