Vanilla JS로 크롬 앱 만들기 - To Do List

wldls·2022년 11월 11일
0

javascript

목록 보기
16/33

노마드 코더(Vanilla JS로 크롬 앱 만들기) 영상으로 클론 코딩 하며 기록한 글 입니다

TO DO LIST

1단계 : 먼저 input 으로 사용자의 value를 받아보자

HTML의 태그를 javascript로 접근하여 각 변수에 저장하였다
변수 toDoForm 즉 input에 사용자가 value 값을 넣고 엔터(=submit)를 동작하면 감지하여 handleSubmit 함수를 호출한다
함수가 동작하게 되면 javascript가 발생한 이벤트는 함수의 첫번째 인자로 전달된다
e.preventDefault() 이벤트를 막아줌으로써, 엔터를 쳐도 submit의 기본동작인 새로고침이 발생 되는것을 막아준다
사용자가 입력한 input의 현재 value를 새로운 변수에 복사 후에 input을 빈칸으로 할당한다
(저장된 value값이 빈칸 되는것이 아님 ❗)

다음 결과를 콘솔로 확인해보자 👉

submit 되자마자 변수에 새로운 value가 저장되며
input은 빈칸이 되는것을 확인할 수 있다

2단계 : todo를 그려 넣어보자 📝

paintToDo() 함수는 toDo에서 그려낼 사용자의 value값을
인자로 받는 함수이다
ul안에 추가되는 태그인 lispan을 불러와
appenChild로 자식요소로 만든다
인자로 받은 사용자의 value값을 span이 자식요소로 포함된
li 변수에 innerText 되어 할당된다
li 변수는 다시 ul의 변수에 자식요소로 만들어
사용자의 value가 submit될때마다 todo-list에 저장되는 모습이 보여진다

이때 빈칸을 submit 하려고 하면 todo-list에 나타나지 않는다
이유가 뭘까 🤨

input에 required를 넣었기 때문에 HTML에서 field를 보호하고 있다

3단계 : toDo를 삭제하는 button을 추가해보자

button은 click 이벤트를 기다리고 있어야 한다
paintToDo() 함수 안에서 모든 것이 append되기 전에 button을 생성한다
innerText로 ❌ 이모지를 할당 한 후 li변수의 자식요소로 넣어준다

buttonTag.innerText = "❌";

아래와 같이 span태그 옆에 button이 보여진다


button을 클릭하면 삭제하기 위해
deleteToDo() 삭제 함수를 생성하여
paintToDo() 함수 안에서 button 태그가 클릭이 감지 될때 호출하게 하게 한다
하지만 아직 어떤 li가 삭제 되는지 알지 못한다
여러 todo-list가 생성된다면 같은 event를 기다리며
같은 deleteToDo() 함수를 실행 시킬 것이다
선택한 그것이 무엇인지 어떻게 알수 있을까 ? 🤨

클릭 이벤트에 대한 정보가 있는 e 를 deleteToDo() 파라미터 안에 넣는다 그리고 클릭 된 button이 어떤 것인지 단서를 살펴보자

e의 이벤트는 property를 가지는데 어떤 button을 클릭했는지 알려준다
콘솔로 toDo를 각각 살펴보면 parentNode: li를 찾을 수있다
이것은 누가 그 button의 부모냐는 의미이다
이것으로 button이 클릭 되었는지는 알수 있다
하지만 그것의 parentElement를 얻어야 한다

e.target.parentElement

👉 target 속성은 이벤트가 발생한 대상 객체를 가리킴
parentElement 속성은 지정된 요소의 부모 요소를 반환

  • 우리는 e(event)에 대한 유용한 정보를 얻고 있다
  • target(클릭된 html element)으로 보다 자세히 살펴봐야한다
  • 그 html element에는 많은 property가 있는데
    그것중에 parentElement(클릭된 element의 부모)이다
  • 클릭한 target인 button, 부모 parentElement인 li 인것

클릭 된게 어느것인지 .innerText로 얻은 화면의 모습이다
이제 어떤것이 클릭 되었는지 알수 있다 !

이를 바탕으로 todo-list 삭제 함수에 적용해보자

function deleteToDo(e) {
  const valueLiTag = e.target.parentElement;
  valueLiTag.remove();
}

