본문 바로가기

Javascript/응용프로젝트

[3] 자바스크립트로 투두리스트(todo-list) 만들기 + localStorage에 대해서

 

자바스크립트로 투두리스트 (todo-list) 만들기 3편입니다.

완성본과 HTML 과 CSS 는 1편에서 확인해주세요

1편 https://qowbtm45.tistory.com/44

2편 https://qowbtm45.tistory.com/46

 


기능 설명 (3편)

1. 새로고침해도 데이터가 저장될 수 있도록 하기

2.데이터마다 id, content, status 값 저장하기

 

local Storage (로컬 스토리지)에 대해서

- 데이터를 사용자 로컬에 보존하는 방식

- 데이터를 저장, 덮어쓰기, 삭제 등 CRUD (Create, Read, Update, Delete를 앞글자를 따서 만든 단어)가 가능

- 자바스크립트로 조작하며, 모바일에서 사용이 가능함

 

local Storage (로컬 스토리지) 와 쿠키 차이점

- 유효 기간이 없고 영구적으로 이용 가능

- 5MB 까지 사용 가능 ( 쿠키는 4KB )

- 필요할 때 언제든 사용 가능 ( 쿠키는 서버 접속시에 자동 송신 )

 

local Storage (로컬 스토리지) 기본 구성

- 키(key) 와 값(value) 를 하나의 세트로 저장

- 도메인과 브라우저별로 저장

- 값은 반드시 문자열로 저장해야함

주의 할 점
- 데이터 저장 형식이 오직 문자형(string) 데이터 타입만 지원함
=> 다른 타입의 데이터를 저장하려 할 경우 문자형으로 변환하기 때문에 객체형 데이터나 숫자형 데이터를 저장할 때 주의

