관리 메뉴

공부기록용

Javascript로 Todo List 만들기(3주) 본문

🗂️프로젝트🗂️

Javascript로 Todo List 만들기(3주)

과부하가즈아 2023. 10. 24. 16:50

🕐3주차 개발일지🕧

📌3주차 목표📌(목표수정!)

  • 코드 리팩토링
  • localStorage 연결 
  • todo의 상태 추가
    • 진행, 완료의 구분
    • 진행 🔄️ 완료의 전환
    • 진행, 완료의 todo css분류(1)

코드 리팩토링

todo의 삭제나 todo의 제목과 메모를 저장소에 저장하고 불러와서 사용하기 위한 코드 리팩토링 진행

// key 값
const TODOLIST = "todoList";
let todoList = [];
let id = 0;

// todo 값을 받아오기
function createTodo() {
  const titleInputVal = titleInput.value;
  const memoInputVal = memoInput.value;

  paintTodo(titleInputVal, memoInputVal);
  saveTodo(titleInputVal, memoInputVal);
}

// todo를 배열로 저장하기 위한
function saveTodo(titleInputVal, memoInputVal) {
  if (titleInputVal.trim() !== "" && memoInputVal.trim() !== "") {
    const todoObj = {
      id: (id += 1),
      title: titleInputVal,
      memo: memoInputVal,
    };
    todoList.push(todoObj);
  }
}
// 전체 todo 삭제
function delTodoAll() {
  const allTodoItem = document.querySelectorAll(".todo-item");

  // 주석으로 해도 있는걸로 인식해서 html을 수정했더니 원하는대로 작동!
  if (todoListBox.innerText === "") {
    alert("삭제할 목록이 없습니다.");
  } else {
    // console.log(allTodoItem);
    if (confirm("정말 삭제하시겠습니까?")) {
      allTodoItem.forEach((item) => {
        item.remove();
      });
      alert("삭제되었습니다.");
    }
  }
}
// todo를 웹 상에 나타내어 주기
function paintTodo(titleInputVal, memoInputVal) {
  // console.log(completedVal)//false

  // 날짜구하기
  let today = new Date();
  let year = today.getFullYear(); // 년도
  let month = today.getMonth() + 1; // 월
  let date = today.getDate(); // 날짜

  const divEl_item = document.createElement("div"); // todo-item
  const divEl_itemTop = document.createElement("div"); // todo-item-top
  const dateEl = document.createElement("div"); // date
  const btnEl = document.createElement("dutton"); // element-del
  const divEl_title = document.createElement("div"); // title
  const divEl_memo = document.createElement("div"); // memo

  if (titleInputVal === "" && memoInputVal === "") {
    alert("TODO를 입력하세요.");
  } else if (titleInputVal === "") {
    alert("할 일을 입력하세요.");
  } else if (memoInputVal === "") {
    alert("메모를 남겨주세요");
  } else {
    // item box
    divEl_item.classList.add("todo-item");
    todoListBox.appendChild(divEl_item);
    divEl_item.id = todoList.length + 1;

    // item bot top
    divEl_itemTop.classList.add("todo-item-top");
    divEl_item.appendChild(divEl_itemTop);

    // date
    dateEl.classList.add("todo-create-date");
    dateEl.innerText = `${year}-${month}-${date}`;
    divEl_itemTop.appendChild(dateEl);

    // trash btn
    btnEl.classList.add("element-delete");
    btnEl.textContent = "🗑️";
    divEl_itemTop.appendChild(btnEl);
    btnEl.addEventListener("click", () => {
      if (confirm("정말 삭제하시겠습니까?")) {
        todoListBox.removeChild(divEl_item);
        todoList = todoList.filter((todo) => todo.id !== Number(divEl_item.id));
        location.reload(); // 재부팅해서 id 다시 정렬
      }
    });

    // title
    divEl_title.classList.add("todo-title");
    divEl_title.innerText = titleInputVal;
    divEl_item.appendChild(divEl_title);

    // memo
    divEl_memo.classList.add("todo-memo");
    divEl_memo.innerText = memoInputVal;
    divEl_item.appendChild(divEl_memo);

    titleInput.value = "";
    memoInput.value = "";
  }
}
// inputBtn.addEventListener("click", createTodo)을 함수로 바꿈
function init() {
  inputBtn.addEventListener("click", createTodo);
}
init(); // 호출까지 해줘야 함_따로 부르는 곳이 없으니까
index.js(리팩토링)
const titleInput = document.querySelector(".todo-title-input");
const memoInput = document.querySelector(".todo-memo-input");
const inputBtn = document.querySelector(".todo-input-btn");

