본문 바로가기
Front-end/Vanilla JS

Vanilla JS - 지역저장소를 이용해서 toDoList 만들기(1)

by devraphy 2020. 10. 12.

1. 복기

- 지난 시간에 간단하게 지역저장소(Local Storage)에 대해서 알아보았다.

- 지역저장소란, 브라우저가 제공하는 일종의 DB이며, JS를 이용해 접근 및 통제하여 브라우저를 운영하는데 필요한 정보를 사용자 측에 저장하는 것을 의미한다. 

 

- 이번 포스팅에서는 지역저장소에 정보를 저장하고 저장된 정보를 활용하는 방법을 다루려고한다. 

- 이를 위해, 저장 및 삭제 기능을 갖는 간단한 toDoList를 만들어보자. 

 


 2. 기본코드

- 지난 포스팅에서 사용했던 코드를 그대로 이어서 사용해보자. 

- 추가된 사항은 다음과 같다.

  • toDoList를 만들기 위해, 입력란이 필요하다. → input태그 
  • input태그에 입력된 정보를 활용해야 한다. → form태그
  • toDoList는 여러개의 입력을 받을 수 있어야 한다. → ul태그
  • 사용자가 할일(toDo)을 입력하면 이를 리스트로 출력해야 한다. → li태그 

 

[HTML 코드]

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vanilla JS</title>
    <link rel="stylesheet" href="index.css" />
  </head>
  <body>
    <div class="js-clock">
      <h1>00:00</h1>
    </div>
    <form class="js-form form">
      <input type="text" placeholder="What is your name?"/>
    </form>
    <h4 class="js-greetings greetings"></h4>
    <form class="js-toDoForm">
      <input type="text" placeholder="Write what you need to do."/>
    </form>
    <ul class="js-toDoList"></ul>
    
    <!-- Script 태그는 Body태그의 가장 마지막에 위치한다. -->
    <script src="index.js"></script>
    <script src="clock.js"></script>
    <script src="greeting.js"></script>
    <script src="todo.js"></script>
  </body>
</html>

- 앞으로 작성할 JS코드는 todo.js라는 파일에 작성할 예정이다. 

 

 

[출력화면]

input태그만 생성해도 전혀 무관하다.

 


3. 지역저장소 활용 전에 필요한 기능 구현 

- 지역저장소를 활용하기에 앞서, input태그에 입력된 내용이 출력되도록 만들자. 

- 구현할 내용의 순서는 다음과 같다. 

 

1) querySelector를 이용해 정보를 가져오거나 사용할 HTML 엘리먼트를 객체화 

const toDoForm = document.querySelector(".js-toDoForm");
const toDoInput = toDoForm.querySelector("input");
const toDoList = document.querySelector(".js-toDoList");

 

 

2) input에 입력(submit)에 반응하는 eventHandler

- input태그에서 submit 동작이 실행되면 해당 정보가 다른곳으로 전송된다.

- 현재 지역저장소를 사용하고 있지 않기때문에, submit기능이 작동되면 웹페이지가 새로고침되므로 

- submit기능이 작동되지 않도록 한다. 

function handleSubmit(event) {
    event.preventDefault(); // 엔터를 눌러도 submit이 작동하지 않도록 기본 기능을 정지시킴
    const currentValue = toDoInput.value;
    paintToDo(currentValue);
    toDoInput.value = "";
}

 

 

3) input태그에서 입력된 값을 HTML에서 li태그를 이용해 출력하는 함수

- ul 태그 아래에 자식태그 형태로 li태그가 삽입되어야 한다. 

 

function paintToDo(text) {
    const li = document.createElement("li"); // li 생성(엘리먼트를 생성시키는 함수 )
    const delBtn = document.createElement("button"); //버튼 생성 
    const span = document.createElement("span");
    
    delBtn.innerText = "❌";
    span.innerText = text;

    li.appendChild(span); // li 태그 내부에 자식태그를 생성하는 함수 
    li.appendChild(delBtn);
    toDoList.appendChild(li);
}

 

 

[여기까지 전체코드]

const toDoForm = document.querySelector(".js-toDoForm");
const toDoInput = toDoForm.querySelector("input");
const toDoList = document.querySelector(".js-toDoList");

function paintToDo(text) {
    const li = document.createElement("li"); // li 생성(엘리먼트를 생성시키는 함수 )
    const delBtn = document.createElement("button"); //버튼 생성 
    const span = document.createElement("span");
    
    delBtn.innerText = "❌";
    span.innerText = text;

    li.appendChild(span); // li 태그 내부에 자식태그를 생성하는 함수 
    li.appendChild(delBtn);
    toDoList.appendChild(li);
}

