본문 바로가기
Front-end/javascript

크롬 앱 만들기 [바닐라 JS] Day 6

by 8Antony 2021. 12. 1.

드디어 실습

로그인 버튼을 구현해 볼꺼다.

 

 

 

Summary #7.0 ~ #7.8

 


#7.0 Set up

 

<form id="todo-form">
      <input type="text" placeholder="Write a To Do and Press Enter" required />
</form>
<ul id="todo-list"></ul>

 <script src="NJS/todo.js"></script>

 

JS todo.js를 만들어 연결해주었고, 새로운 form을 만들어 id를 todo-form으로 했다.

list를 만들기 위해 ul을 하나 만듬. 안에 li 태그가 없는 이유는 JS로 구현할것이기 때문.

 

 

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

function handleToDoSubmit(event) {
    event.preventDefault(toDoInput.value);
    console.log(toDoInput.value);
}


toDoForm.addEventListener("submit", handleToDoSubmit);

 

기본동작을 멈추고 입력값을 확인하기 위해 콘솔로 출력

 

 

이제 엔터를 누르면 input 칸을 비워보도록 할것

 

 

function handleToDoSubmit(event) {
    event.preventDefault(toDoInput.value);
    const newTodo = toDoInput.value;
    toDoInput.value="";
}


toDoForm.addEventListener("submit", handleToDoSubmit);

 

toDoInput.value를 공란으로 채워주면 된다.

콘솔에 newTodo가 출력 되지만, 엔터를 누르면 input도 비워진다.

 

 


 

#7.1 Adding ToDos

 

todo list를 만드는것은 greeting과 유사하다. 그래서 필자는 greeting을 다시 보고 왔다.

 

 

function paintToDo(newTodo) {
    console.log("I will paint", newTodo);
}

function handleToDoSubmit(event) {
    event.preventDefault(toDoInput.value);
    const newTodo = toDoInput.value;
    toDoInput.value="";
    console.log(newTodo);
    paintToDo(newTodo);
}


toDoForm.addEventListener("submit", handleToDoSubmit);

 

브라우저에 입력한 Todo가 나오도록 paintToDo 함수를 만들었다.

 

 

 

잘 작동하고, 엔터를 누르면 지워진다.

이제 handleToDoSubmit에 있는 console.log는 지우겠다.

우리가 list를 html이 아닌 JS에서 만들고 있다.

 

 

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

function paintToDo(newTodo) {
    const li = document.createElement("li");
}

function handleToDoSubmit(event) {
    event.preventDefault(toDoInput.value);
    const newTodo = toDoInput.value;
    toDoInput.value="";
    paintToDo(newTodo);
}


toDoForm.addEventListener("submit", handleToDoSubmit);

 

 

우리는 todo list를 완수하면 삭제도 해야하기 때문에 li안에 span을 넣고싶다.

이럴때는 li처럼 span을 하나 생성해주고, li의 자식으로 넣으면 된다.

 

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

function paintToDo(newTodo) {
    const li = document.createElement("li");
    const span = document.createElement("span");
    li.appendChild(span);
    span.innerText = newTodo;
    console.log(li);
    toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
    event.preventDefault(toDoInput.value);
    const newTodo = toDoInput.value;
    toDoInput.value="";
    paintToDo(newTodo);
}


toDoForm.addEventListener("submit", handleToDoSubmit);

 

 

+ 새로운 li를 list에 추가하는것이다. 뭐 그렇다면 span을 li안에 넣은것처럼

 

 

 

이제 화면에 내가 추가한 todo list내용이 나온다 ! Good

이제 해결해야하는 문제는, 1. todo list를 삭제할 수 있도록 하는것과 2. 새로고침하면 todo들이 날아간다는 것이다.

 

 


 

#7.2 Deleting To Dos 

 

이제 toDo를 삭제하는 button을 추가하자. JS로 만들 것

 

 

function paintToDo(newTodo) {
    const li = document.createElement("li");
    const span = document.createElement("span");
    const button = document.createElement("button");
    button.innerText = "🌙"
    li.appendChild(span);
    li.appendChild(button);
    span.innerText = newTodo;
    console.log(li);
    toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
    event.preventDefault(toDoInput.value);
    const newTodo = toDoInput.value;
    toDoInput.value="";
    paintToDo(newTodo);
}


toDoForm.addEventListener("submit", handleToDoSubmit);

 

 

li안에 button을 추가했다.

물론 아직 삭제 기능은 X

 

 

 

function paintToDo(newTodo) {
    const li = document.createElement("li");
    const span = document.createElement("span");
    span.innerText = newTodo;
    const button = document.createElement("button");
    button.innerText = "🌙"
    li.appendChild(span);
    li.appendChild(button);
    toDoList.appendChild(li);

 

 

순서를 변경했다. .append는 맨 마지막에 놓아야 한다.

이제 클릭을 감지하기 위해 click event listener를 추가할거다.

 

 

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

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

function paintToDo(newTodo) {
    const li = document.createElement("li");
    const span = document.createElement("span");
    span.innerText = newTodo;
    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(toDoInput.value);
    const newTodo = toDoInput.value;
    toDoInput.value="";
    paintToDo(newTodo);
}


toDoForm.addEventListener("submit", handleToDoSubmit);

 

deleteToDo 함수를 만들고 이벤트 리스너도만들었다.

버튼이 여러개가 있을때 우리가 어떤 버튼을 눌렀는지 알기 위한 방법중 하나는 path를 보는것이다.

path를 살펴보면 어떤 element에서 클릭이 일어난건지 알 수 있다.

또한 타겟이 button인것도 확인할 수 있다.

 

 

전체 코드이다.

deleteToDo에서는 event를 파라미터로 받는다.

어떤 버튼이 정확히 눌렸는지에대한 정보와, 우리가 무엇을 삭제해야하는지 알아야 하기때문에, 우리는 event의 target(button)의 부모를 찾는다. (부모는 target를 포함한 상위의 무엇인가이다.)

이 경우에선 그게 li였고, 그것을 지움으로써 우리가 li를 삭제하는것이다.

 

 


#7.3 Saving To Dos

 

이제 실제로 toDo 들을 저장해볼거다. (지금은 새로고침 하면 모두 날아간다.)

greeting과 마찬가지로 local storage를 사용할것이다.

 

단계별로 생각을하면

1. toDo를 저장한다.

2. 저장된 toDo들을 불러온다.

이렇게 두 단계로 나뉠 수 있다. 이 말은 즉, 입력받으면 그것을 화면에 바로 띄우는것이 아니라, local storage에 저장을하고 그 저장된 애들을 화면에 띄운다는 말이다.

저장 먼저 해보자

local storage에는 array는 저장할 수 없다. text만을 저장할 수 있다.

그래도 일단 array로 저장하려는 시도를 해보자.

 

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

const toDos = [];

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

function saveToDos () {
    localStorage.setItem("todos",toDos)
}

function paintToDo(newTodo) {
    const li = document.createElement("li");
    const span = document.createElement("span");
    span.innerText = newTodo;
    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(toDoInput.value);
    const newTodo = toDoInput.value;
    toDoInput.value="";
    paintToDo(newTodo);
    saveToDos;
}


toDoForm.addEventListener("submit", handleToDoSubmit);

 

 

local storage에도 잘 들어가긴 하는데... 문제가 있다..

왜 엔터를 누르면 todo가 추가가 안될까..? 전에 입력한 todo가 delete된다

 

아직까지 해결해야할 문제는 이렇게 저장은 되지만 새로고침하면 화면에 나오지 않는다는 점과, 새로고침 후 새로운걸 입력하면 기존 입력에 덮어씌워지는것과, array가 아닌 text로 저장된다는 점이다.

우리는 입력값들을 array로 저장하고싶다.

브라우저에는 기능이 하나 있는데, JS object나 array를 --> string으로 바꿔주는 기능이 있다. 이걸 사용할거다.

JSON.stringity()이다.

 

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

 

 

이걸 사용하면 이렇게 array모양으로 저장이 된다.

아직 여전히 새로고침하면 덮어씌워지지만, 큰 문제 하나를 해결했다.

 


 

#7.4 Loading To Dos Part One

 

 

우리는, 유저가 이름을 submit하면, form안에 적은 username을 없앨 필요가 있다.

 

 

1. html 자체를 없애기

2. css를 사용해서 숨기기 (이걸로 한다)

 

const TODOS_KEY = "todos";

 

const savedToDos = localStorage.getItem(TODOS_KEY);

if (savedToDos !== null) {
  const parsedToDos = JSON.parse(savedToDos);
  parsedToDos.forEach((item) => console.log("this is the turn of ", item));
}

 

todos들을 받는 savedToDos를 만들고, 이게 비어있지 않다면 (저장되어있는게 있다면) 그것을 parse하여 담는 parsedToDOs를 만들었다.

 

 

아주아주 잘 작동한다!

우리는 JS를 사용해서 원소 하나하나에 대해 함수를 실행할 수 있다.

뭐. 화면에 나오게 한다든지, 혹은 값을 변경한다든지 그런것말이다.

 

function sayHello (item) {
    console.log("this is" ,item);
}

const savedToDos = localStorage.getItem(TODOS_KEY);

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

 

파라미터에 item을 넣었다. 정보가 넘어온다면 콘솔에 출력될 것이다.

짠! 아주 잘 넘어온다.

이런 방법 말고도 한가지 방법이 더 있는데 단축키 구현 같은 곳에서는 이런 function(sayHello) 을 많이 만들지 않아도 된다.

이렇게 새로운 함수를 만드는 대신에

 

 

if (savedToDos !== null) {
    const parsedToDos = JSON.parse(savedToDos);
    parsedToDos.forEach((item) => console.log("this is the turn of ", item));
  }

 

이렇게 만들수도 있다.

 

 


 

#7.5 Loading To Dos Part Two

 

JS의 object나 array등을 string으로 변환할 수 있고, 다시 그 string을 JS에서 사용 가능한 object로 만들 수 있다는 것을 배웠다.

그 다음에는 array의 각 item에 대해 하나의 function을 실행할 수 있다는 것을 배웠다.

그리고 그 함수는 람다식을 사용하거나 혹은 우리가 여태 그랬던 방식으로새로운 함수를 만드는 방법 총 두가지가 있었다.

이제 이번시간에는 우리가 forEach에서 console.log를 했던것을 바꿔볼것이다.

우리는 console.log가 필요 없기 때문 ^^

우리가 원하는것은 item들을 화면에 그려주는것이다.

그리고 화면에 그려주는 paintToDo function은 이미 가지고 있다.

우리는 함수를 호출해주기만하면 된다.

 

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

 

이렇게 호출해주기만 하면, 원소들을 하나씩 넘기며 paint해줄것이다.

 

 

잘 넘어온다.

 

이제 남은 문제는 저장된것을 새로고침해도 불러오지만, 거기에 대고 또 새로운걸 입력한다면 local Storage에서 덮어씌워진다는 것이다.

우리는 기존것과 새것 모두 보존하고싶다.

 

우리의 app이 실행될때마다 배열이 비어있기 때문이다.

그리고 newToDo를 작성하고 form을 submit할 때마다 newToDo를 그 빈 array에 push한다.

또 localStorage에 저장할때에는 새로운 toDo들만 포함하고 있는 array를 저장하는 것이다.

이 과정들은 전에 있던 toDo들을 날리고 새로운것만 저장하게 한다.

이 문제는 매우 쉽게 해결할 수 있다.

바로 저 toDos array를 빈 값으로 시작하는 대신에 const->let으로 바꾸고 localStorage에 toDo들이 있으면 toDos에 parsedToDos를 넣어서 전에 있던 toDo들을 복원하는 것이다.

 


 

#7.6 Loading To Dos Part Two

 

우리의 이제 이 todo의 원소들에게 ID같은것을 부여하여 text대신 object를 만들고 싶은것이다.

 

 

이렇게 id와 text가 한쌍인 배열을 만들고 싶은 것. 약간 태그를 사용하는 방법과 비슷한 것 같다.

먼저 랜덤 ID를 만드는것을 배워보자.

안전한 랜덤은 아니고, element가 만들어질때 이 ID를 가지게 된다.

 

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

 

 

자. 새로운 object를 만들었다. 우리가 text로 입력받은것은 이 오브젝트의 원소로 들어가며, 이제 text대신 object를 push한다.

 


 

#7.7 Deleting To Dos part Two

 

array에서 어떻게 element를 삭제하는지 알기 위해서는 paintToDo에서 어떤 일이 일어나는지 완전히 이해해야 한다.

forEach 함수는 배열의 원소당 한번씩 함수를 실행시킨다.

그리고 원소는 하나의 오브젝트이다. 이걸 이해하는게 굉장히 중요한데, forEach와 비슷한 역할을 하는것을 만들것이다.

바로 filter이다. 이 함수는 만약 array에서 뭔가를 삭제할 때 실제로 그 array에서 그걸 지우는게 아니라, 지우고 싶은 item을 제외한 새 array를 만든다. item을 지우는것이 아닌, 제외하는 것이다.

filter는 filter 함수를 필요로 한다.

 

function sexyFilter() {

}

[1,2,3,4].filter(sexxyFilter);

 

이런 형태로 사용하는데, filter는 forEach와 매우 유사하게 앞에 있는 배열의 원소를 하나씩 넣어 함수를 실행한다. (그림에서는 sexyFilter)

즉, 1,2,3,4를 가진 배열에 대해 sexyFilter(1), sexyFilter(2).. 이런식으로 실행한다는 것이다.

sexyFilter 함수는, 새 array에서도 "기존의 원소들을 모두 포함하고 싶다면" 반드시 true를 return해야한다.

false를 return하는 item은 새 array에 포함되지 않는다.

 

 

이렇게 false 만을 리턴하는 함수를 사용하면 빈 array가 되지만, true만을 리턴하는 함수를 사용하면 모두 보존된다.

 

 

이런식으로 사용한다면 특정 원소를 제외한 array를 만들수도 있다.

리턴값이 item !== 3이기 때문에, 1,2,4,5가 오는 경우에는 이 조건식이 충족되어 true이지만, 3이오면 조건식에 충족되지 않아 false가 되기 때문이다.

숫자 말고 string으로 해보자.

 

 

숫자라면 특정 숫자가 아닌, 범위에 해당되거나 되지 않는 경우에도 제외시킬 수 있다.

 

 

 

 


 

#7.8 Deleting To Dos part Three

 

7장의 마지막..

filter를 사용하면 기존 array를 편집하는게 아니라 새로운 array를 만든다는 것이다.

 

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

 

우리는 기존의 toDos에 덮어씌워 갱신시켜줘야한다.

toDos의 원소들을 toDo라는 이름으로 돌며 우리가 찾은 li의 id와 같은것을 제외시키는 것이다.

 

하지만 여전히 제대로 지워지지 않는다

data type이 다르기 때문인데, toDo들의 id는 숫자이지만 li의 id는 string이기 때문이다.

 

타입이 다르기 때문에 해당되는 것이 없어 모두 true가 반환 된 것! 하지만 우리는 타입 캐스팅 하는 방법을 앞에서 배웠다.

parseInt를 사용하면 된다.

 

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

 

그리고 다시 saveToDos를 호출하는 것을 잊지 말자.

첫 입력에서는 handle함수에서 스트링으로 바꿔줬지만, 우리는 새로고침을 하며 JSON.pase로 다시 object로 만들어서 삭제하도록 했다.

그렇기 때문에 다시 저장하기 위해서는 string으로 바꿔줘야해서 saveToDos를 다시 호출하는 것이다.

 

 

이제 삭제하고 새로고침해도 문제없다.

댓글