const todoListBox = document.querySelector(".todo-list");
const todoItemBox = document.querySelector(".todo-item");

const todoCount = document.querySelector(".todoCount");

// key 값
const TODOLIST = "todoList";
let todoList = [];
let id = 0;

// todo 값을 받아오기
function createTodo() {
  const titleInputVal = titleInput.value;
  const memoInputVal = memoInput.value;

  paintTodo(titleInputVal, memoInputVal);
  saveTodo(titleInputVal, memoInputVal);
}



// todo를 배열로 저장하기 위한
function saveTodo(titleInputVal, memoInputVal) {
  if (titleInputVal.trim() !== "" && memoInputVal.trim() !== "") {
    const todoObj = {
      id: (id += 1),
      title: titleInputVal,
      memo: memoInputVal,
    };
    todoList.push(todoObj);
  }
}

// 전체 todo 삭제
function delTodoAll() {
  const allTodoItem = document.querySelectorAll(".todo-item");

  // 주석으로 해도 있는걸로 인식해서 html을 수정했더니 원하는대로 작동!
  if (todoListBox.innerText === "") {
    alert("삭제할 목록이 없습니다.");
  } else {
    // console.log(allTodoItem);
    if (confirm("정말 삭제하시겠습니까?")) {
      allTodoItem.forEach((item) => {
        item.remove();
      });
      alert("삭제되었습니다.");
    }
  }
}

// todo를 웹 상에 나타내어 주기
function paintTodo(titleInputVal, memoInputVal) {
  // console.log(completedVal)//false

  // 날짜구하기
  let today = new Date();
  let year = today.getFullYear(); // 년도
  let month = today.getMonth() + 1; // 월
  let date = today.getDate(); // 날짜

  const divEl_item = document.createElement("div"); // todo-item
  const divEl_itemTop = document.createElement("div"); // todo-item-top
  const dateEl = document.createElement("div"); // date
  const btnEl = document.createElement("dutton"); // element-del
  const divEl_title = document.createElement("div"); // title
  const divEl_memo = document.createElement("div"); // memo

  if (titleInputVal === "" && memoInputVal === "") {
    alert("TODO를 입력하세요.");
  } else if (titleInputVal === "") {
    alert("할 일을 입력하세요.");
  } else if (memoInputVal === "") {
    alert("메모를 남겨주세요");
  } else {
    // item box
    divEl_item.classList.add("todo-item");
    todoListBox.appendChild(divEl_item);
    divEl_item.id = todoList.length + 1;

    // item bot top
    divEl_itemTop.classList.add("todo-item-top");
    divEl_item.appendChild(divEl_itemTop);

    // date
    dateEl.classList.add("todo-create-date");
    dateEl.innerText = `${year}-${month}-${date}`;
    divEl_itemTop.appendChild(dateEl);

    // trash btn
    btnEl.classList.add("element-delete");
    btnEl.textContent = "🗑️";
    divEl_itemTop.appendChild(btnEl);
    btnEl.addEventListener("click", () => {
      if (confirm("정말 삭제하시겠습니까?")) {
        todoListBox.removeChild(divEl_item);
        todoList = todoList.filter((todo) => todo.id !== Number(divEl_item.id));
        location.reload(); // 재부팅해서 id 다시 정렬
      }
    });

    // title
    divEl_title.classList.add("todo-title");
    divEl_title.innerText = titleInputVal;
    divEl_item.appendChild(divEl_title);

    // memo
    divEl_memo.classList.add("todo-memo");
    divEl_memo.innerText = memoInputVal;
    divEl_item.appendChild(divEl_memo);

    titleInput.value = "";
    memoInput.value = "";
  }
}

// inputBtn.addEventListener("click", createTodo)을 함수로 바꿈
function init() {
  inputBtn.addEventListener("click", createTodo);
}
init(); // 호출까지 해줘야 함_따로 부르는 곳이 없으니까

 


