[프로젝트] 바닐라 JS로 크롬 앱 만들기 7 To-Do-List 구현

이하영·2023년 8월 11일
0

학습(프로젝트)

목록 보기
7/12
post-thumbnail

노마드 코더 - 바닐라 JS로 크롬 앱 만들기 강의

이제 To-Do-List를 구현해보자.

강의 #7.0 Setup

학습 날짜 : 23.08.11

To-Do-List를 구현하려면 먼저 form이 필요하다. 그리고 리스트를 만들어준다. 이때 ul 태그만 HTML로 작성하고 자바스크립트로 li를 추가할 것이다.

<form id="todo-form">
    <input type="text" placeholder="할일을 작성하고 눌러주세요.">
</form>
<ul id="todo-list"></ul>

form은 기본적으로 submit 이벤트를 갖고 있다. 함수를 만들고 그 이벤트의 기본 동작인 submit를 막을 것이다.

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

function handleToDoSubmit(event){
    event.preventDefault();
}

toDoForm.addEventListener("submit", handleToDoSubmit);

input의 value 값을 얻고, 입력을 하면(Enter를 누르면) 입력한 것을 비워야 한다. 그런데 비우기 전에 value 값을 저장줘야 한다.

function handleToDoSubmit(event){
    event.preventDefault();
    const newTodo = toDoInput.value;
    toDoInput.value="";
}
  • toDoInput.value를 비웠다고 해서 newToDo가 비워지는 것은 아니다.
    (input의 value를 새로운 변수에 복사하여 저장했기 때문)

강의 #7.1 Adding ToDos

학습 날짜 : 23.08.11

To-Do-List를 화면에 추가해보자.

function paintToDo(newTodo){
  
}

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

toDoForm.addEventListener("submit", handleToDoSubmit);

toDo를 그리는 함수를 만들고, handleToDoSubmit 함수에서 호출하고 newTodo를 paintToDo 함수에 보낸다.(input에서 value를 얻어서 paintToDo 함수에 넘긴다.)

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

paintToDo 함수에서는 HTML ul태그 안에 li를 만들어 줄 것이다.

나중에 버튼을 넣을 것이기 때문에 li 안에 span을 이용해 newTodo를 넣어준다.

그 다음에 li를 toDoList에 추가해준다. toDoList.appendChild(li);


강의 #7.2 Deleting To Dos

학습 날짜 : 23.08.11

To-Do-List에 toDo를 삭제하는 버튼을 만들어보자.

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);
}

사용자가 누르면 텍스트가 삭제되는 기능이 있다는 것을 인식시키기 위해 ❌ 버튼을 만들어주고, 버튼을 li에 넣어준다.

이모지 단축키

윈도우에서는 윈도우키 + . 세개를 같이 눌러준다.
Mac에서는 control cmd space 세개를 같이 눌러준다.

버튼에 클릭 이벤트가 발생했을 때 삭제 기능을 담당하는 함수가 실행되도록 구현한다.

function deleteToDo(event){
    const li = event.target.parentElement;
    li.remove();
}
  • button에 eventListener를 추가한다.
    이때, 여러 버튼이 있을 경우 어떤 버튼을 클릭하고 어떤 li를 삭제해야하는지 모른다.
  • event의 property 중 target을 이용히면 해당 button을 알 수 있고 그 버튼의 parentElement가 그 버튼이 속해있는 li를 가리킨다.
    event.target.parentElement
    • target : 클릭된 HTML 요소
    • parentElement : 해당 요소의 부모
  • 버튼이 속해있는 li를 삭제한다.

강의 #7.3 Saving To Dos

학습 날짜 : 23.08.11

이번에는 실제로 toDo들을 저장해보자.

먼저 toDo들을 localStorage에 저장한다.

const toDos = [];

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

function handleToDoSubmit(event){
    event.preventDefault();
    const newTodo = toDoInput.value;
    toDoInput.value="";
    toDos.push(newTodo);
    paintToDo(newTodo);
    saveToDos();
}
  • toDos 배열을 만든다.
    newTodo가 그려질 때마다 그 텍스트를 배열에 push 한다.
    toDos.push(newTodo);
  • 배열의 요소들을 각각 localStorage에 저장한다.
    localStorage에는 String만 저장 가능하다!
    하지만 toDoList를 배열로 저장하고 싶다.
    localStorage.setItem("todos", JSON.stringify(toDos))

JSON.stringify()

  • 자바스크립트 object나 array 등을 String으로 만들어준다.

강의 #7.4 Loading To Dos part One

학습 날짜 : 23.08.12

toDos가 localStorage에는 저장되어 있지만 아직 화면에는 나타나진 않는다. 이번에는 화면에 나타내보자.

localStorage에는 String 타입만 저장할 수 있기 때문에 JSON.stringify로 배열처럼 생긴 String을 저장한다.
localStorage.setItem("todos", JSON.stringify(toDos))

그리고 다시 JSON.parse를 이용해 String 타입을 object로 바꿔준다.
JSON.parse(localStorage.getItem("todos"))

JSON.parse()

  • 자바스크립트가 이해할 수 있는 array로 만들어준다.

parsedToDos가 배열이라서 forEach를 이용할 수 있다.

forEach(실행할 함수)

  • array에 있는 각각의 모든 item에 대해 function을 실행해준다.

function 작성 방법(Arrow function)

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

//Arrow function
(item) => console.log("this is the turn of ", item)

위의 두 코드는 같은 코드이다.


강의 #7.5 Loading To Dos part Two

학습 날짜 : 23.08.12

이제 parsedToDos 배열에 있는 item들을 화면에 그려보자.