function handleSubmit(event) {
    event.preventDefault(); // 엔터를 눌러도 submit이 작동하지 않도록 기본 기능을 정지시킴
    const currentValue = toDoInput.value;
    paintToDo(currentValue);
    toDoInput.value = "";
}

function init() {
    //loadToDos();
    toDoForm.addEventListener("submit", handleSubmit);
}

init();

 

 

[출력화면]

li태그가 ul태그안에 있는 것을 확인할 수 있다.

 


4. 지역저장소를 활용한 기능 구현 

- 이제 기본적인 틀은 갖추어졌다. 그러므로, 지역저장소를 활용하여 input태그에서 입력된 정보들을 활용해보자.

 

1) 지역저장소에서 사용할 key값을 설정 및 지역저장소에서 해당 key값을 가져오는 함수

const TODOS_LS = 'toDos'; // 지역저장소에 저장될 value에 대응하는 key값을 설정 

function loadToDos() { // 지역저장소에 저장된 key값의 value를 불러오는 function 
    const loadedToDos = localStorage.getItem(TODOS_LS);
}

function init() {
    loadToDos(); // 함수를 만든 후, 반드시 호출해야 한다. 
    toDoForm.addEventListener("submit", handleSubmit);
}

init();
  • 여기까지 지역저장소를 사용할 준비가 완료되었다. 
  • input태그에 입력된 값들이 li태그로 잘 출력되는 것을 확인하였고, 지역저장소에서 사용할 key값 설정 및 해당 key값을 불러오는 기능까지 완성하였다.
  • 이제 남은 기능은 input태그에서 입력된 정보들을 지역저장소에 저장하는 삽입기능과 원하는 정보를 삭제하는 기능만이 남았다.
  • 여기서 몇가지 고려사항이 있다. 저장할 데이터(할 일)는 여러개인데 사용하는 key는 1개라는 것이다.
    - 사용자가 몇개의 할일을 기록할지 모르기때문에 key값을 미리 만들어 놓는 것은 공간복잡도의 관점에서 비효율적이다.
    - 그러나 key값을 1개만 사용하면 사용자가 특정 데이터(할 일)을 삭제할 때, 여러개의 li태그 중 해당 데이터를 담고 있는 li태그를 어떻게 구분할 것인가에 대한 문제도 발생한다. 어떻게 해결할 수 있을까? 
  • 배열과 객체를 사용하여 이 문제를 해결할 수 있다. 

 

2) 지역저장소를 활용한 할일(toDo) 데이터 저장 및 삭제기능 

- 배열을 이용하여 1개의 key가 여러개의 value를 저장할 수 있도록 한다. 

- 데이터(할 일)을 삭제할 때, 데이터간의 구분을 위해 li태그에 id값을 설정한다. 

- 이를 종합하여, value에는 사용자가 입력한 내용(text)과 해당 내용을 구분할 수 있는 id값을 부여한다. 

const toDos = []; // toDo를 저장할 리스트 생성 

function saveToDos() { // 배열(toDos)을 지역저장소에 저장하는 함수
    localStorage.setItem(TODOS_LS, toDos);
}

function paintToDo(text) {
    const li = document.createElement("li"); // li 생성(엘리먼트를 생성시키는 함수 )
    const delBtn = document.createElement("button"); //버튼 생성 
    const span = document.createElement("span");
    const newId = toDos.length + 1;
    
    delBtn.innerText = "❌";
    span.innerText = text;

    li.appendChild(span); // li 태그 내부에 자식태그를 생성하는 함수 
    li.appendChild(delBtn);
    toDoList.appendChild(li);
    li.id = newId; // li태그를 삭제할 때, 어떤 li태그를 삭제할 것인지 구분하기 위해 id속성값을 부여 

    const toDoObj = { // toDos 배열안에 넣을 정보를 setup 
        text: text,
        id: newId
    };
    toDos.push(toDoObj); // setup된 정보를 배열에 push 
    saveToDos(); // toDos를 지역저장소에 저장하는 함수를 호출 
}

 

* 지역저장소에는 key와 value를 한 쌍의 형태로 데이터를 저장한다. 그러나 value는 string 외의 다른 자료형을 가질수 없다. saveToDos() 함수를 보면 toDoObj라는 객체를 지역저장소에 저장한 것을 확인할 수 있다. 만약 이대로 사용한다면 어떻게 출력될까?

 

 

[출력화면]

- 아래 사진을 보면 toDos에는 입력된 데이터가 Object형태로 잘 저장된 것을 확인할 수 있다. 

- 그러나 지역저장소에는 [object Object] 라는 이름으로 3개의 데이터가 저장되어 있는 것을 볼 수 있다. 