localStorage연결

localStorage연결하는 함수를 작성하여 저장이 필요한 데이터마다에 사용하고자 함

function saveTodoList() {
  localStorage.setItem(TODOLIST, JSON.stringify(todoList));
}

todo의 id랑 제목, 메모를 저장할 수 있도록 함

// todo를 localStorage에 저장
function saveTodo(titleInputVal, memoInputVal) {
  if (titleInputVal.trim() !== "" && memoInputVal.trim() !== "") {
    const todoObj = {
      id: (id += 1),
      title: titleInputVal,
      memo: memoInputVal,
    };
    todoList.push(todoObj);
    saveTodoList();
  }
}

function loadTodoList() {
  const loadedTodoList = localStorage.getItem(TODOLIST);
  if (loadedTodoList !== null) {
    const parsedTodoList = JSON.parse(loadedTodoList);

    for (let todo of parsedTodoList) {
      const title = todo.title;
      const memo = todo.memo;
      paintTodo(title, memo);
      saveTodo(title, memo);
    }
  }
}

todo의 단일 삭제와 전체 삭제시의 데이터 변화도 localSotrage에 반영하기

// trash btn
btnEl.classList.add("element-delete");
btnEl.textContent = "🗑️";
divEl_itemTop.appendChild(btnEl);
btnEl.addEventListener("click", () => {
  if (confirm("정말 삭제하시겠습니까?")) {
    todoListBox.removeChild(divEl_item);
    todoList = todoList.filter((todo) => todo.id !== Number(divEl_item.id));
    location.reload(); // 재부팅해서 id 다시 정렬
    saveTodoList();
  }
});
// 전체 todo 삭제
function delTodoAll() {
  const allTodoItem = document.querySelectorAll(".todo-item");

  // 주석으로 해도 있는걸로 인식해서 html을 수정했더니 원하는대로 작동!
  if (todoListBox.innerText === "") {
    alert("삭제할 목록이 없습니다.");
  } else {
    // console.log(allTodoItem);
    if (confirm("정말 삭제하시겠습니까?")) {
      allTodoItem.forEach((item) => {
        item.remove();
      });
      alert("삭제되었습니다.");
      localStorage.clear(); // 전체 삭제시 localStorage.clear()진행으로 아예 다 날리기
    }
  }
}
index.js(localStorage연결)
const titleInput = document.querySelector(".todo-title-input");
const memoInput = document.querySelector(".todo-memo-input");
const inputBtn = document.querySelector(".todo-input-btn");

const todoListBox = document.querySelector(".todo-list");
const todoItemBox = document.querySelector(".todo-item");

const todoCount = document.querySelector(".todoCount");

// key 값
const TODOLIST = "todoList";
let todoList = [];
let id = 0;

// todo 값을 받아오기
function createTodo() {
  const titleInputVal = titleInput.value;
  const memoInputVal = memoInput.value;

  paintTodo(titleInputVal, memoInputVal);
  saveTodo(titleInputVal, memoInputVal);
}

function saveTodoList() {
  localStorage.setItem(TODOLIST, JSON.stringify(todoList));
}

// todo를 localStorage에 저장
function saveTodo(titleInputVal, memoInputVal) {
  if (titleInputVal.trim() !== "" && memoInputVal.trim() !== "") {
    const todoObj = {
      id: (id += 1),
      title: titleInputVal,
      memo: memoInputVal,
    };
    todoList.push(todoObj);
    saveTodoList();
  }
}

function loadTodoList() {
  const loadedTodoList = localStorage.getItem(TODOLIST);
  if (loadedTodoList !== null) {
    const parsedTodoList = JSON.parse(loadedTodoList);

    for (let todo of parsedTodoList) {
      const title = todo.title;
      const memo = todo.memo;
      paintTodo(title, memo);
      saveTodo(title, memo);
    }
  }
}

