[Mini Project]TO DO LIST

hameee·2023년 1월 1일
1

Mini Project

목록 보기
1/1
post-thumbnail

후기

 작은 프로그램을 만들어보는 것이 큰 도움이 되었다는 누군가의 말과 "코드의 흐름을 잡기 어려운데 어떻게 공부해야 하죠?"라는 내 질문에 복잡한 파일보다는 간단한 파일을 보면서 분석해보라는 스터디원의 조언을 듣고 간단한 코드로 기초를 잡아야겠다는 생각을 해왔다. 미루고 미루다 '이러다가는 안 하겠는데?'라는 생각에 '파일이라도 만들어 놓으면 언젠가는 완성하겠지'라는 마음으로 시작한 프로젝트, 간단한 TO DO LIST이다. 내 앞에 '자, 이제 써봐!'라고 말하는 듯한 빈 파일에 1차 당황, 나와는 아직 어색한 사이인 JS에 2차 당황, 뜻하지 않게 마주한 오류에 3차 당황했지만, 구글과 스터디원의 도움으로 하나씩 해결하며 완성할 수 있었다. 너무 간단한 프로그램이라 아래 작성한 기술 스택이니 구현 기능이니 이런 단어가 부끄럽지만, 기록하지 않으면 기억에서 사라질 것이 분명하기에 TO DO LIST 프로젝트 회고를 작성한다.

깃허브: https://github.com/qwerty00ui88/TO-DO-LIST

배운 점

오류를 분석하고 넘어갈 것
❌지우고 다른 방법으로 코드를 짜봐야지!
✅이 방법을 사용하면 왜 오류나고, 해결하려면 어떻게 해야 할까?

프로젝트 설명

1.기술 스택

HTML, JavaScript, CSS

2.구현 기능

list 추가
list 삭제
새로고침 후에도 기존 데이터 보존

3.Error

-삭제 버튼 에러

1) 코드 설명

todo list 1개의 dom 생성 시 -> <i>에 삭제 이벤트 추가, <li>에 삭제 시 list를 특정하기 위한 id 부여

// <추가 버튼 클릭 시 실행되는 dom 변환 코드 내부(convertToDom)>
// <li> -> todo list 1개, <i> -> li 내부의 삭제 아이콘 
const convertToDom = (text) => {
  //...생략...
  i.addEventListener('click', deleteLi);
  li.setAttribute('id', data.length - 1);
  //...생략...
}

삭제 버튼 클릭 시 -> data에서 삭제, localstorage 배열에서 삭제, 기존 dom 삭제, 새로운 dom 생성

// <localstorage의 value 할당 코드>
let data = JSON.parse(localStorage.getItem('localData'))
  ? JSON.parse(localStorage.getItem('localData'))
  : [];

// <삭제 버튼 클릭 시 실행되는 이벤트 함수>
const deleteLi = e => {
  // data와 localstorage 배열에서 삭제
  let targetIndex = Number(e.target.parentElement.id);
  data.splice(targetIndex, 1);
  localStorage.setItem('localData', JSON.stringify(data));

  // 기존 dom 삭제
  while (ul.firstChild) {
    ul.removeChild(ul.firstChild);
  }
  // 새로운 dom 생성
  data.forEach(el => convertToDom(el));
};

2) 문제점

첫 번째 사진의 2를 삭제할 경우, 모든 <li>의 id가 2로 바뀜. 그래서 다음 요소 삭제 시, 어떤 x를 클릭하든 localstorage 배열에서 index가 2인 것이 삭제됨. (두 번째 사진에서 무엇을 누르던 마지막에 있는 4가 삭제됨.)

3) 문제 발생 이유

"dom 생성", "localstorage에 저장된 배열", "localstorage에 저장된 배열을 할당해준 data라는 변수"가 연동된다고 착각

// <id 생성 코드>
li.setAttribute('id', data.length - 1);// ★

// <삭제 버튼 클릭 시 실행되는 이벤트 함수>
const deleteLi = e => {
  // 삭제 전 data = [1, 2, 3, 4]
  // 삭제 전 localstorage 배열 = [1, 2, 3, 4]
  let targetIndex = Number(e.target.parentElement.id);// targetIndex = 1
  
  data.splice(targetIndex, 1);// 삭제 후 data = [1, 3, 4]
  localStorage.setItem('localData', JSON.stringify(data));// 삭제 후 localstorage 배열 = [1, 3, 4]

  // 기존 dom 삭제
  while (ul.firstChild) {
    ul.removeChild(ul.firstChild);
  }
  // 새로운 dom 생성
  // ★<li>의 id로 설정해주고 있는 'data.length - 1'은 무조건 2이므로 모든 id가 2로 설정됨
  data.forEach(el => convertToDom(el));
};

