[Project] 에러 장인이 말아주는 자바스크립트 투두리스트 에러 모음집

2ㅣ2ㅣ·2023년 12월 3일

JavaScript

목록 보기
1/14

✌🏻 나만의 일정관리 브라우저 만들기 ✌🏻

브라우저 창 하나로 하루 일과를 정리하고 실행할 수 있으면 좋겠다고 생각해서 프로젝트를 만들어봤다. 신문같은 역할이라고 생각이 들어서 이름은 📄paper 로 정했다.
첫 프로젝트라서 간단하게 먼슬리투두, 투데이투두, 각 날짜를 표시하는 기능만 넣어 보았다.

🩸 간단한 기능 소개
1. form에 할일을 입력하고 엔터를 누르면 리스트가 등록된다.
2. 완료한 리스트는 체크버튼을 클릭해 빗금을 긋고 맨 아래로 정렬한다.
3. 체크 버튼을 한번 더 클릭시 빗금이 풀리고 다시 위로 정렬된다.
4. 글씨 부분을 더블클릭해 수정모드에 진입한다.
5. 수정이 끝나면 엔터키나 esc키를 눌러 빠져나온다.
6. 해당 날짜가 아닌 리스트들은 자동으로 삭제된다
(ex. 10월->11월, 1일->2일 로 바뀌는 동시에 삭제됨)

자세한 코드는 깃헙에서 확인해보고!
https://github.com/cseeeeee/paper
여기서는 프로젝트 동안 발생한 에러를 간단히 정리해보겠다.

📌 태그 여닫기 항상 확인하자

리스트를 추가했는데 난데없이 삭제되는 에러가 발생했다.
html 코드를 확인해보니 역시나 form 태그가 잘못된걸 발견.

수정전

<section id="todo-form">
    <form id="todo-form">
      <h3 class="todo_title">today todo-list</h3>
      <input type="text"
      id="input-month"
      placeholder="What are you going to do today?" required/>
  
  
  
  <ul id="todo-list"></ul>
  </form>
  </section>

수정후

<section id="todo-form">
    <form id="todo-form">
      <h3 class="todo_title">today todo-list</h3>
      <input type="text"
      id="input-month"
      placeholder="What are you going to do today?" required/>
    </form>
  <ul id="todo-list"></ul>
  </section>

📌 자바스크립트 전역변수는 파일이 달라도 공유된다

문제상황: 투데이 투두에 추가한 리스트가 먼슬리 투두에 추가되는 에러 발생
단, 새로고침시 원래대로 투데이투두에 추가됨..🤔

처음엔 렌더링 에러인줄 알았는데 알고보니 자바스크립트 파일 간 전역변수를 공유한다는 특성때문이라는걸 알게되었다.
에러를 해결하기 위해선 파일별로 함수명이나 고유키값을 각각 다르게 지정해주거나 스크립트 지정시 타입을 모듈로 바꿔야 하는데 일일이 수정하기 귀찮으니 스트립트 타입을 모듈로 바꿔버렸다.
이렇게하면 모듈별로 전역 스코프를 따로 쓸 수 있다.

<script type="module" src="todoDaily.mjs"></script>
<script type="module" src="todoMonthly.mjs"></script>

더 자세한 내용은 아래 링크 확인하기
https://helloworldjavascript.net/pages/293-module.html

📌 클래스를 동적으로 추가할때는 classList 사용하자

문제상황: 자바스크립트에서 클래스를 생성했는데 css에서 읽지 못하는 상황이 발생했다. 설상가상으로 브라우저 화면상에도 스타일이 적용이 되지 않았다.
이상한건 개발자 도구로 확인하면 지정되어있음.. 묘하다 묘해.


원래 의도대로라면 버튼이 주황색으로 바뀌어야 하지만 눈 비비고 봐도 깜장색임.
캐시 삭제, script순서 바꾸기 등 해볼 수 있는건 다 해보고 마지막이다 생각하고 classList로 바꿔봤더니 문제 해결

수정전

const button=document.createElement('button');
button.setAttribute('class','daily__check');
  

수정후

const button=document.createElement('button');
button.classList.add('daily__check');  

📌 브라우저에서도 잘 작동하는지 변수를 통해 확인하자

문제상황: 새로고침시 체크된 리스트들의 체크 스타일이 적용되지 않았다.
왜일까? 컴퓨터 사고로 생각해보면 단순명료하게 답을 알 수 있다.
브라우저는 체크가 되어있는지 무슨 스타일이 적용되는지 들은 바가 없으니까 ㅋ

우선, 리스트(li)들을 담은 객체 newtoDoObj 을 다음과 같이 세팅했다.
isChecked라는 변수를 통해 브라우저에게 체크 상태를 공지할 것이다.

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

isChecked:0 일때는 체크가 되지 않은 상태, isChecked:1 일때는 체크가 된 상태로 구분하여 로컬에서 해당 리스트 체크 유무를 확인하려고 한다.