// 전체 todo 삭제
function delTodoAll() {
  const allTodoItem = document.querySelectorAll(".todo-item");

  // 주석으로 해도 있는걸로 인식해서 html을 수정했더니 원하는대로 작동!
  if (todoListBox.innerText === "") {
    alert("삭제할 목록이 없습니다.");
  } else {
    // console.log(allTodoItem);
    if (confirm("정말 삭제하시겠습니까?")) {
      allTodoItem.forEach((item) => {
        item.remove();
      });
      alert("삭제되었습니다.");
      localStorage.clear();
      todoCount.innerText = "진행 중인 🥕: 0";
    }
  }
}

// todo를 웹 상에 나타내어 주기
function paintTodo(titleInputVal, memoInputVal) {
  // console.log(completedVal)//false

  // 날짜구하기
  let today = new Date();
  let year = today.getFullYear(); // 년도
  let month = today.getMonth() + 1; // 월
  let date = today.getDate(); // 날짜

  const divEl_item = document.createElement("div"); // todo-item
  const divEl_itemTop = document.createElement("div"); // todo-item-top
  const dateEl = document.createElement("div"); // date
  const btnEl = document.createElement("dutton"); // element-del
  const divEl_title = document.createElement("div"); // title
  const divEl_memo = document.createElement("div"); // memo

  if (titleInputVal === "" && memoInputVal === "") {
    alert("TODO를 입력하세요.");
  } else if (titleInputVal === "") {
    alert("할 일을 입력하세요.");
  } else if (memoInputVal === "") {
    alert("메모를 남겨주세요");
  } else {
    // item box
    divEl_item.classList.add("todo-item");
    todoListBox.appendChild(divEl_item);
    divEl_item.id = todoList.length + 1;

    // item bot top
    divEl_itemTop.classList.add("todo-item-top");
    divEl_item.appendChild(divEl_itemTop);

    // date
    dateEl.classList.add("todo-create-date");
    dateEl.innerText = `${year}-${month}-${date}`;
    divEl_itemTop.appendChild(dateEl);

    // trash btn
    btnEl.classList.add("element-delete");
    btnEl.textContent = "🗑️";
    divEl_itemTop.appendChild(btnEl);
    btnEl.addEventListener("click", () => {
      if (confirm("정말 삭제하시겠습니까?")) {
        todoListBox.removeChild(divEl_item);
        todoList = todoList.filter((todo) => todo.id !== Number(divEl_item.id));
        location.reload(); // 재부팅해서 id 다시 정렬
        saveTodoList();
      }
    });

    // title
    divEl_title.classList.add("todo-title");
    divEl_title.innerText = titleInputVal;
    divEl_item.appendChild(divEl_title);

    // memo
    divEl_memo.classList.add("todo-memo");
    divEl_memo.innerText = memoInputVal;
    divEl_item.appendChild(divEl_memo);

    titleInput.value = "";
    memoInput.value = "";
  }
}

// inputBtn.addEventListener("click", createTodo)을 함수로 바꿈
function init() {
  loadTodoList(); // local Storage에 todo가 존재하는지 확인
  inputBtn.addEventListener("click", createTodo);
}
init(); // 호출까지 해줘야 함_따로 부르는 곳이 없으니까


todo의 상태추가

todo의 진행과 완료의 위한 값 추가하기

completedVal = false;를 기본 값으로 false인 경우 진행 중인 todo로 설정하도록 함
// todo 값을 받아오기
function createTodo() {
  const titleInputVal = titleInput.value;
  const memoInputVal = memoInput.value;
  const completedVal = false;

  paintTodo(titleInputVal, memoInputVal, completedVal);
  saveTodo(titleInputVal, memoInputVal, completedVal);
}

function saveTodoList() {
  localStorage.setItem(TODOLIST, JSON.stringify(todoList));
}

// todo를 localStorage에 저장
function saveTodo(titleInputVal, memoInputVal, completedVal) {
  if (titleInputVal.trim() !== "" && memoInputVal.trim() !== "") {
    const todoObj = {
      id: (id += 1),
      completed: completedVal, // false_진행중, true_완료
      title: titleInputVal,
      memo: memoInputVal,
    };
    todoList.push(todoObj);
    saveTodoList();
  }
}

function loadTodoList() {
  const loadedTodoList = localStorage.getItem(TODOLIST);
  if (loadedTodoList !== null) {
    const parsedTodoList = JSON.parse(loadedTodoList);

    for (let todo of parsedTodoList) {
      const title = todo.title;
      const memo = todo.memo;
      const complete = todo.completed;
      paintTodo(title, memo, complete);
      saveTodo(title, memo, complete);
    }
  }
}

