Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2주차 기본/심화 과제] WEB 💛 TO DO MATE 🌈 없애보자!!!!! #5

Merged
merged 24 commits into from
May 4, 2023

Conversation

simeunseo
Copy link
Member

@simeunseo simeunseo commented Apr 20, 2023

🔗 구현 페이지

위 배포 페이지는 SPA가 아닙니다! vite로 만든 SPA도 github pages로 배포할 수 있는 방법이 따로 있던데 시간 관계상,,, 다음에 해보는 걸로! 😙

✨ 구현 기능 명세

  • 기본 과제

    • 할일 목록 데이터

      • 할일 목록 데이터를 상수파일에 저장해 사용
    • 하트 안의 숫자

      • 미완료한 할일의 갯수를 계산하여 보여줌
      • 할 일 완료 클릭시 갯수가 줄어듦
    • 카테고리별 할일 추가

      • 카테고리별 할일 추가 기능
      • 모달로 구현
    • 달력 / MY 버튼 클릭시 페이지 이동

      • 달력 -> href="/" (SPA 구현 전에는 "/home"으로 했어용.. 괜찮쥬?😓)
      • MY -> href="/mycategory"
  • 심화 과제

    • 카테고리별 할일 추가

      • 모달을 열면 input에 자동 포커스
      • 연달아 추가되며, 중복되는 할일 있을 시 alert하고 추가하지 않음
    • Vanilla JS로 SPA 구현하기 (요거 구현해놨었는데 PR에서 빠져있었네요ㅜㅜ 뒤늦게 추가)

      • 라우터 구현
      • 폴더구조 참고 + 응용
    • My 페이지 Drag & Drop

      • Drag&Drop으로 카테고리 순서 변경 가능
      • 새로고침해도 유지
      • 메인에도 반영

🌼 PR Point

  • JS to SPA

    먼저 1주차처럼 기본 html, css, js로만 구현한게 week2 폴더에 있고, 이걸 vite를 이용해서 SPA로 바꾼 것은 week2-SPA 폴더에 있습니다!

  • 데이터 구성

    1번 과제와는 다르게 상수 데이터를 어떤 식으로 구성할 것인가에 대해서도 굉장히 고민이 많아지더라구요... 이걸 카테고리 별로 나눠야할지? 아니면 할 일 목록별로 나눠야할지? 그리고 카테고리 박스 별 색상을 상수 데이터 안에 넣어놓을 것인가에 대해서도 중간에 변동이 있었습니다. 고민끝에 저는 이러한 구조로 데이터를 작성했습니다.

    {
      category: "과제",
      list: [
        {
          content: "db 프로젝트",
          done: true,
        },
        {
          content: "pl 과제",
          done: true,
        },
      ],
    },
    {
      category: "공부",
      list: [
        {
          content: "이산구조 복습",
          done: false,
        },
        {
          content: "react 강의",
          done: true,
        },
      ],
    },
    ...
  • 함수 구성

    • home은 크게 6가지 함수로 동작합니다.

      1. checkDone() : 할일 클릭을 감지하고 처리함
      2. todoCount() : 미완료 할일의 수를 화면에 표시함 (완료 버튼 클릭, 새항목 추가 등 미완료 할일의 수가 변경되는 지점마다 호출됨)
      3. listToTodo(list) : list를 탐색하면서 요소를 하나씩 Todo로 만들어서 화면에 그림
      4. listToTodoList(list, categoryName) : listToTodo()에서 호출되는 함수로, 상수 데이터에서 두번째 depth에 있는 할일 목록(list)를 다룸
      5. checkModal() : modal의 열고 닫힘을 관리하는 함수
      6. resolveModal() : modal안에서 일어나는 폼 제출(할일 추가)를 관리하는 함수
    • mycategory는 크게 6가지 함수로 동작합니다.

      1. makeCategories(list) : list(카테고리 목록 리스트)를 탐색하면서 하나씩 카테고리 박스로 만들어서 화면에 그림
      2. extractCategoryName(list) : list(전체 할일 목록 데이터)를 탐색하면서 카테고리 이름만을 골라내어 새로운 리스트로 만들어 반환함
      3. resolveDragDrop(item, parent) : 드래그앤드롭 기능을 처리함
      4. arrayInsertAfter(array, target, reference) : array에서 target value를 reference value의 바로 뒤로 순서를 옮기는 함수 - resolveDragDrop()에서 사용
      5. arrayInsertBefore(array, target, reference) : array에서 target value를 reference value의 바로 앞으로 순서를 옮기는 함수 - resolveDragDrop()에서 사용
      6. changeLocalStorage(list) : 변경된 categoryList(순서)에 따라 전체 데이터를 재배치하여 localStorage에 반영하는 함수
  • 새로고침 없이 작동하도록 하기

    과제 시뮬 영상 보니까 모든 기능 작동이 새로고침 없이 진행되길래 저도 할일추가 할 때 preventDefault로 새로고침을 방지하고 새로고침이 없어도 바로바로 반영이 되도록 구현했습니다! 그게 진짜진짜 어렵더라구요 8ㅅ8


