나의 첫번째 js 프로젝트 : 일정관리 웹앱 만들기

rlorxl·2022년 4월 26일
5

회고

목록 보기
2/2
post-thumbnail

기획의도 🖋

자바스크립트를 어느정도 익혔는지 점검하기 위해 누구나 거쳐간다는 투두리스트 만들기 노선에 탑승.🙋‍♀️ 사실 자바스크립트 공부를 처음 시작했을때부터 유투브 튜토리얼 같은걸 보면서 깔짝댓었는데 이해하는데 어려움이 있어서 끝까지 완성을 못한채 중간까지 하고 내버려둬야 했다. 이번에 이전에 인강과제를 하면서 조금 익혔던 달력만들기와 투두리스트를 합쳐서 나만의 일정관리 웹앱을 만들었다.

평소에 나는 달력에 일정을 쓰고 계획이 이행되면 색깔있는 펜으로 줄을 긋거나 앞에 체크표시를 하는걸 좋아하는 파워 계획형 인간이기 때문에 달력에 그날그날의 성취도가 색깔로 표시되고 원하는 색으로 달력이 꽉 채워진다면 매우 뿌듯하고 좋을 것 같았다.


주요기능 & 목표 💡

  • 캘린더 기반의 기록 앱
  • 카테고리가 있고 이행 시 체크 표시가 되도록 하기
  • 달력에서 달성률 단계별로 명도차이 주기
  • 달성률을 그래프로 나타내기 ---> 추후!

주된 기능은 투두리스트, 일정관리 앱의 취지에 맞게 단순한 기능만 있게 하고 추가로 달력에 달성률을 명도차이로 표시해서 한 눈에 볼 수 있게만 만들고자 했다.
그리고 프로젝트의 목적이 지금까지 자바스크립트 공부에 대한 리마인딩과 플러스 '비동기 통신을 자유자재로 사용해 보고 싶다!' 에 있으므로 최대한 관련 지식을 익히는걸 중점으로 두었다.

피그마로 내가 생각한 일정관리 웹앱의 화면 모습을 만들었다. 달력에 색깔이 진할수록 달성률이 높고 흐릴수록 낮다. 카테고리나 일정은 여러개 추가할 수 있고, 카테고리 하나를 선택하고 아래에 해당 카테고리 메모를 추가한다. 추가한 일정은 오늘 날짜에 일정이 있다면 아래에 작성한 리스트가 보여지고 없다면 보이지 않는다.
추가로 새로운 일정 추가 버튼UI가 리스트상 가장 아래쪽에 있는 것이 리스트가 많아져서 버튼이 밀리는 경우 보이지 않는 단점이 있어 항상 날짜 오른쪽에 버튼이 위치하도록 디자인을 수정했다.


fake api 사용 - json server

일정 데이터들을 저장하는 목적으로 제이슨 서버를 사용했다. 제이슨 서버는 자동으로 생성되는 db에 데이터를 저장하면 만들어진 서버와 통신할 수 있도록 해주는 가상 api이다.
POST, GET, PATCH, DELETE -> 각각 데이터 저장, 가져오기, 수정하기 메서드
자세한 사용법은 여기 https://jsonplaceholder.typicode.com/guide/ 를 참고했다.


일정 업로드 - db.json

db.json에 저장되는 데이터의 형식을 정확히 어떤식으로 만들어져야 하는지 몰라서 헷갈렸다. 저장될 데이터는 일정이 추가된 날짜인 date와 날짜 아래에 연결된 memo가 배열의 형태로 필요하고 memo안에는 category, memo, completed로 이행 여부를 저장하도록 해야한다.
그리고 일정이 저장되는 data와 카테고리만 저장되는 categories가 각각 있어야 한다.
그리고 아이디는 데이터가 늘어날때 자동으로 추가되는 값이므로 따로 추가해주지 않았다.

{
  "data": [
    {
      "date": "2022-4-26",
      "memo": [
        {
          "category": "운동",
          "memo": "스쿼트 100개",
          "completed": false
        },
        {
          "category": "스터디",
          "memo": "자바스크립트 공부하기",
          "completed": false
        }
      ],
      "id": 1
    },
    {
      "date": "2022-4-27",
      "memo": [
        {
          "category": "운동",
          "memo": "aaa",
          "completed": false
        }
      ],
      "id": 2
    }
  ],
  "categories": [
    {
      "title": "운동",
      "id": 1
    },
    {
      "title": "스터디",
      "id": 2
    }
  ]
}

업로드는 일정을 그릴때 요소에 데이터속성으로 아이디가 추가되게 하고 아이디 유무의 여부에 따라 업로드와 업데이트를 구분지었다.

// 가장 처음 일정: id가 없을때
    const upload = () => {
        const $textArea = getAll('.memo-write');
        const date = get('#date');
        const cate = get('.selected');
        const list = { date: date.value, memo: [] };
        for(let i = 0; i < $textArea.length; i++){
            list.memo.push(
                {
                    category: cate.textContent,
                    memo: $textArea[i].value,
                    completed: false
                }
            )
        }
        fetch(API_URL, {
            method: 'POST',
            headers: { 'Content-type': 'application/json' },
            body: JSON.stringify(list),
        })
        .then((response) => {
            response.json();
            console.log(response);
        })
        .catch((error) => console.error(error.message));
    }

    // 두번째 일정부터: id가 있을때
    const update = async (id) => {
        const data = await getData(id); 
        const $textArea = getAll('.memo-write');
        const cate = get('.selected');
        const list = { memo: data.memo };
        for(let i = 0; i < $textArea.length; i++){
            list.memo.push(
                {
                    category: cate.textContent,
                    memo: $textArea[i].value,
                    completed: false
                }
            )
        }
        const json = await (
        fetch(`${API_URL}/${id}`,{
            method: 'PATCH',
            headers: { 'Content-type': 'application/json' },
            body: JSON.stringify(list),
        })
        .then((response) => {
            return response.json();
        })
        .then((json) => {
            console.log('update!')
            console.log(json)
        })
        .catch((error) => console.error(error.message))
        );
        return json;
    }

일정 가져오기

선택한 날짜의 데이터를 가져오기 위해서 api에서 제공하는 _gte_lte로 범위를 설정했다. db.json에 내가 넣어놓은 정보를 입력해 해당되는 범위의 데이터만 불러올 수 있게 해준다. RANGE_DATE라는 파라미터에 선택한 날짜를 넘기고 같은 변수가 들어가기 때문에 선택한 하루의 데이터만 가져오게 할 수 있다. 데이터가 없다면 일정이 그려지지않도록 hidden 클래스를 부여하고 일정이 있다면 데이터를 화면에 그리거나('role1') 데이터를 받아와서('role2') 수정이나 삭제가 가능하도록 역할을 나눴다.

let RANGE_DATE = '';
const getData = async (role, RANGE_DATE) => {
    console.log('get Data...')

    const data = await(
    await fetch(`http://localhost:3000/data?date_gte=${RANGE_DATE}&date_lte=${RANGE_DATE}`)
    .then((response) => {
        if(!response.ok) throw 'Error';
        return response.json();
    })
    .then((json) => {
        if(json.length === 0){
            console.log('선택날짜에 데이터가 없습니다.')
            showHiddenClass();
            return;
        } else {
        if(role === 'role1'){
            console.log(json);
            renderTasks(json); 
        } else if (role === 'role2'){
            return json; // data로 들어가는 json
        }}
    })
    .catch((error) => console.error(error.message))
    )

    if(role === 'role2') return data;
};

일정 업데이트 문제

📍 일정을 업로드, 업데이트할 때 json에는 데이터 추가, 수정이 잘 되는데 화면에는 변화가 없었다. 업데이트까지는 잘 되는데 데이터를 불러오는 부분에서 이전 데이터까지만 불러와서 찾아보니 update함수전에 renderTask함수안의 getData가 먼저 실행되고 update는 가장 마지막에 되고 있었다.
이때 예전에 배웠던 비동기를 동기식으로 사용하는 경우 먼저 수행될 작업들이 끝나고 뒤의 로직이 돌아갈 수 있도록 update를 하는 함수에 async/ await을 사용해서 업데이트가 진행된 후에 데이터가 그려지도록 수정했더니 화면에 추가하는 일정이 바로바로 눈에 보여지는걸 볼 수 있었다.


일정 삭제

일정 삭제 부분은 전체삭제와 각각 삭제되는 기능을 구분했다. 일부만 삭제할 때는 전체데이터를 가져온 후 해당되는 일정만 자르고(splice) 삭제되는 일정을 제외한 나머지 데이터를 불러와서 그린다. 체크박스와 일정 텍스트를 input과 label로 구성했기 때문에 둘을 서로 연결할 때 data-id도 같이 생성되도록 해서 삭제시 배열의 인덱스처럼 활용했다.

<input type="checkbox" id="chk0" class="input-check" data-id="0">
<label for=chk0" class="label">...</label>

📍 이 때 일정을 삭제할 때마다 화면이 깜빡이면서 새로고침되는 문제가 있었는데..
비동기로 데이터를 불러왔는데 왜 새로고침되는건지 난감했다. form에서 전송될 때 이벤트도 아니라서 preventDefault()도 안먹히고 삭제되는 일정을 제외하고 데이터도 잘 불러와지고 있어서 그 이유를 찾았는데 알고보니 vscode live server에 기본으로 있는 reload이벤트가 있었다.


이부분인데 살펴보니 소켓통신이 발생될때 reload라는 데이터가 전달되고? 존재 한다면 리로드가 발생하는 것 같았다. 아마 json-server가 이 소켓 통신 방식으로 데이터를 요청하고 가져오는 것 같았다.

소켓통신??
소켓이란 두 프로그램이 서로 데이터를 주고 받을 수 있도록 양쪽에 생성되는 통신 단자이다. 소켓통신이랑 서버와 클라이언트 양쪽에서 서로에게 데이터 전달을 하는 방식의 양방향 통신이다.

vscode 라이브서버 설정에서 리로드를 막는 코드를 추가하니 해결이 됐다.

"liveServer.settings.ignoreFiles": [
        ".vscode/**",
        "**/*.scss",
        "**/*.sass",
        "**/*.ts",
        "**"
    ],

참고 https://github.com/ritwickdey/vscode-live-server/issues/256


달성률 나타내기

달성률은 월별로 데이터를 가져온뒤(_dte,_lte) 날짜별 일정 안에있는 completed === true의 비율을 변수로 설정하고 해당되는 날짜의 스타일이 각각 변경되도록 구현했다.
해당되는 날을 가져올 때는 데이터에 있는 date의 내용과 추가되어있는 데이터 속성의 내용('2022-4-22'의 형식)이 동일하다면 그 요소를 찾아오도록 했다.

지정한 데이터 속성의 내용으로 해당 요소만 가져오는 법

document.querySelectorAll(`[data-name=${찾아올 데이터 속성의 내용}]`)
const calcRating = (data) => {
  data.forEach( data => {
    let completedCount = 0;
    let memoLength = data.memo.length;
    data.memo.forEach( memo => {
      if(memo.completed === true) completedCount++;
    })
    const ratio = ~~((completedCount / memoLength)*100);
    const colorDay = getAll(`[data-date='${data.date}']`);

    switch(true){
      case ratio === 100: 
        colorDay[0].classList.add('full');
        break;
      case ratio >= 50 && ratio < 100: 
        colorDay[0].classList.add('half');
        break;
      case ratio < 50:
        colorDay[0].classList.add('less');
        break;
    }
  })
}

🪄 얻은 것

  • 문제가 생겼을 때 콘솔에 찍어보는것 외에도 디버깅을 해보면서 문제를 찾아나가는 속도가 빨라진것 같고 코드가 어떤식으로 실행되는지 이해할 수 있게 되었다.
  • DOM노드를 동적으로 생성하고 컨트롤하는것에 어느정도 숙지가 되었다.
  • 비동기 통신에 대한 이해도가 높아졌고 fetch, then체이닝, async/await을 어떤 때에 실질적으로 사용하면 좋은지 느낄 수 있었다. (나도 이제 통신 비슷한걸 한다 야호)

부족하지만 항상 만들고 싶었던 투두리스트의 모든 기능을 만들어본것에 만족한다.
그래프로 나타내는 부분은 그 부분에 대한 공부를 하고 추후에 추가할것이다.
기능이 구현되는것에만 중점이 있다보니 비슷한 코드가 조금 반복되기도 하고 어느부분에 어떤 기능을 하는 코드가 들어가야되는지 잘 모른다 🤦‍♀️
하지만 오히려 나중에 다시 리팩토링 해보면서 고칠 부분이 많이 보여진다면 그게 또 좋은걸지도 모르겠다.

profile
즐겜하는거죠

2개의 댓글

comment-user-thumbnail
2023년 4월 10일

저 혹시 소스 코드를 봐도 전체적인 흐름이 이해가 되지 않아서 그런데ㅜㅜ
혹시 코드공유 가능하신가요?ㅠ

1개의 답글