todo의 진행괴 완료의 상태를 구분을 위한 CSS와 todo에 이벤트 추가시 해당 CSS가 적용되도록

false(진행중): 당근 색으로의 CSS / true(완료): 제목 줄그음과 검은 색으로의 CSS전환
// todo 더블 클릭시 상태전환
divEl_item.addEventListener("dblclick", () => {
  // console.log(completedVal);
  completedVal = !completedVal;
  if (completedVal === false) {
    divEl_item.style.border = "3px solid rgb(221, 111, 8)";
    divEl_title.style.border = "2px solid rgb(129, 189, 137)";
    divEl_title.style.textDecoration = "none";
    divEl_memo.style.border = "2px solid rgb(129, 189, 137)";
  } else if (completedVal === true) {
    divEl_item.style.border = "2px solid black";
    divEl_title.style.border = "2px solid black";
    divEl_title.style.textDecoration = "line-through";
    divEl_memo.style.border = "2px solid black";
  }
});

✍️회고✍️

원하는 기능들을 구현하기 위해서 코드 리팩토링을 진행했다ㅎ js를 싹 지우고 다시 작성을 했다. 근본적인 이유로는 작성한 날짜 순으로 todo를 정렬하고 싶었는데 페이지가 새로 로딩 되면서 저장한 todo들이 날아가게 되면서 해당 기능을 구현하기 위해서는 작성한 todo가 남아있어야 됬기 때문이다. 덕분에 localStorage의 get이나 set에 대한 걸 숙지하게 되는 시간이었다.

리팩토링을 진행하면서 todo의 진행과 완료의 구분도 추가로 작성했다. 구분을 CSS의 변화로 주고 싶었고 해당 부분까지 작성했으나 이 역시 페이지가 새로고침하면 바뀐 CSS가 사라져서 localStorage에 저장하고 스타일에 관한 함수도 새로 만들고 적용될 진행과 완료의 CSS도 함수로 따로 구분지어서 모든 기능단위가 함수로 진행되는 경험을 했다. 이것이 바로,, 함수형 프로그래밍인가 싶었다. 확실히 분류를 해두니 이곳저곳에 적용(?)하기가 한결 수월했어서 이래서 함수형 프로그래밍~ 하는구나 몸소 체험했다. 또 매개변수를 불러오는 부분도 조금 더 익숙해진것 같았다.

📌4주차 목표📌

  • Todo의 상태 추가
    • 진행 중인 todo 카운팅
    • 진행, 완료의 todo css분류(2)
      • 각 사항을 localStorage에 저장
  • 반응형 웹으로 전환
index.js
const titleInput = document.querySelector(".todo-title-input");
const memoInput = document.querySelector(".todo-memo-input");
const inputBtn = document.querySelector(".todo-input-btn");

const todoListBox = document.querySelector(".todo-list");
const todoItemBox = document.querySelector(".todo-item");

const todoCount = document.querySelector(".todoCount");

// key 값
const TODOLIST = "todoList";
let todoList = [];
let id = 0;

// todo 값을 받아오기
function createTodo() {
  const titleInputVal = titleInput.value;
  const memoInputVal = memoInput.value;
  const completedVal = false;

  paintTodo(titleInputVal, memoInputVal, completedVal);
  saveTodo(titleInputVal, memoInputVal, completedVal);
}

function saveTodoList() {
  localStorage.setItem(TODOLIST, JSON.stringify(todoList));
}

// todo를 localStorage에 저장
function saveTodo(titleInputVal, memoInputVal, completedVal) {
  if (titleInputVal.trim() !== "" && memoInputVal.trim() !== "") {
    const todoObj = {
      id: (id += 1),
      completed: completedVal, // false_진행중, true_완료
      title: titleInputVal,
      memo: memoInputVal,
    };
    todoList.push(todoObj);
    saveTodoList();
  }
}