아직 아무것도 체크되지 않았으므로 isChecked:0 임이 확인된다.

doneTodo함수에서 forEach함수를 이용해 체크되면 1 아니면 0으로 바꿔준다

if(li.style.color==='gray'){
    li.style.textDecoration='none';
    li.style.opacity=1;
    li.style.color='black';
      toDos.forEach( todo =>{
        if(todo.id == li.id){
          todo.isChecked=0;
        }
      })
    
  }
  else{
    li.style.textDecoration='line-through';
    li.style.color='gray';
    li.style.opacity=0.5;
    li.isChecked=0;
    toDos.forEach( todo =>{
      if(todo.id == li.id){
        todo.isChecked=1;
      }
    })
  }

체크되었을때(li.style.color='gray') isChecked가 1로 바뀌었음이 확인된다. 바로 밑에 있는 li는 체크되어있지 않으므로(li.style.color='black') isChecked가 0이다.

여기까지 하고 새로고침을 해보자.

로컬에서는 isChecked가 1인데 브라우저 화면에서는 isChecked가 0인 스타일이 적용되어있다. 왜일까? 🤔

코드의 흐름을 다시 보자. 브라우저가 새로고침시 가장 먼저 실행되는 코드가 무엇일까? 바로 이부분이다.

if(savedToDos){
  toDos= JSON.parse(savedToDos);
  if(toDos[0]){
    toDos.forEach( todo =>{
      paintToDo(todo)
    });
  }
}

savedToDos가 true라면, 즉 로컬에 setItem이 있다면 로컬에 있는 배열을 parse 시켜서 리스트를 하나씩 브라우저에 찍는 동작을 한다. 그 과정에서 가장 먼저 실행되는 함수가 paintToDo이다. 그렇다면 paintToDo 함수에도 isChecked 내용을 저장시켜주면 되겠다!

새로고침시에도 유지되려면 paint함수에 다음 코드를 추가해야 한다.

if(newTodo.isChecked){
    li.textDecoration='line-through';
    li.style.color='gray';
    li.style.opacity=0.5;
    li.isChecked=0;
  }

이제 새로고침해도 체크된 리스트들이 그대로 유지된다!
(명확한 비교를 위해 새로고침시 텍스트 데코를 없앴다)

📌 완료된 리스트들을 아래로 정렬하고 싶다

리스트를 아예 지우면 그날 뭘 했는지 잘 모를거같아서 빗금으로 처리해두려고 한다. 간단하게 그냥 리스트를 하나 더 만들어버렸다. 처음 리스트를 생성하면 todoList에 생성된다. 완료한 리스트 이름을 doneList라고 하고 isChecked가 1이면 doneList.appendChild하고 0이면 todoList.appendChild한다.

우선 html 코드에서 todo-list바로 밑에 done-list를 만들어준다.

  <ul id="todo-list"></ul>
  <ul id="done-list"></ul>

그다음 자스 코드에서 done-list를 불러오고

const doneList=document.querySelector("#done-list");

doneToDo함수에 다음 코드를 추가한다. li.remove를 하는 이유는 체크 된 li를 원래 있던 todoList->doneList로 이동해야 하기 때문이다. 즉 todoList에서 지워져야 한다. 한번 더 체크하면 doneList->todoList로 이동해야 하기 때문에 두 경우 다 remove 처리 해줬다.

if(li.style.color==='gray'){
    li.style.textDecoration='none';
    li.style.opacity=1;
    li.style.color='black';
      toDos.forEach( todo =>{
        if(todo.id == li.id){
          todo.isChecked=0;
        }
      })
      li.remove();
      toDoList.appendChild(li);
    
  }
  else{
    li.style.textDecoration='line-through';
    li.style.color='gray';
    li.style.opacity=0.5;
    toDos.forEach( todo =>{
      if(todo.id == li.id){
        todo.isChecked=1;
      }
    })
    li.remove();
    doneList.appendChild(li);
  }

체크된 li가 밑으로 정렬됐다!

📌 하루가 지나면 리스트를 초기화하고 싶다

문제 상황: 방법이 떠오르지 않음...ㅋㅋ
포인트는 현재 날짜와 li를 등록한 날짜를 각각 구한 후 둘을 비교해야할 것 같은데.
일단 이렇게 해봤다.
updateDate 함수를 정의하고 todo.id의 날짜와 currentDay를 뽑았다.

const currentDay=new Date().getDate();

function updateDate(todo){
  const todoDate=new Date(todo.id).getDate()
  console.log('todoDate: ',todoDate);
  console.log('currentDay: ',currentDay);
}

오 다행히 둘다 오늘날짜(11/21)로 잘 출력된다.

그렇다면 todoDate와 currentDay가 같지 않을때 배열을 초기화하고 저장하면 되겠다.

