본문 바로가기
Project

[feature] 라이브러리 사용하지 않고 캘린더 만들기

by Daen12 2023. 8. 16.

프로젝트 기능 개발 중, 내가 참여한 스터디의 방장이 캘린더에 일정 등록을 하면

메인 페이지의 나의 캘린더에 자동으로 일정 등록이 되도록 하는 방법을 고민했다.

 

메인페이지 클릭 시 백엔드에서 API를 호출해 일정에 대한 데이터를 받은 후, 해당 데이터에 대한 상태관리를 통해 캘린더에 나타나도록 했다. 그런데 기존의 캘린더 라이브러리를 원하는대로 커스터마이징 하는게 어렵게 느껴져서 직접 캘린더를 만들어보기로 했다.

 

0-1) 백엔드 api를 다음과 같이 호출하였다. ''data''변수에 호출된 데이터가 저장된다.

function getCalendar(){
    async function requestCalendar(): Promise<void> {
        try {
            const response: AxiosResponse<CalendarData> = await authHttp.get<CalendarData>(`/calendar/study?user=${pk}`);
            const {calendar} = response.data;
            if(calendar){
              setData(calendar)
            }
        } catch (error) {
            const err = error as AxiosError
            console.log(err);
        }
    }
    requestCalendar()
}

 

0-2) liTags라는 JSX.Element 배열 타입의 변수를 생성하였다. 이 변수는 캘린더에 들어갈 날짜별 li태그의 바구니로 활용된다.

let liTags:JSX.Element[] = [];

 

1) 먼저, 해당 월의 1일이 몇요일인지 & 지난 달의 마지막 날짜가 몇일인지 구한다.

//1=월 2=화 ... 7=일
const firstDayOfMonth = new Date(currYear, currMonth, 1).getDay();
const lastDateOfLastMonth = new Date(currYear, currMonth, 0).getDate();

 

2) for문을 돌면서 1일 전의 (지난달) 날짜들이 첫줄에 들어오도록 한다.

(style에 inactive를 넣어 이번달이 아닌 날짜들은 흐리게 표시되도록 하였다)

for (let i = firstDayOfMonth; i > 0; i--) {
      liTags.push(<li key={`prev-${i}`} className={style.inactive}>{lastDateOfLastMonth - i + 1}</li>);
}

 

3) 1-2번과 비슷한 로직으로 다음달의 날짜들로 마지막줄을 채운다.

const lastDayOfMonth = new Date(currYear, currMonth, lastDateOfMonth).getDay();

for (let i = lastDayOfMonth; i < 6; i++) {
      liTags.push(<li key={`next-${i}`} className={style.inactive}>{i - lastDayOfMonth + 1}</li>);
}

 

4) 이번 달의 날짜들은 1일부터 마지막날까지 for문을 돌면서 li태그로 채워준다.

오늘 날짜에는 style에 active를 넣어 위 사진과 같이 동그랗게 표시되도록 하였다. 

또한 갖고있는 data 배열을 탐색하면서 해당 날짜의 일정이라면 filter 되도록 한 후에 daySchedules 변수에 저장하였다. 

이 daySchedules를 활용하여 해당 날짜에 일정이 있는지 판단하고, 있다면 hasSchedule이라는 스타일 클래스를 넣어 일정표시가 되도록 하였다.

const lastDateOfMonth = new Date(currYear, currMonth + 1, 0).getDate();

for (let i = 1; i <= lastDateOfMonth; i++) {
      const isToday = i === currentDate.getDate() && currMonth === currentDate.getMonth() && currYear === currentDate.getFullYear();
      const daySchedules = data.filter(schedule => {
        const scheduleDate = new Date(schedule.startTime);
        return scheduleDate.getFullYear() === currYear && scheduleDate.getMonth() === currMonth && scheduleDate.getDate() === i;
      });
      
      liTags.push(
        <li key={`curr-${i}`} 
        className={`${isToday ? style.active : ''} ${daySchedules.length > 0 ? style.hasSchedule : ''}`}
        onClick={() => handleDateClick(i, daySchedules)}
        >{i}
        </li>
      );
    }

 

5) 위 모든 과정은 renderCalendar이라는 함수에 들어있다! 이 함수는 liTags를 리턴한다. tsx 파일에서 이를 아래와 같은 형태로 불러올 수 있다.

const months = ["January", "February", "March", "April", "May", "June", "July",
                  "August", "September", "October", "November", "December"];
                  
<div>
  <header className={style.header}>
    <p className={style.current_date}>{months[currMonth]} {currYear}</p>
    <div className={style.icons}>
    //< > 월 바꾸는 버튼
      <span id="prev" className={style.material_symbols_rounded} onClick={() => handleIconClick("prev")}>&#9664;</span>
      <span id="next" className={style.material_symbols_rounded} onClick={() => handleIconClick("next")}>&#9654;</span>
    </div>
  </header>
  <div className={style.calendar}>
    <ul className={style.weeks}>
      <li>Sun</li>
      <li>Mon</li>
      <li>Tue</li>
      <li>Wed</li>
      <li>Thu</li>
      <li>Fri</li>
      <li>Sat</li>
    </ul>
    <ul className={style.days}>
    //renderCalendar() 함수 호출!
      {renderCalendar()}
    </ul>
 </div>

 

이렇게 하면, 추후에 달력 클릭 시 팝오버가 뜨게 한다거나 해당 날짜에 스케줄 미리보기를 추가한다던가.. 여러가지 활용성이 생긴다! 

댓글