function loadTodoList() {
  const loadedTodoList = localStorage.getItem(TODOLIST);
  if (loadedTodoList !== null) {
    const parsedTodoList = JSON.parse(loadedTodoList);

    for (let todo of parsedTodoList) {
      const title = todo.title;
      const memo = todo.memo;
      const complete = todo.completed;
      paintTodo(title, memo, complete);
      saveTodo(title, memo, complete);
    }
  }
}

// 전체 todo 삭제
function delTodoAll() {
  const allTodoItem = document.querySelectorAll(".todo-item");

  // 주석으로 해도 있는걸로 인식해서 html을 수정했더니 원하는대로 작동!
  if (todoListBox.innerText === "") {
    alert("삭제할 목록이 없습니다.");
  } else {
    // console.log(allTodoItem);
    if (confirm("정말 삭제하시겠습니까?")) {
      allTodoItem.forEach((item) => {
        item.remove();
      });
      alert("삭제되었습니다.");
      localStorage.clear();
      todoCount.innerText = "진행 중인 🥕: 0";
    }
  }
}

// todo를 웹 상에 나타내어 주기
function paintTodo(titleInputVal, memoInputVal, completedVal) {
  // console.log(completedVal)//false

  // 날짜구하기
  let today = new Date();
  let year = today.getFullYear(); // 년도
  let month = today.getMonth() + 1; // 월
  let date = today.getDate(); // 날짜

  const divEl_item = document.createElement("div"); // todo-item
  const divEl_itemTop = document.createElement("div"); // todo-item-top
  const dateEl = document.createElement("div"); // date
  const btnEl = document.createElement("dutton"); // element-del
  const divEl_title = document.createElement("div"); // title
  const divEl_memo = document.createElement("div"); // memo

  if (titleInputVal === "" && memoInputVal === "") {
    alert("TODO를 입력하세요.");
  } else if (titleInputVal === "") {
    alert("할 일을 입력하세요.");
  } else if (memoInputVal === "") {
    alert("메모를 남겨주세요");
  } else {
    // item box
    divEl_item.classList.add("todo-item");
    todoListBox.appendChild(divEl_item);
    divEl_item.id = todoList.length + 1;

    // todo 더블 클릭시 상태전환
    divEl_item.addEventListener("dblclick", () => {
      // console.log(completedVal);
      completedVal = !completedVal;
      if (completedVal === false) {
        divEl_item.style.border = "3px solid rgb(221, 111, 8)";
        divEl_title.style.border = "2px solid rgb(129, 189, 137)";
        divEl_title.style.textDecoration = "none";
        divEl_memo.style.border = "2px solid rgb(129, 189, 137)";
      } else if (completedVal === true) {
        divEl_item.style.border = "2px solid black";
        divEl_title.style.border = "2px solid black";
        divEl_title.style.textDecoration = "line-through";
        divEl_memo.style.border = "2px solid black";
      }
    });

    // item bot top
    divEl_itemTop.classList.add("todo-item-top");
    divEl_item.appendChild(divEl_itemTop);

    // date
    dateEl.classList.add("todo-create-date");
    dateEl.innerText = `${year}-${month}-${date}`;
    divEl_itemTop.appendChild(dateEl);

    // trash btn
    btnEl.classList.add("element-delete");
    btnEl.textContent = "🗑️";
    divEl_itemTop.appendChild(btnEl);
    btnEl.addEventListener("click", () => {
      if (confirm("정말 삭제하시겠습니까?")) {
        todoListBox.removeChild(divEl_item);
        todoList = todoList.filter((todo) => todo.id !== Number(divEl_item.id));
        location.reload(); // 재부팅해서 id 다시 정렬
        saveTodoList();
      }
    });

    // title
    divEl_title.classList.add("todo-title");
    divEl_title.innerText = titleInputVal;
    divEl_item.appendChild(divEl_title);

    // memo
    divEl_memo.classList.add("todo-memo");
    divEl_memo.innerText = memoInputVal;
    divEl_item.appendChild(divEl_memo);

    titleInput.value = "";
    memoInput.value = "";
  }
}

// inputBtn.addEventListener("click", createTodo)을 함수로 바꿈
function init() {
  loadTodoList(); // local Storage에 todo가 존재하는지 확인
  inputBtn.addEventListener("click", createTodo);
}
init(); // 호출까지 해줘야 함_따로 부르는 곳이 없으니까
Comments