❌ button을 클릭했을때 event를 얻게 된다 ->
event는 어떤 button이 클릭되었는지 대상(target)을 준다->
target의 부모(parentElement)를 접근 할수 있다 ->
삭제하고 싶은 li를 변수에 할당한다 -> remove()

4딘계 : toDo 저장하기

해당 toDo 삭제 기능은 되지만 새로고침을 하면 모두 삭제 되어 버린다 ❗🥲 문제를 해결해보자

브라우저에 어떻게 저장 할수 있을까 ?
그것은 localStorage이다

const toDos = [];

먼저 toDo 들을 저장하는 것부터 시작해보자
저장 하기 위해서는 배열을 만들어야 한다 그리고 사용자의 입력값을 .push로 넣는다 목표는 배열 안의 요소들을 localStorage에 넣는 것
하지만 localStorage에 array를 저장할 수 없다
오직 텍스트만 저장하기 때문에 다음의 localStorage에 저장하는 함수를 만든다

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

위의 saveToDos() 함수 역할은 오직
toDos array 내용을 localStroge에 넣는 역할 하는 함수이다

function handleSubmit(e) {
  e.preventDefault();
  const userInputValue = toDoInput.value;
  toDoInput.value = "";
  toDos.push(userInputValue);
  paintToDo(userInputValue);
  saveToDos();
}

saveToDos() 호출 하는 시점에는
사용자가 입력한 값인 userInputValue를 갖고 있는 array가 있다
input에 입력 후 Application을 확인해보면 다음과 같다


하지만
Application에 단순히 string 으로 저장되는게 아니라
배열로 저장하고 싶다면 어떻게 해야될까 ?

이 값을 string으로 바꿔 주는 브라우저 기능을 알아보자

JSON.stringify() 메서드는 JavaScript 값이나 객체를 JSON 문자열로 변환한다, 배열로 전달할 경우 지정한 속성만 결과에 포함한다

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

Application에서 string으로 나타나는 것을 JSON.stringify() 메서드를 통해 배열의 모양으로 저장 된다
아래 화면 확인 👇

5단계 : localSorage 불러오기

저장한 값은 localStroge에 저장되어 있다
하지만 새로고침을 하면 화면에 나타나지 않고 사라진다
이것을 해결해보자

단순한 string을 살아있는 array로 변환하기

localStorage.getItem("toDos")
//console ->  '["1","2","3","4","5"]'    - string 형태

JSON.parse(localStorage.getItem("toDos"))
//console -> (5) ['1', '2', '3', '4', '5']  - 배열 형태

웹 서버에서 데이터를 수신할 때 데이터는 항상 문자열이다
그러므로 toDos 를 가져오면 콘솔에는 string으로 나타나는데
이것을 JSON.parse() 메서드로 데이터를 파싱하면 JavaScript 배열을 반환 한다

const savedToDosLocal = localStorage.getItem(TODOS_KEY);

if (savedToDosLocal) {
  const parsedToDos = JSON.parse(savedToDosLocal);
  parsedToDos.forEach(paintToDo);
}

if 조건문으로 savedToDosLocal가 localStorage 에 존재 한다면
localStorage 에서 온 string 을 JSON.parse()로 살아있는 javascript array로 변하게 한다
그리고 배열 형태로 변환된 변수 parsedToDos를 forEach() 메서드로
array에 있는 각각의 paintToDo(사용자 value값을 인자로 받아 화면에 생성하는 함수)에 대해 function을 실행 할수 있게 한다

forEach() 메서드 는 주어진 함수를 배열 요소 각각에 대해 실행 해주는 것

하지만 여기서 문제는 새로고침 후 다시 toDo를 저장하면 localStorage에 이전의 저장된 toDo들 위에 새로 저장 된다
-> Application이 시작 될때 새로 저장된 userInputValue를 항상 비어있는 array에 push 하기 때문이다 이전의 저장된 toDo들은 localStorage에만 있는 상태
즉, toDos의 이전 복사본을 잊어버리고 있다

해결방법 :
Application이 시작 될때 toDos = [ ] 의 빈값 대신에
const ->let 으로 바꿔 업데이트 되게 만들고
빈배열 toDos에 parse하여 살아있는 배열이 된 것을 다시 할당 👇