전에 todos를 화면에 그리는 paintToDo 함수를 만들어뒀기 때문에 우리는 forEach 함수에 paintToDo만 넣어주면 쉽게 구현할 수 있다.

const TODOS_KEY = "todos";

const toDos = [];

function saveToDos(){
    localStorage.setItem(TODOS_KEY, JSON.stringify(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);
}

const savedToDos = localStorage.getItem(TODOS_KEY);

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

하지만, application이 시작될 때마다 toDos 배열은 항상 비어있기 때문에
const toDos = [];

newToDo를 작성하고 submit를 할 때마다 localStorage에 새로운 것만 저장된다.(기존에 저장되어 있던 list는 없어짐)

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

newTodo만 toDos 배열(빈 배열)에 push하게 된다.
toDos.push(newTodo);

그리고 그 toDo를 저장할 때 새로운 toDo들만 포함된 배열을 저장하고 있다.

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

이전 것과 새로운 것을 모두 유지하고 저장해야 한다.

그러기 위해서는 application이 시작될 때 toDos 배열을 빈 값으로 시작하는 것이 아니라,

  1. 기존 const로 선언했던 것을 let으로 수정해 업데이트가 가능하도록 만들고
    let toDos = [];

  2. localStorage에 toDo가 있으면 toDos에 parsedToDos를 넣어서 전에 있던 toDo를 복원하면 된다. toDos = parsedToDos;

    let toDos = [];

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

강의 #7.6 Deleting To Dos part One

학습 날짜 : 23.08.13

toDoList에 삭제하는 버튼을 누르면 화면에서는 사라지지만 localStorage에는 그대로 남아있는 문제가 있다. 이번에는 그걸 해결해보자.

자바스크립트와 HTML 입장에서는 어떤 HTML의 요소를 지워야 하는지 알고 있지만, 어떤 toDo를 localStorage에서 지워야 하는지는 모른다.
어떤 item을 지워야 하는지 알아야 한다.

이번 강의에서는 toDo들에게 랜덤한 id를 설정해주어 각 li를 구별할 수 있도록 할 것이다.

  1. Date.now()를 이용해 id 값을 설정해준다.
    Date.now()는 밀리초를 주는 함수인데, 랜덤처럼 보인다. 이 함수를 사용해 랜덤 숫자를 받아서 id 값을 설정해주는 것이다.

  2. object를 localStorage에 저장하도록 수정한다.
    toDos.push(newTodo); 매번 사용자가 적은 text를 push하고 있다.
    text 대신 object를 push할 수 있도록 수정한다.

    const newTodoObj = {
        text:newTodo,
        id:Date.now(),
    };
    toDos.push(newTodoObj);

  1. 화면에 표시하는 paintToDo 함수도 수정한다.
  • paintToDo 함수도 object인 newTodoObj를 받아 사용하도록 수정
    paintToDo(newTodo);paintToDo(newTodoObj);

  • newTodoObj는 text와 id를 가지고 있다. 텍스트를 출력할 때는 newTodo의 text 값을 가져와야 한다.
    span.innerText = newTodo;span.innerText = newTodo.text;

  • newTodoObj가 가지고 있는 id로 각 li item을 구별할 수 있다. li에 id 값을 설정해준다.

    const li = document.createElement("li");
    li.id = newTodo.id;


강의 #7.7 Deleting To Dos part Two

학습 날짜 : 23.08.13

array에서 요소를 삭제하는 방법을 알아보자.

parsedToDos.forEach(paintToDo);

  • forEach 함수는 paintToDo 함수를 parsedToDos 배열의 요소마다 실행한다.
    → forEach는 paintToDo를 기본적으로 실행함
    → forEach는 각각의 item을 주고, 그 item은 object이다.

array에서 뭔가를 삭제할 때 실제로 array에서 지우는 것이 아니다.
실제로 일어나는 일은 지우고 싶은 item을 빼고 새로운 array를 만드는 것이다.(item을 지우는 것이 아니라 제외한다고 생각하면 된다.)

  • filter 함수가 필요하다.

filter 함수

  • 배열의 요소를 순차적으로 순회하면서 조건에 일치하는(true) 요소를 모아 새로운 배열을 반환한다.
  • arr.filter(callback(element, index, array), thisArg)
const arr = [1234, 5454, 223, 122, 45, 7655, 353];
function filterFunction(array){ return array <= 1000 };

arr.filter(filterFunction);
결과 : [223, 122, 45, 353]

강의 #7.8 Deleting To Dos part Three

학습 날짜 : 23.08.13

삭제 버튼을 누른 li의 id를 받고, 이 id를 제외하고 새로운 array를 만든다. 그리고 toDos array를 업데이트 한다.

toDos = toDos.filter(toDo => toDo.id !== li.id);

  • 버튼을 클릭했던 li의 id를 갖고 있는 toDo를 제외하고, id가 다른 toDo를 남긴다.
function deleteToDo(event){
    const li = event.target.parentElement;
    toDos = toDos.filter(toDo => toDo.id !== li.id);
    li.remove();
}

이때, li.id의 타입은 String이고 toDo.id는 number 타입이어서 정상적으로 작동하지 않는다.
parseInt()를 사용해 String 타입을 number 타입으로 수정해준다.
toDos = toDos.filter(toDo => toDo.id !== parseInt(li.id));

그리고 삭제를 하고 나서 saveToDos 함수를 실행해주어야 한다.
(삭제한 것을 저장하기 위해)

function deleteToDo(event){
    const li = event.target.parentElement;
    li.remove();
    toDos = toDos.filter(toDo => toDo.id !== parseInt(li.id));
    saveToDos();
}
profile
안녕하세요, 웹 개발자 이하영입니다!

0개의 댓글