ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JavaScript] 투두리스트 만들기 - 생성한 아이템 업데이트하기 (1)
    만학도 프로젝트/JavaScript TodoList 2026. 2. 16. 23:45

     

    업데이트 기능 준비

    지난 글에서 로컬스토리지에 할 일들을 생성하고 불러오는 기능까지 완료했었습니다.

    지금까지 완성한 코드를 실행해 보면 아이템들이 잘 생성되기도 하고 로컬스토리지에 저장도 되고, 페이지를 새로고침 해도 그대로 잘 남아있습니다.

    그런데 아직 업데이트 기능이 없어서, 아이템 앞에 있는 체크박스를 클릭해도 새로고침 하면 금방 사라져 버리고, edit 버튼을 클릭해도 아무 일도 일어나질 않죠?

     

    그래서 이번에는 지난 시간에 예고했던 대로 CRUD 중에서 U, 아이템을 업데이트하는 기능을 만들어보려고 하는데요.

     

    todo list에서 업데이트 기능은 방금 말한 것처럼 크게 두 가지가 있죠?

     

    첫 번째는 완료 상태를 구분하는 체크박스 기능

    두 번째는 우리가 그동안 작성해온 아이템의 타이틀을 수정하는 기능입니다.

     

    그런데 그 전에 한 가지 중요한 작업이 필요합니다.

     

    데이터 구조 수정하기

    업데이트를 위해 가장 먼저 해야 할 일은 우리의 데이터 구조를 수정하는 일입니다.

    지금 기능에서 "블로그 글쓰기"와 "운동하기" 아이템을 생성했다면 로컬스토리지에 쌓인 데이터 구조는 다음과 같을 것입니다.

    [{"title":"블로그 글쓰기"},{"title":"운동하기"}]

     

    당장은 타이틀 데이터만 필요했기 때문에 이걸로도 충분했지만, 업데이트는 상황이 조금 다릅니다.

    체크박스를 예로 들어보죠.

     

    만약 내가 오늘 운동하기를 완료했다면, title"운동하기"인 아이템을 찾아서 업데이트해야 합니다.

    그런데 만약 운동하기 아이템이 두 개라면 어떤 아이템의 상태를 완료로 처리하게 될까요?

     

    물론 하루에 운동을 두 번 하는 사람이 흔하진 않지만 어찌 됐든 지금 구조로는 타이틀이 중복된 경우에 업데이트해야 할 대상을 제대로 찾지 못해 의도치 않은 버그가 발생할 가능성이 있습니다.

     

    그래서 업데이트를 하기 전에, 아이템을 생성할 때 해당 아이템의 고유번호를 추가하려고 하는데요.

    고유번호와 함께, 체크 상태를 관리할 속성도 함께 추가해 봅시다.

    고유번호를 id, 체크 상태를 isCompleted로 해볼게요.

     

    최종적으로 우리 할 일 아이템의 데이터 구조가 다음과 같이 되도록 해볼 겁니다.

    [
      { 
        id: 1234123412345,   // [NEW] 고유 식별자 (생성 날짜)
        title: "블로그 쓰기",
        isCompleted: false   // [NEW] 완료 여부 (Boolean 타입)
      },
      { 
        id: 1234123412345,   
        title: "운동하기",
        isCompleted: false
      }
    ]

     

    함수 수정하기

    데이터 구조는 우리가 생성할 때 만들어 봅시다.

    처음부터 구조를 잡아둬야 데이터 흐름이 생성 로직에 일관되게 흐를 수 있을 것 같아요.

    그럼 handleFormSubmit부터 수정해야 합니다.

    (나중에 이 데이터를 만드는 역할이 꼭 여기일 필요가 있는지는 마지막에 모든 기능을 만들고 나서 한번 더 점검해 보도록 해요!)

    const handleFormSubmit = (event) => {
      event.preventDefault();
    
      const inputValue = todoInput.value.trim();
    
      if (inputValue === '') return;
    
      // title만 추가하던 데이터를 이곳에서 미리 가공해서 전달해줍시다.
      const newTodo = {
        id: Date.now(),
        title: inputValue,
        isCompleted: false,
      };
    
      saveTodoItem(addTodoItem(newTodo)); // addTodoItem에 타이틀이 아닌 데이터 객체를 넘겨줍니다.
      clearTodoInput();
    };

    기존에 타이틀만 가지고서 todo 데이터를 관리하던 방식에서 이젠 필요한 데이터로 구조화된 todo 객체를 가지고 이후 로직들이 동작하게끔 해야 합니다.

     

    addTodoItem에 타이틀이 아닌 객체를 전달해 주는 형태로 코드가 변경됐으니, addTodoItem도 뭔가 수정을 해줘야겠죠?

    const addTodoItem = (todo) => { //itemTitle을 todo로 변경
      const todoItem = createTodoItem(todo); // itemTitle을 todo로 변경
      todoList.appendChild(todoItem);
      return todoItem;
    };

    근데 addTodoItem은 기존에도 파라미터 값을 받아서 뭔가를 하기보다는 사실상 화면에 todoItem을 그려주는 역할이 메인이었고, 파라미터로 전달받은 값은 그대로 createTodoItem에 전달만 해주고 있었으니, 사실상 파라미터 이름만 변경하면 됐었네요!

    이러면 createTodoItem 쪽에 로직 변경이 좀 더 필요하겠죠?

    const createTodoItem = (todo) => { // itemTitle을 todo로 변경
      const todoItem = todoTemplate.content
        .cloneNode(true)
        .querySelector('.todo-item');
    
      todoItem.querySelector('.item-title').textContent = todo.title; // todo에서 title 추출
      todoItem.querySelector('.item-checkbox').checked = todo.isCompleted; // isCompleted 상태를 checkbox 요소에 반영
      todoItem.setAttribute('id', todo.id); // todo의 id 값을 요소의 id값으로 반영
    
      return todoItem;
    };

    이렇게 todo 객체의 각 속성들을 꺼내서 HTML요소에도 심어줘야 타이틀을 다룰 때처럼 이후에 로직들도 여기에 맞춰 대응할 수 있게 됩니다.

     

    그리고 마지막으로 saveTodoItem 로직을 수정해야 해요.

    const saveTodoItem = (todoItem) => {
      const todoData = {
        id: todoItem.getAttribute('id'), // id값 추가
        title: todoItem.querySelector('.item-title').textContent,
        isCompleted: todoItem.querySelector('.item-checkbox').checked // isCompleted 추가
      };
      const todos = getTodos().concat(todoData);
    
      localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
    };

    방금 전에 todoItem에 각 todo 객체의 속성 값들을 심어줬으니, 여기서 잘 꺼내서 todoData에 반영해 줘야 로컬스토리지에 잘 저장되겠죠?

     

    자, 이제 데이터 구조 변화는 잘 마무리된 것 같습니다.

    생각보다 바꿔야 될 게 많았죠? 진작에 잘 고민해서 설계했다면 좋았을걸 싶긴 하지만,

    이것도 다 배우고 성장해 나가는 과정 아니겠습니까..ㅎㅎㅎㅎㅎ

     

    마무리

    여기까지 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();
    };
    
    const initTodoApp = () => {
      todoForm.addEventListener('submit', handleFormSubmit);
      loadTodoItem();
      clearTodoInput();
    };
    
    document.addEventListener('DOMContentLoaded', initTodoApp);

    그리고 로컬호스트를 한번 다 비우고 (기존 데이터 삭제를 위해)

    이 코드를 실행해서 블로그 글쓰기와 운동하기를 추가해 보면,

    이제 기존에 저장된 데이터들이 처음에 의도했던 모양으로 잘 저장되는 모습을 확인할 수가 있습니다.

     

    여기까지 데이터 구조 수정하기는 잘 마무리된 것 같아요.

    다음에는 본격적으로 업데이트 기능을 만들어 보도록 하겠습니다.

    체크 박스부터 시작해 보죠!

     

    질문이나, 의문점이 드는 부분이라거나 제가 실수를 했다거나, 혹은 좀 더 좋은 방향이 있다면 댓글 부탁드립니다.

    더 좋은 글을 쓰고 제가 성장하는 데에도 큰 도움이 될 거예요! :)

    감사합니다.

    댓글

Designed by BigTop.