4) 해결

id를 고정된 값이 아닌 변수로 설정하여 todo list 1개의 dom이 생성될 때마다 변경되도록 함

// <추가 버튼 클릭 시 실행되는 dom 변환 코드 내부(convertToDom)>
const convertToDom = (text, index) => {
  //...생략...
  i.addEventListener('click', deleteLi);
  li.setAttribute('id', index);// ★id를 data의 길이(고정된 것)가 아닌 변수(변경 가능한 것)로 설정
  //...생략...
}
// <삭제 버튼 클릭 시 실행되는 이벤트 함수>
const deleteLi = e => {
  // data와 localStorage 배열에서 삭제
  let targetIndex = Number(e.target.parentElement.id);
  data.splice(targetIndex, 1);
  localStorage.setItem('localData', JSON.stringify(data));
  // 렌더링 함수 실행
  render();
};

// <렌더링 함수>
// dom 생성 시 id를 인자로 보내줌
const render = () => {
  while (ul.firstChild) {
    ul.removeChild(ul.firstChild);
  }
  // ★forEach의 2번째 매개변수 index는 현재 처리중인 요소의 인덱스를 받음
  data.forEach((el, index) => convertToDom(el, index));
};

변경 사항(2023.01.03)

1.체크 여부 저장

1) 변경 전

새로고침 시 list는 그대로지만 모두 unchecked 상태가 됨

2) 변경 후

-check 여부 저장하기 위해 localStorage에 저장되는 배열의 요소를 문자열에서 객체로 변경

// 변경 전
data.push(textarea.value);
// 변경 후
data.push({ text: textarea.value, check: false });

-checkbox에 data와 localStorage 업데이트하는 클릭 이벤트 추가

// Dom 생성 함수 내부
input.addEventListener('click', handleCheck);

// 체크박스 클릭 시 실행되는 함수
const handleCheck = (e) => {
  let targetIndex = Number(e.target.parentElement.id);
  data[targetIndex].check = e.target.checked;// data 변경
  localStorage.setItem('localData', JSON.stringify(data));// localStorage 변경
};

-새로고침 시 check된 상태이면 input 태그에 checked 속성 추가

// Dom 생성 함수 내부
check ? input.setAttribute('checked', '') : null;

2.input과 label 연결

1) 변경 전

체크박스를 스크린리더로 읽을 경우 label 내용을 읽어주지 않음

2) 변경 후

-<label><input type = "checkbox">를 for와 id로 연결

// 변경 전 스크린리더: "선택됨. 체크박스 선택됨."
// 변경 후 스크린리더: "선택됨. "label 안의 내용". 체크박스 선택됨."

// 아래 코드 추가
input.setAttribute('id', `checkbox_${index}`);
label.setAttribute('for', `checkbox_${index}`);

새롭게 알게 된 것

1.redner 함수가 특정 기능이 있는 것이 아님. DOM을 생성하고 append 해주는 함수의 이름을 내가 render라고 정한 것뿐임.

2.새로고침 후 바로 localStorage의 데이터가 화면에 렌더링 되게 하려면 렌더링해 주는 함수의 실행문을 작성해야 함

render();

3.event.target.parentElement
의미: 이벤트가 일어난 엘리먼트의 부모 엘리먼트
사용: x 버튼을 누르면 x 버튼이 속한 <li>가 삭제되는 기능 구현에 사용

4.<li> 삭제 이벤트는 <li> 생성 시에 주면 됨.

5.속성 삭제
removeAttribute()

6.HTML의 checked와 JS의 checked
1) HTML

// 현재 상태와 관련 없음
❌ 체크로 바꿔라!
✅ 처음 렌더링 시 체크된 상태로 렌더링하겠다!
<input type='checkbox' checked> 

2) JS

// 현재 상태와 관련 있음
✅ 현재 상태를 check/uncheck로 바꿔라!
체크박스 태그.checked = true;// Check
체크박스 태그.checked = false;// Uncheck

0개의 댓글