ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JavaScript] 투두리스트 만들기 - 생성한 아이템 저장하고 불러오기 (1)
    만학도 프로젝트/JavaScript TodoList 2024. 3. 1. 21:36

    지난 글에서 할 일을 생성하는 기능을 만들었었는데요.

    생성하는 기능이 잘 동작하긴 하는데, 페이지를 새로고침 하면 아이템들이 모두 사라져 버립니다.

    이번에는 localStorage를 활용해서 아이템을 생성할 때 그 정보를 저장해 두고,

    새로고침을 해서 페이지가 갱신될 때 localStorage에 저장된 정보를 가지고 와서 아이템들이 사라지지 않고 페이지가 갱신되기 직전의 모습을 유지할 수 있도록 해보려고 해요.

     

    리팩터링

    일단은 지난번에 작성했던 코드를 조금 수정해 보겠습니다.

    수정하는 이유는, 앞으로의 코드들이 읽기, 수정, 삭제 기능들이 붙으면서 점점 더 복잡해질 예정이기 때문에 조금 더 의미를 명확하게 구분할 수 있도록 각 역할에 맞는 이름을 붙여주기 위해서예요.

     

    일단은 handleFormSubmit 함수 안에서 과하다 싶을 정도(?)로 각각의 역할로 구분할 수 있는 로직들에 이름을 붙여주는 형태로 리팩터링 해보려고 합니다.

     

    저는 크게 3가지로 나눴는데요. 

    1. 첫 번째 템플릿 태그를 활용해서 가상의 .todo-item을 만드는 역할 (생성)
    2. 두 번째는 만들어진 .todo-item #todoList에 넣어주는 역할 (추가)
    3. 마지막은 .todo-item을 추가하고 나서 todoInput을 초기화해 주는 역할 (인풋 초기화)

    위 세 가지 역할을 각각 createTodoItem, addTodoItem, clearTodoInput으로 나눠줄게요.

    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 createTodoItem = (itemTitle) => {
      const todoItem = todoTemplate.content
        .cloneNode(true)
        .querySelector('.todo-item');
    
      todoItem.querySelector('.item-title').textContent = itemTitle;
    
      return todoItem;
    };
    
    // 추가
    const addTodoItem = (itemTitle) => {
      const todoItem = createTodoItem(itemTitle);
      
      todoList.appendChild(todoItem);
    };
    
    // 인풋 초기화
    const clearTodoInput = () => {
      todoInput.value = '';
    };
    
    const handleFormSubmit = (event) => {
      event.preventDefault();
    
      const inputValue = todoInput.value.trim();
    
      if (inputValue === '') return;
    
      addTodoItem(inputValue);
      clearTodoInput();
    };
    
    todoForm.addEventListener('submit', handleFormSubmit);

    생성은 추가와 같이 이뤄지는 거니깐, addTodoItem안에서 createTodoItem를 호출하도록 handleFormSubmit 함수를 수정했습니다.

     

    저장 로직 만들기

    이제 저장로직을 만들어 볼 텐데요.

    로컬스토리지를 통해서 저장 로직을 만들 때 가장 먼저 할 것은 불러오는 환경을 구축하기입니다. 

    왜냐하면 저장할 때 어디에 저장할지를 분명히 해야 하기 때문이에요.

     

    왠지 로컬스토리지를 전혀 모르시는 분들은 '어디냐니? 로컬스토리지에다 하는 거 아니야?' 하실 수도 있을 것 같은데,

    로컬스토리지는 객체처럼 key:value 쌍으로 값을 저장하고 불러올 수가 있는 구조로 되어있어요.

     

    그래서 어떤 키에다 값을 저장할지를 생각하고, 그 값을 불러오는 함수를 먼저 만들어 둘 거예요.

    그래야 그 키 값에 새로운 값을 덮어쓸 수가 있거든요.

     

    저는 여러 개의 .todo-item들의 정보를 저장해야 하니깐 배열의 형태로 아이템들을 다룰 거고, 그 배열을 'todos'라는 키로 관리해보려고 합니다.

    // ...
    
    const STORAGE_KEY = 'todos';
    
    const getTodos = () => {
      return JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
    };
    
    ...

    그래서 이렇게 로컬스토리지의 todos 값을 가져오는 getTodos함수를 만들어봤는데요.

     

    로컬스토리지의 getItem 메서드로 'todos'에 접근해서 값이 있으면 JSON으로 파싱 해서 리턴해주고, 없으면 빈배열을 리턴해주는 함수입니다.

     

    로컬스토리지의 키 값은 불러올 때도, 저장할 때도, 삭제할 때도 써야 하니깐 STORAGE_KEY로 따로 선언해 뒀어요.

     

    자, 그러면 이제는 진짜 저장하는 saveTodoItem함수를 아래와 같이 만들어 주겠습니다.

    // ...
    
    const STORAGE_KEY = 'todos';
    
    const getTodos = () => {
      return JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
    };
    
    const saveTodoItem = (todoItem) => {
      const todoData = {
        title: todoItem.querySelector('.item-title').textContent,
      };
      const todos = getTodos().concat(todoData);
      localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
    };
    
    // ...

    createTodoItem으로 만든 todoItem을 아규먼트로 saveTodoItem에 전달해 주면 다음과 같은 동작이 수행되는 건데요.

    1. title 값을 찾아서 todoData 객체를 완성합니다.
    2. 앞서 만든 getTodos를 활용해서 로컬스토리지에 저장된 값을 불러오고, 그 뒤에 todoData를 concat 메서드로 붙여서 todos 배열을 완성합니다.
    3. 새로운 아이템이 추가된 todos를 로컬스토리지에 저장합니다.

     

    여기서 제가 단순히 todoItem들의 title만 감싸는 배열을 만들지 않은 이유는, 투두 아이템에 체크 상태도 함께 관리하기 위해서예요. 다음에 체크상태를 관리할 때 todoData에 프로퍼티를 더 추가할 예정입니다. 

     

    자, 그럼 이제 이 저장 로직을 아이템이 추가될 때 잘 실행시켜주기만 하면 될 것 같은데요.

    addTodoItem 코드를 조금만 수정하고, handleFormSubmit 에서 실행하는 걸로 해보겠습니다.

    그러면 다음과 같은 코드가 완성이 됩니다.

    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 = {
        title: todoItem.querySelector('.item-title').textContent,
      };
      const todos = getTodos().concat(todoData);
      
      localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
    };
    
    const createTodoItem = (itemTitle) => {
      const todoItem = todoTemplate.content
        .cloneNode(true)
        .querySelector('.todo-item');
    
      todoItem.querySelector('.item-title').textContent = itemTitle;
    
      return todoItem;
    };
    
    const addTodoItem = (itemTitle) => {
      const todoItem = createTodoItem(itemTitle);
    
      todoList.appendChild(todoItem);
      return todoItem; // <- 여기서 생성한 아이템을 리턴!
    };
    
    const clearTodoInput = () => {
      todoInput.value = '';
    };
    
    const handleFormSubmit = (event) => {
      event.preventDefault();
    
      const inputValue = todoInput.value.trim();
    
      if (inputValue === '') return;
    
      saveTodoItem(addTodoItem(inputValue)); // <- 여기서 실행!
      clearTodoInput();
    };
    
    todoForm.addEventListener('submit', handleFormSubmit);

     

    이렇게 수정하면, addTodoItem 에서 추가된 아이템이 그대로 saveTodoItem 의 아규먼트로 전달되면서 화면에 아이템을 추가하고 저장하는 순서로 코드가 동작하겠죠?

     

    단순히 저장하나 넣었는데도 코드가 이렇게나 많아지다니.. 그래도 각 동작들에 이름 붙이기를 잘해둬서 크게 어지럽진 않은 것 같아요.

    아무튼 여기까지 코드를 실행해 보면, 투두 아이템이 새로 추가될 때마다 아이템들이 로컬스토리지에 잘 저장되는 걸 볼 수 있습니다!

     

    이제 저장은 잘 되는 것 같죠?

    급 마무리

    사실 이번 포스트에서 저장하고 불러오는 것까지 하려고 했는데, 생각보다 글 쓰는 게 시간이 너무 오래 걸리기도 했고 쓰고 나니 저장만 하는데도 양이 꽤 되는 것 같아서 불러오기는 다음에 이어서 작성해 보도록 할게요!

     

    아직도 새로고침 하면 다 사라지긴 하지만, 저장 기능은 완성이에요!

    다음에는 저렇게 로컬스토리지에 저장된 정보를 불러와서 아이템들을 그려주는 기능을 완성해 보도록 하겠습니다.

     

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

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

    감사합니다.

    댓글

Designed by BigTop.