🥺 소요 시간, 어려웠던 점

  • 15h+ ??? ㅠㅠ
  • 할일 추가 모달 로직

    처음에 모달의 열고 닫힘을 감지하고 어떤 카테고리를 클릭해서 열린건지를 알아내서 할일을 추가하는 것 까지를 하나의 함수에서 모두 처리했었는데요! 이렇게 하니까 한번 모달이 작동되고나서 또 계속해서 작동이 되려면 (즉, 할일추가 + 버튼이 눌림을 addEventListner로 감지하려면) 또다시 이 함수를 호출해야 했어요. 그런데 그렇게 하니까 recursion이 일어나서 아주 엉망이 된거예요... 진짜 이거 고치는데만 반나절 쓴 것 같아요. 해결 방법은 바로 '모달의 열고 닫힘을 관리하는 함수'와 '모달 안에서 일어나는 일을 관리하는 함수'를 분리하는 것이었습니다. 즉, 전자의 함수는 진짜 모달의 '열고 닫힘'에만 관여하는 거죠. 이게 말로 설명하려니 어려운데... 아무튼 함수를 기능별로 분류하는 것이 얼마나 중요한 일인지 알았습니다. 은빈이가 자스 클린 코드 관련 레포를 스타했길래 저도 읽어보니깐 (굉장히 유용한 아티클이더라구요!) 함수를 기능단위로 쪼개는게 중요하고, 또 그래야 함수 이름도 명확해지더라구요. 이번 기회로 확실히 배웠습니다.
  • Drag&Drop

    처음 해보는 기능이라 약간 막막했는데, 이 자료를 한번 따라하고 나서 진행하니 훨씬 수월했던 것 같아요! 근데 어려웠던 건, 이 드래그한 카테고리(draggedItem)를 드롭한 부분(dropZone)의 앞으로 이동하게 할 건지, 뒤로 이동하게 할 건지를 제가 짜야하는 부분이었어요. 그니까 원래 draggedItemdropZone보다 앞에 있던 거면 dropZone의 뒤에 붙여줘야 하고, 뒤에 있던 거면 dropZone의 앞에 붙여줘야 했어요. 이 사실을 깨닫고 일단 카테고리 목록을 순서대로 저장하는 list를 하나 만들고, 이동이 일어날 때마다 draggedItem에 해당하는 카테고리와 dropZone에 해당하는 카테고리의 인덱스를 확인해서 arrayInsertBefore(array, target, reference) 또는 arrayInsertAfter(array, target, reference) 둘 중 하나를 호출했습니다.
  • PR 관련...

    코드가 길어지니까 PR 남기기도 난감하네요... 다음 과제부터는 그냥 코드에 주석으로 설명을 남기면 좋을 것 같습니다!

🌈 구현 결과물

  • 미완료한 할일 수 반영

default.mov
  • 항목 추가

default.mov
  • 카테고리 순서 변경

default.mov

@simeunseo simeunseo changed the title [1주차 기본/심화 과제] WEB 💛 TO DO MATE 🌈 없애보자!!!!! [2주차 기본/심화 과제] WEB 💛 TO DO MATE 🌈 없애보자!!!!! Apr 20, 2023
@simeunseo simeunseo requested review from kwonET and borimong April 21, 2023 10:13
@simeunseo simeunseo self-assigned this Apr 21, 2023
@simeunseo simeunseo marked this pull request as ready for review April 21, 2023 10:20
@simeunseo simeunseo requested a review from ljh0608 April 22, 2023 06:53
Copy link

@kwonET kwonET left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와 은서야 정말 고생많았다 ~
특히 드래그 드롭이나 SPA로 구현한 부분은 배워갈 점이 너무 많았어!
SPA쪽은 폴더 구성이라도 좀 더 찬찬히 보려고.

함수 명세에 관한 부분/ 주석 관련한 부분에서도 다음 과제 때 참고해야할 정도로
코드리뷰하기 넘 좋았어!

최고 최고 갓은서

Comment on lines +8 to +9
localStorage.getItem("todo_data") === null &&
localStorage.setItem("todo_data", JSON.stringify(TODO_DATA)); //localStorage 초기화
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if문 대신 연산자 쓴 거 참고해야겠다 ㅎㅎ