해결 방법
- JSON 형태로 데이터를 읽고 쓰기
localStorage.setItem("json", JSON.stringfy({a:1, b:2})
JSON.parse(localStorage.getItem("json"))
=> {a : 1, b: 2}​

: 위와 같은 방법으로 배열, 숫자, 객체형 타입의 데이터를 저장할 때에 사용 가능

 

local Storage (로컬 스토리지) 기본 문법

// 키에 데이터 쓰기
localStorage.setItem("key", value)

// 키로 부터 데이터 읽기
localStorage.getItem("key")

// 키의 데이터 삭제
localStorage.removeItem("key")

// 모든 키의 데이터 삭제
localStorage.clear()

// 저장된 키/값 쌍의 개수
localStorage.length

출처 : https://velog.io/@lee_geon_woo4336/Localstorage%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90


new TodoList 인스턴스에 Storage 인스턴스 추가

document.addEventListener("DOMContentLoaded", () => {
	...
  const todoList = new TodoList(new Storage());
	...
});

TodoList 클래스에서 storage 설정

 

constructor () 

constructor(storage) {
this.assignElement();
this.addEvent();
this.initStorage(storage);
this.loadSavedData();
}
1. constructor 에서 storage 를 받아오기 때문에 constructor의 매개변수로 storage 를 넣어준다.

 

storage 클래스 변수 생성

class TodoList {
  storage;
  ...
 }

 

initStorage()

  initStorage(storage) {
    this.storage = storage;
  }
1. 받아온 storage 를 클래스 변수로 만들어놓은 storage 에 넣어준다.

 

onClickAddBtn() 

  onClickAddBtn() {
    if (this.todoInputEl.value.length === 0) {
      alert("내용을 입력해주세요");
      return;
    }
    const id = Date.now();
    this.storage.saveTodo(id, this.todoInputEl.value); // 추가
    this.createTodoElement(id, this.todoInputEl.value);
  }
1. storage 클래스에 있는 saveTodo 를 가져와 id 값과 todoInput 에 적은 value 값을 파라미터로 보내주도록 함

 

saveTodo() - TodoList 클래스에 속한 함수

  saveTodo(target) {
    const todoDiv = target.closest(".todo");
    todoDiv.classList.remove("edit");
    const todoInputEl = todoDiv.querySelector("input");
    todoInputEl.readOnly = true;
    const { id } = todoDiv.dataset; // 추가
    this.storage.editTodo(id, todoInputEl.value); // 추가
  }
1. 생성한 todolist div 의 data-id 에서 id 값을 저장해준다.
2. localStorage 에서 값이 변경될 수 있도록 id 값과 수정된 Input 의 value 값을 보내주도록 함

 

deleteTodo() - TodoList 클래스에 속한 함수

  deleteTodo(target) {
    const todoDiv = target.closest(".todo");
    todoDiv.addEventListener("transitionend", () => {
      todoDiv.remove();
    });
    todoDiv.classList.add("delete");
    this.storage.deleteTodo(todoDiv.dataset.id); // 추가
  }
1. 삭제될 div 의 id 값을 class Storage의 deleteTodo 에게 보내주도록한다
=> localStorage 에서 같은 id 값을 가진 값을 삭제하기 위해서

 

completeTodo()

  completeTodo(target) {
    const todoDiv = target.closest(".todo");
    todoDiv.classList.toggle("done");
    const { id } = todoDiv.dataset;
    this.storage.editTodo(
      id,
      "",
      todoDiv.classList.contains("done") ? "DONE" : "TODO"
    ); // 추가
  }
1. 위 코드와 같이 클릭한 타겟의 id 값을 저장한다.
2. class storage 에게 id 과, 빈 string 값 , 그리고 todolist 에 완료된 클래스가 있을 경우에는 "DONE" 값을 보내고, 아니라면
"TODO" 값을 보내도록 함.
=> localStorage 의 현재 상태 (status) 값을 바꾸기 위해서

 

loadSavedData() - localStorage 에 저장된 데이터들을 새로고침해도 UI에 보일  수 있게 하는 함수

  loadSavedData() {
    const todosData = this.storage.getTodos();
    for (const todoData of todosData) {
      const { id, content, status } = todoData;
      this.createTodoElement(id, content, status);
    }
  }
1. class storage 에서 가져온 데이터를 todosData 에 넣어준다.
2. 받아온 데이터들을 for of 문을 사용해서 id 값과 content, status 를 저장해준다.
3. 저장된 id 값과 content, status 를 createTodoElement 에게 파라미터로 전달한다.
=> 저장된 데이터를 토대로 element 를 동적으로 생성해줘서 UI에 보여주기 때문에

 


Storage 클래스

 

getTodos() - localStorage에서 데이터 분석해서 가져와주는 함수

  getTodos() {
    return localStorage.getItem("todos") === null
      ? []
      : JSON.parse(localStorage.getItem("todos"));
  }
1. localStorage에서 "todos" 로 저장된 값이 없을 경우에는 빈 배열을 반환함
2. 저장된 값이 있을 경우에는 JSON.parse() 메서드를 사용해 저장된 문자열의 값을 객체형으로 반환받을 수 있도록 해줌.

 

saveTodo() - 투두리스트에 대한 데이터 저장하는 함수

  saveTodo(id, todoContent) {
    const todosData = this.getTodos();
    todosData.push({ id, content: todoContent, status: "TODO" });
    localStorage.setItem("todos", JSON.stringify(todosData));
  }
1. getTodos() 에서 가져온 데이터를 todosData 에 저장한다.
2. todosData에 class TodoList 에서 받아온 파라미터들을 객체형으로 넣어주도록 함.
3. 저장된 todosData 를 localStorage에 저장해줌
=> 현재는 데이터가 객체형으로 되어있어서 문자열로 바꿔서 저장해줘야하기 때문에 JSON.stringfy() 를 사용

 

editTodo() - 수정된 데이터들을 저장해주는 함수

  editTodo(id, todoContent, status = "TODO") {
    const todosData = this.getTodos();
    const todoIndex = todosData.findIndex((todo) => todo.id == id);
    const targetTodoData = todosData[todoIndex];
    const editedTodoData =
      todoContent === ""
        ? { ...targetTodoData, status }
        : { ...targetTodoData, content: todoContent };
    todosData.splice(todoIndex, 1, editedTodoData);
    localStorage.setItem("todos", JSON.stringify(todosData));
  }
1. class TodoList 의 editTodo() 와 다르게 status 의 기본 값을 "TODO" 로 해준다.
2. saveTodo () 와 같이 getTodos() 에서 데이터를 가져와 todosData 에 저장해줌
3. 수정되는 todo 를 찾아주기 위해서 findIndex() 메서드를 사용해준다
=> localStorage는 배열 안에 객체형으로 데이터들이 저장되어있기때문에 index 로 현재 수정되는 target 데이터를 찾으려면 index 를 알아내야함
=> localStorage 데이터의 id (todo.id) 와 class TodoList 에서 파라미터로 받아온 id 가 같은 데이터를 찾아주고 그 index 를 반환받음
4. class TodoList 에서 수정된 content 값 (todoContent) 가 없다면 원래 데이터들을 저장하고, status 값이 변경될 수 있으니 바뀐 status 값을 저장해준다.
5. class TodoList 에서 수정된 content 값 (todoContent) 가 있다면 원래 데이터들을 저장하되, 바뀐 값만 content 값으로 넣어주도록 함.
6. 바뀐 데이터를 splice 메서드를 사용해서, 그 바뀐 index의 데이터 부분만 바꿔주도록 함
7. 수정된 데이터 (todosData) 를 localStorage 에 저장해줌
splice()
- 배열의 기존 요소를 삭제 또는 교체하거나 새 요소를 추가하여 배열의 내용을 변경하는 배열함수
    array.splice(start[, deleteCount[, item1[, item2[, ...]]]])​

start
배열의 변경을 시작할 인덱스

deleteCount
배열에서 제거할 요소의 수

item1, item2
배열에 추가할 요소
아무 요소도 지정하지 않으면 splice()는 요소를 제거하기만 함

 

deleteTodo() - 데이터를 삭제해주는 함수

  deleteTodo(id) {
    const todosData = this.getTodos();
    todosData.splice(
      todosData.findIndex((todo) => todo.id == id),
      1
    );
    localStorage.setItem("todos", JSON.stringify(todosData));
  }
1. 위와 동일함
2. todosData 에서 findIndex 를 찾아, 삭제 버튼을 클릭한 타겟의 id 를 비교해 일치하다면 그 데이터의 인덱스를 반환해줌
3. 반환된 인덱스 값을 splice의 매개변수로 넣어주고, 1개 삭제해주도록 한다 (뒤에 아무것도 안쓰면 삭제됨)
4. 삭제 된 후의 데이터를 localStorage 에 저장해줌

전체 기능 구현 코드

사진이 안보인다면, 새로고침 해주세요