let toDos = [];
const savedToDosLocal = localStorage.getItem(TODOS_KEY);

if (savedToDosLocal) {
	const parsedToDos = JSON.parse(savedToDosLocal);
  	toDos = parsedToDos; // 추가
  	parsedToDos.forEach(paintToDo);
}

아래 화면을 확인해보면 기존 1,2,3,4,5 가 사라지지 않고
새로 입력 한 toDo인 6,7,8이 이어서 추가 된 모습이다

하지만 여기서 또 다른 문제는
❌ button으로 삭제해도 화면에서는 사라지지만
localStorage에서는 그대로 유지 되어있다
다음 단계에서 해결해보자

6단계 : 삭제한 toDo -> localStorage에 업데이트 하기

javascript , HTML 입장에서는 우리가 원하는 것을 정확히 파악하지 못하고 있다
toDo를 삭제하는 역할인 함수 deleteToDo()를 확인해보면

function deleteToDo(e) {
  const valueLiTag = e.target.parentElement;
  valueLiTag.remove();
}

위의 코드처럼 화면에서 어떤 HTML의 element 를 지워야 하는지는 알고 있다
하지만 어떤 toDo 텍스트를 데이터 베이스에서 지워야 하는지 모른다

해결방법 : toDo 들에게 ID 추가 , 텍스트를 object로 만든다 👇

[{id:id이름, text:"toDo 내용"}]

id로 각각의 li item을 구별 짓게 된다
데이터베이스에게 id 를 저장하는 옵션을 주었다
이제 object에 id가 저장되는 것이 확인된다

사용자가 ❌ button을 누른 것의 id를 얻어야 한다
4단계 : toDo 저장하기에서 push로 array에 item을 추가하는 것을 확인했었다 다음 단계에서는 삭제하는 방법을 알아보자

7단계 : item을 삭제하는 방법

짚고 넘어가야 할 요점은

parsedToDos.forEach(paintToDo);

forEach 함수는 toDo를 생성하고 그리는 역할인 paintToDo() 함수를,
localStorage 에서 온 string 을 살아있는 javascript array로 변하게하는 parsedToDos변수의 배열의 요소마다 실행한다
forEach 함수는 paintToDo에 각각의 item(object)을 주어 실행 하는 것

여기서 알아야 할 메서드 filter 이다
filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환하는 것.

만약 array에서 뭔가를 삭제할 때 삭제 item을 지우는게 아니라
삭제 item을 제외 하는 것
예전 array는 유지되고 삭제 item이 제외 된 새로운 array가 생성 됨

예를 들어 1000보다 높은 숫자를 제외하고 filter로 새로 배열 생성해보자

const array = [231,342,3425,24,5,67,456,156]

//javascript가 array의 각 item을 selectArr( )에 인자로 각각 전달된다
function selectArr(num) {return num <= 1000}
console.log(array.filter(selectArr))
// 결과 : [231, 342, 24, 5, 67, 456, 156]

결과는 1000보다 높은 3425만 제외된 나머지 요소들이 새로운 배열로 반환 되었다

이것을 응용해 사용자가 선택한 그것과 다른 id만 새로 반환하면 된다

function deleteToDo(e) {
  const valueLiTag = e.target.parentElement;
  valueLiTag.remove();
  toDos = toDos.filter((toDo) => toDo.id !== parseInt(valueLiTag.id)); // 추가
  saveToDos();
}

삭제할때 localStorage에 업데이트 해줘야 한다
삭제버튼을 클릭했던 li의 id를 갖고있는 toDo 를 제외한
다른 id는 남겨야 한다 -> toDo.id !== valueLiTag.id
toDo 는 인자로써 아무거나 넣어도 상관없는 이름일뿐,
toDo는 toDos DB에 있는 요소중 하나이다 그 요소 하나하나의 id와 사용자가 선택한 li의 id가 같은지 비교하는 것
주의❌ valueLiTag.id 는 string이고 , toDo.id 는 number라서 비교가 안되기 때문에 parseInt로 문자-> 숫자로 바꿔준다

아래 화면에서 선택한 li가 삭제 되는지 확인해 보자

profile
다양한 변화와 도전하는 자세로

0개의 댓글