Comment on lines +11 to +14
listToTodo(todoData);
checkDone();
todoCount();
checkModal();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 되면 윈도우가 로드될 때마다 위 함수가 실행되는 거 아닌가요?!
나는 로드될 때 데이터만 업데이트하는 식으로 했는데
이렇게 해준 이유가 궁금행

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

으음 윈도우가 로드될 때마다 로컬스토리지에서 값을 가져와서 화면에 뿌리고, 완료된 할일수 체크하고, 모달이 작동하게하는 저 일련의 함수들이 모두 실행되어야 작동이 되더라구... 데이터만 업데이트한다면 저 필요한 기능들이 작동을 하질 않는달까..? 사실 나도 사실 무지성 코딩하느라 그냥 돌아가게 하는데 혈안이었네 ㅠㅠ

Comment on lines +99 to +100
const addBtn = document.getElementsByClassName("add-btn");
const addBtnList = [...addBtn];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

querySelectorAll 대신 이걸 쓴 이유가 궁금합니다!
바로 배열로 만들어주려고 그런건가?

Copy link
Member Author

@simeunseo simeunseo May 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 안그래도 이 과제하면서 querySelectorAll과 getElementBy...의 차이점에 대해 알아봤었는데
이 글 보니까 일반적으로 후자가 더 빠르다는? 말에 이걸 쓴거같아~ 개인적으로 저 메소드가 습관이 되기도 했다

});
});

curModalFor || resolveModal(modal);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

연산자를 적재적소에 잘 쓴다 굳굳 참고해야겠어~~

Comment on lines +90 to +98
list.forEach((category) => {
let filteredItem = localStorageData.filter(
(item) => item.category === category
)[0];
newData.push({
category: filteredItem.category,
list: filteredItem.list,
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forEach와 filter를 정말 잘 활용한다 ~~
특히 로컬스토리지 데이터는 다루기가 좀 복잡하다고 느꼈는데,
가독성 좋게 짠 것 같아 ㅎㅎ
배워갑니다 굳

animation: bounce 1s ease infinite;
}

@keyframes bounce {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

바운시 바운시를 이렇게 구현했군!
귀여워 ㅋㅋ

Copy link

@borimong borimong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

두 번째 과제도 넘넘 수고많았어!!!!! :)

todoCount();
checkModal();
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 이렇게 정리해주니까 넘 깔끔하고 좋다!!!!😍🤩

Comment on lines +21 to +22
const doneBtn = document.getElementsByClassName("done-btn");
const doneBtnList = [...doneBtn]; //HTMLCollection to Array

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거는 const doneBtnList = Array.from(document.getElementsByClassName("done-btn")); 요렇게 쓰면 한 줄로 쓸 수 있을 것 같다!!! :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 맞아 array로 바꾸는 방법이 .from을 포함해서 여러가지가 있었는데 웬만하면 한줄에 끝내는게 더 깔끔하겠구나!

const doneBtn = document.getElementsByClassName("done-btn");
const doneBtnList = [...doneBtn]; //HTMLCollection to Array

doneBtnList.forEach((item) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기 반복 인자는 item 이라고 쓰기보다, doneBtnList 의 개별 요소를 뜻하는 doneBtn 으로 써주면 더 가독성 있는 코드가 좋을 것 같아!!! :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

호곡 그러게 그냥 item이 제일 무난해서 그렇게 매번 썼었는데 의미를 담는게 더 좋겠다!!!

localStorageData
.find((item) => item.category === changedCategory)
.list.find((item) => item.content === changedTodo).done = false;
item.attributes.done.value = "false";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기 setAttribute 를 통해 수정해주면 더 가독성 있을 것 같다!!! :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호!!! setAttribute기억할게!!! 돌아돌아 갔군..

Comment on lines +66 to +69
let todoContent = todoTemplate.cloneNode(true); //템플릿 복사
let todoNewHtml = todoContent.innerHTML; //템플릿 안의 html 복사

todoNewHtml = todoNewHtml //복사한 html에서 필요한 부분을 item 내용에 맞게 변경

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기서 둘 다 const 로 변경하구 todoNewHtml은 innerHTML.replace 로 붙여써주면 더 좋을 것 같다!! 그리구 newTodoContent newTodoHtml 로 변수명을 바꿔주는 건 혹시 어떻게 생각해?? ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 좀더 명확한 변수명@@ 조언 고마우

const form = document.getElementById("add-todo__form");
//form이 제출됐을 때
form.addEventListener("submit", (e) => {
e.preventDefault();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

완전 꼼꼼미!!!!

@simeunseo simeunseo merged commit 00336b1 into main-old May 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants