[JavaScript] 바닐라 JS로 크롬 앱 만들기_#11

강성일·2023년 3월 8일
0
post-thumbnail

Date.now()

localStorage는 toDos array를 저장해두는 곳이지 데이터베이스가 아니다.
콘솔에서 toDos.array를 보면 [”a”, ”b”, ”c”] 식으로 array가 나오는데 이것이 데이터베이스다.

우리는 화면에서 어떤 Html의 element를 지워야하는지 알고 있지만, 어떤 todo text를 데이터베이스에서 지워야하는지 알 수 없다.

그래서 우리는 toDos를 더 나은 방법으로 만들어야 한다.
바로 array 안에 있는 각각 item 들에게 id를 부여하는 것이다.
즉, text 대신 object를 만들고 싶은거다.
ex) [{id:121212, text:“a”}] 이런 식으로 말이다.

각각 item들의 id는 랜덤하게 줄 것인데 이는 Date.now()를 사용할 것이다.
Date.now()는 밀리초(1000분의 1초)를 주는데 이 값이 랜덤처럼 보여 이 것을 사용할 것이다.


function handleToDoSubmit(event) {
  event.preventDefault();
  const newToDo = toDoInput.value;
  toDoInput.value = "";
  
	const newToDoObj = {
    text: newToDo,
    id: Date.now(),
  };
  
  toDos.push(newToDoObj);
  
  paintToDo(newToDo);
  saveToDos();
}

기존 item은 text 형태였기 때문에 newToDoObj을 추가하여 object 형식으로 toDos에 푸시를 해준다.

다음으로 이 id를 사용하기 위해서는 id를 Html에 두는 것이다.
paintToDo가 string으로 newToDo를 주는 것 대신에 newToDoObj을 줄 것이다.

그냥 paintToDo(newToDoObj) 으로 변경 후, 글자를 적게되면 [object Object]로 나오게 된다.

우리는 이 형태가 아닌 우리가 적은 실제 글자를 보고 싶다.
따라서 paintToDo 함수 내부를 변경할 것이다.


function paintToDo(newToDo) {
  
  const li = document.createElement("li");
	li.id = newToDo.id;
  
  const span = document.createElement("span");
  span.innerText = newToDo.text;
  
  const button = document.createElement("button");
  button.innerText = "❌";
  button.addEventListener("click", deleteToDo);
  li.appendChild(span);
  li.appendChild(button);
  toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
  event.preventDefault();
  
  const newToDo = toDoInput.value;
  toDoInput.value = "";
  
  const newToDoObj = {
    text: newToDo,
    id: Date.now(),
  };
  
  toDos.push(newToDoObj);
  paintToDo(newToDoObj);
  saveToDos();
}

paintToDo(newToDoObj)도 변경하고, 내부의 newToDo(object)를 text 형식으로 받아오게 한다.

즉, span.innerText = newToDo는 .text를 붙여
span.innerText = newToDo.text 로 변경해준다.
그럼 우리가 원하는 작성한대로 실제 글자를 볼 수 있다.

그리고 paintToDo는 newTodo 라는 이름의 인수의 값으로 newTodoObj 라는 object를 받고있기 때문에 li.id = newToDo.id로 설정해준다.

이제 우리는 데이터베이스 안에서 item 들이 id를 가지고 있다는 것을 확인할 수 있다.
이 뜻은 이제 삭제가 가능하다는 뜻이다 !

하지만 삭제를 위해선 우리가 ❌ 버튼을 누른 id 값도 필요하다.


function deleteToDo(event) {
  const li = event.target.parentElement;
  li.remove();
}

우리는 리스트를 화면에서 지우기 전에 li를 얻게 된다.
따라서 console을 찍어보면 알겠지만 li의 id 또한 먼저 얻고 있었다는 말이다.


filter

만약 우리가 array에서 뭔가를 삭제할 때, 실제로 일어나는 일은 삭제가 아니라 제외다.
선택된 item을 제외하고 array를 새로 만든다는 뜻이다. 이것은 filer 함수를 쓰면 된다.


const newFilter(){

}

[1, 2, 3, 4].filter(newFilter)

filter는 forEach와 비슷하다.
filter는 newFilter를 불러주고 1, 2, 3, 4에 차례로 newFilter가 4번 실행될 것이다.

newFilter(1)
newFilter(2)
newFilter(3)
newFilter(4)

이렇게 말이다.
만약 새 array에서 이 object를 유지하고 싶다면, newFilter는 반드시 true를 반드시 리턴해야한다.

false를 리턴 시, 그 item은 새로운 array에 포함되지 않을 것이다.
위에서 newFilter(3) 가 false라면 3을 제외한 나머지 1, 2, 4만을 포함한 새로운 array가 만들어진다.

그렇다면 newFilter는 어떻게 만들어야할까?
newFilter는 3이 아니면 true를 리턴해야한다.

따라서 이런 형태가 될 것 이다.

function newFilter(item){return item !== 3}

[1, 2, 3, 4, 5].filter(newFilter)

newFilter는 argument(item)을 각각 4번 가져오기 때문에 item을 저장해둘 공간 또한 필요하다.
그렇기 때문에 newFilter() 괄호 안에는 item이 들어가게 된다.
그리고 3이 아니면 true 이며, 이를 return 하라고 하는 것이다.

따라서 [1, 2, 3, 4, 5].filter(newFilter) 의 결과는 [1, 2, 4, 5] 가 될 것이다.

더 나아가 응용을 해보자면 랜덤한 수를 가지고 있는 arr를 만들고 filter를 이용하여

potato가 1000보다 작거나 같은지 묻고 싶다.


const arr = [1234, 655, 38327, 237]

function newFilter(potato){return potato <= 1000}

arr.filter(newFilter)

로 작성하면 되고, 결과 값은 [655, 237] 가 될 것이다.

다시 원점으로 돌아오자!

우리는 이제 데이터의 id 값을 다룰 수 있게 되었다.

우리의 object는 이렇게 생겼다.

{text: "fsfs", id: 1677844356184}
{text: "ㄹㄴㅇ", id: 1677844514683}
{text: "ㅇㄴㄹ", id: 1677844907495}
{text: "ㄴㅇㄹ", id: 1677844924413}

이것을 toDos로 묶고 filter를 적용할 수 있다.

첫 번째 id를 제외하고 새로운 array를 만들고 싶다고 해보자.

const toDos = [{text: "fsfs", id: 1677844356184},
{text: "ㄹㄴㅇ", id: 1677844514683},
{text: "ㅇㄴㄹ", id: 1677844907495},
{text: "ㄴㅇㄹ", id: 1677844924413} ]

function newFilter(todo){return todo.id !== 1677844356184}

todo.filter(newFilter)

다음과 같이 작성 시, 결과 값은 첫 번째 id 만 제외되어 나머지 3개가 나올 것이다.

이런 식으로 말이다 !!

우리는 리스트를 지울 때마다 각각 다른 id를 얻는다는 사실을 알고 있다.
이제 우리가 해야 하는 일은 이 id가 있는 todo를 지우는 것이다.
filter는 기존 array를 변경하지는 않는다는 것을 기억해두자.


if (savedToDos !== null) {
  const parsedToDos = JSON.parse(savedToDos);
  toDos = parsedToDos;
  parsedToDos.forEach(paintToDo);
}

todos가 localStorage에 저장되어 있으므로, 저장할 때 savedToDos에서 선언했던 것처럼 삭제할 때 또한 toDos array를 업데이트 해야한다.


코드를 먼저 보자.

function deleteToDo(event) {
  const li = event.target.parentElement;
  li.remove();
  toDos = toDos.filter((todo) => todo.id !== parseInt(li.id));
  saveToDos();
}

  1. toDos를 업데이트 할 것이므로, toDos = 이다.

  2. 다음은 필터를 이용할 것이므로 toDos = toDos.filter()

  3. 삭제 버튼을 클릭했던 li의 id를 갖고 있는 todo를 지우고 싶다.
    이 말은 즉, todo의 id가 li의 id와 다른 것을 남기고 싶다.
    toDos = toDos.filter((todo) => todo.id !== li.id)
    todo는 toDos DB 안에 있는 요소 중 하나다.

  1. id는 number이다.
    todo.id는 number이지만 li.id는 string 형태이다.
    따라서 parseInt 형태를 사용하여 number 형태로 바꿔주어야
    정상적으로 기능한다.

  2. 업데이트 되었으므로 saveToDos를 부른다.


4번 문자 type에 대하여 추가 설명해보겠다.

원인은 paintToDo 함수 내에 있는
const li = document.createElement("li");
li.id = newTodo.id;
이 부분에 있다.

li는 DOM을 직접 건드린건데, mdn 문서 뒤져보니 DOM의 id는 문자열이라고 나와 있다.
즉, newTodo.id로 number를 넣어주었어도, DOM에선 string으로 형변환해서 받아들이는거 같다.

사이트 매개변수 id 부분을 참고하여 보면 설명 나와있다!
https://developer.mozilla.org/ko/docs/Web/API/Document/getElementById


✅ 최종 코드

const toDoForm = document.getElementById("todo-form");
const toDoInput = toDoForm.querySelector("input");
const toDoList = document.getElementById("todo-list");

function deleteToDo(event) {
  const li = event.target.parentElement;
  li.remove();
  toDos = toDos.filter((todo) => todo.id !== parseInt(li.id));
  saveToDos();
}

const TODOS_KEY = "todos";

let toDos = [];

function saveToDos() {
  localStorage.setItem(TODOS_KEY, JSON.stringify(toDos));
}

function paintToDo(newToDo) {
  const li = document.createElement("li");
  li.id = newToDo.id;
  const span = document.createElement("span");
  span.innerText = newToDo.text;
  const button = document.createElement("button");
  button.innerText = "❌";
  button.addEventListener("click", deleteToDo);
  li.appendChild(span);
  li.appendChild(button);
  toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
  event.preventDefault();
  const newToDo = toDoInput.value;
  toDoInput.value = "";
  const newToDoObj = {
    text: newToDo,
    id: Date.now(),
  };
  toDos.push(newToDoObj);
  paintToDo(newToDoObj);
  saveToDos();
}

toDoForm.addEventListener("submit", handleToDoSubmit);

const savedToDos = localStorage.getItem(TODOS_KEY);

if (savedToDos !== null) {
  const parsedToDos = JSON.parse(savedToDos);
  toDos = parsedToDos;
  parsedToDos.forEach(paintToDo);
}
profile
아이디어가 넘치는 프론트엔드를 꿈꿉니다 🔥

0개의 댓글