function updateDate(todo){
  const todoDate=new Date(todo.id).getDate()
  if(currentDay !== todoDate){
    toDos=[];
    saveToDos()
  }

여기서 끝이 아니지. 새로고침시 가장 먼저 실행되는건 누구라고?

if(savedToDos){
  toDos= JSON.parse(savedToDos);
  if(toDos[0]){
    toDos.forEach( todo =>{
      paintToDo(todo)
      updateDate(todo)
    });
  }
}

자 이제 하루가 지나면 배열이 초기화될 것이다.

📌 자바스크립트 엔진은 지역변수를 전역변수보다 우선으로 한다

상황: 투두데일리.mjs를 끝내고 투두먼슬리.mjs를 수정하던 중 투두를 추가할때마다 원래 로컬에 저장되어 있던 투두가 지워지는 에러 발생
참고로 해당 클립을 제외하고 모두 투두데일리 파일에서 발췌해온거다. 전체적인 동작을 동일하므로 굳이 구분할 필요 없다~!


tttt라는 새로운 투두를 입력하자 원래 있던 test가 없어졌다.

해당 클립은 아주 명확한 개념으로 정리가 가능하다.
정답은 바로 스코프체인 ❗️❗️
먼저 에러가 발생했던 코드를 보자

let toDosMonthly=[];

if(savedToDosMonthly){
  const toDosMonthly=JSON.parse(savedToDosMonthly);
  if(toDosMonthly[0]){
    toDosMonthly.forEach( todo =>{
      paintToDo(todo);
    })
  }
}

전역변수로 toDosMonthly를 선언해놓고 제어문 안에서 지역변수로, 그것도 const로 다시 선언했다. 콘솔창에 에러가 뜨지 않아 한참을 해맸다 😩

자바스크립트는 동일한 변수라도 전역변수보다 지역변수를 우선으로 채택(?)하기 때문에 파일 로드시 전역변수(const toDosMonthly)를 우선 채택한다. 그럼 원래 로컬에 저장되어 있던 투두들만 출력되어야 하는거 아닌가? 라고 오해할 수도 있다. 눈 똑바로 뜨고 이름을 보자. 지역변수다. 제어문 밖으로 나오지 못한다는 뜻이다.
그러므로 투두를 submit할때마다 새로 추가된(paint된) 리스트만 로컬에 저장되는 것이다.

지역변수의 const를 지워서 전역변수에 savedToDosMonthly를 parse시키자.

새로운 투두 kkkk를 입력해도 원래 있던 tttt가 없어지지 않는다. 문제 해결~!

마치며 🥹

이상으로 자바스크립트 개인 프로젝트의 오답노트까지 마무리했다.

느낀점
1. 포스팅은 미리 계획해두자 🐢
사실 처음 프로젝트를 진행할때는 포스팅을 할 계획이 없어서 식별자명을 나만 알아보게 짓는다는가, 코드를 깔끔하게 적지 않는 등 여러모로 지저분한 상황이었다. 프로젝트를 진행하면서 어.. 이거 오답노트를 써야겠는데? 해서 작성하게된게 해당 포스팅이다. 때문에 뒤늦게 포스팅을 해볼까? 생각했을땐 문제 상황의 정확한 흐름이나 화면(캡쳐, 움짤)이 충분하지 않은 상황이 발생했다. 포스팅 하는 내내 생각한거지만 포스팅은 미리 계획해두자
2. 공개여부에 관계없이 식별자명을 명확히 짓자 🫧
1번과 같은 상황에서 이어진 생각인데, 어차피 개발자에게 협업은 숙명이나 마찬가니 누가보든 안보든 깔끔히 적자. 여러 인강이나 블로그를 볼때마다 마주친 말이지만 역시 사람은 직접 겪어봐야 그 말을 진심으로 이해한다.
3. 모를땐 콘솔을 찍어보자 🌟 🌟 🌟
이번 프로젝트를 진행하면서 가장 크게 얻은 점이다. 클론코딩만 했을때는 오류가 발생하면 gpt나 구글링으로 오류를 해결했었다. 막상 백지에서 시작해보려니 생각지도 못한 곳에서 오류가 빈번히 발생했고 해결하기 위해선 코드 흐름을 파악하는게 우선이었다. 어떻게? 내가 확인하고 싶은 코드에 콘솔을 찍어서 작동 여부를 확인하는것이다. 프로젝트 막바지엔 에러가 나면 반갑기까지했다. 또 어디에 콘솔을 찍어볼까~ 하고(ㅋ 살짝 미쳐있었던거같기도하다)

사실 자바스크립트를 공부하는 개발준비생이라면 한번쯤은 만들어봤을법한 프로젝트이고 대단하지도 획기적이지도 않지만 처음으로 한땀한땀 무언가를 만들어 결과물을 냈다는것에 만족한다.

자 이제 시작이다 자바스크립트~! 내꿈을 위한 여행~..
자세한 코드가 궁금하신 분은 제 깃헙으로 모시겠습니다 🏃

profile
https://sususoo.tistory.com/

0개의 댓글