- 이렇게 저장되면 지역저장소에서 해당 value를 불러와서 다시 사용할 수가 없다. 어떻게 해결할까? 

 

 

 

3) JSON을 이용한 Object(객체) 형변환

- JSON(JavaScript Object Notation)이란, 자바스크립트를 이용하여 Object(객체)를 다룰 수 있도록 하는 기술이다. 

- JSON을 이용하면 Object를 지역저장소에 저장할 수 있는 string(문자열)형태로 형변환 할 수 있으며, 반대로 string을 Object로 복원할 수도 있다. 

- 그러므로 해당 Object(객체)를 저장할 때는 string으로 형변환하여 저장하고, 저장된 value를 불러올 때는 다시 Object로 형변환 하는 코드를 작성해야 한다.

function saveToDos() { // 저장할 때, object를 string으로 형변환 
    // JSON.stringify()를 이용하면 object를 string으로 형변환할 수 있다. 
    localStorage.setItem(TODOS_LS, JSON.stringify(toDos));
}

function loadToDos() {
    const loadedToDos = localStorage.getItem(TODOS_LS);
    if (loadedToDos !== null) {
        const parsedToDos = JSON.parse(loadedToDos);
        // 스트링으로 바뀌어 저장되어있는 value를 불러와서 사용해야 하므로
        // JSON을 활용하여 string을 다시 object로 형변환 시켜준다. 
    }
}

 

 

4) forEach를 사용하여 리스트의 요소를 출력하기 

- 위의 코드에서 toDos라는 이름의 리스트를 사용하여 여러개의 데이터를 저장한 것을 확인할 수 있다. 

- 이 리스트에 저장된 각각의 데이터를 추출해야만 온전히 사용할 수 있으므로 그 과정을 코드로 구현한다. 

function loadToDos() {
    const loadedToDos = localStorage.getItem(TODOS_LS);
    if (loadedToDos !== null) {
        const parsedToDos = JSON.parse(loadedToDos);
        
        parsedToDos.forEach(function (toDo) {
            // parsedToDos에 들어있는 각 데이터를 toDo라는 매개변수에 넣어서 아래 명령문을 실행힌다. 
            paintToDo(toDo.text);
            
        })
    }
}

 

 

[출력화면]

 

 


5. 전체코드

const toDoForm = document.querySelector(".js-toDoForm");
const toDoInput = toDoForm.querySelector("input");
const toDoList = document.querySelector(".js-toDoList");

const TODOS_LS = 'toDos';

const toDos = []; // toDo를 저장할 리스트 생성 

function saveToDos() { // 저장할 때, object를 string으로 형변환 
    // JSON.stringify()를 이용하면 object를 string으로 형변환할 수 있다. 
    localStorage.setItem(TODOS_LS, JSON.stringify(toDos));
}

function paintToDo(text) {
    const li = document.createElement("li"); // li 생성(엘리먼트를 생성시키는 함수 )
    const delBtn = document.createElement("button"); //버튼 생성 
    const span = document.createElement("span");
    const newId = toDos.length + 1;
    
    delBtn.innerText = "❌";
    span.innerText = text;

    li.appendChild(span); // li 태그 내부에 자식태그를 생성하는 함수 
    li.appendChild(delBtn);
    toDoList.appendChild(li);
    li.id = newId; // li태그를 삭제할 때, 어떤 li태그를 삭제할 것인지 구분하기 위해 id속성값을 부여 

    const toDoObj = { // toDos 배열안에 넣을 정보를 setup 
        text: text,
        id: newId
    };
    toDos.push(toDoObj); // setup된 정보를 배열에 push 
    saveToDos(); // toDos를 지역저장소에 저장하는 함수를 호출 
}

function handleSubmit(event) {
    event.preventDefault();
    const currentValue = toDoInput.value;
    paintToDo(currentValue);
    toDoInput.value = "";
}

function loadToDos() {
    const loadedToDos = localStorage.getItem(TODOS_LS);
    if (loadedToDos !== null) {
        const parsedToDos = JSON.parse(loadedToDos);
        // 스트링으로 바뀌어 저장되어있는 value를 불러와서 사용해야 하므로
        // JSON을 활용하여 string을 다시 object로 형변환 시켜준다. 
        parsedToDos.forEach(function (toDo) {
            // parsedToDos에 들어있는 각 데이터를 toDo라는 매개변수에 넣어서 아래 명령문을 실행힌다. 
            paintToDo(toDo.text);
            
        })
    }
}

function init() {
    loadToDos();
    toDoForm.addEventListener("submit", handleSubmit);
}

init();

 

댓글