React

[React, JS] 라이브러리 없이 캘린더 구현하기

마뇨기 2024. 2. 21. 17:54

 

드디어 졸업을 했다..! 취업은 언제,,? ㅎ..


 

프로젝트를 진행 중에 손쉽게 날짜를 선택할 수 있도록 캘린더(달력)를 구현하려고 하였다.

캘린더를 사용하는 일은 많으니까 라이브러리를 여러 가지 찾아보았으나... 구현에 있어 몇 가지 불편함이 있었다.

 

라이브러리를 왜 쓰지 않으셨습니까?!

 

1. 커스터마이징 등의 사용법에 대한 설명이 부족하다.

관련된 설명이 부족하다!

물론 나의 문해력이 부족해서 그럴 수도 있지만,, 다른 라이브러리에 비해 설명이 부족하다고 생각한다.

어떠한 기능을 사용하고 싶을 때 찾기가 굉장히 힘들었다.

2. 프로젝트가 무거워지는 걸 원치 않는다.

라이브러리를 추가할 수록 프로젝트의 무게가 점점 무거워지는 걸 원치 않는다.

최종적으로 해당 브라우저를 열 때 성능 저하가 생길 수 있기 때문이다.

프로젝트에서 사용하는 기능은 별로 없는 것이 비해, 라이브러리가 크다면 비효율적일 것이다.

 

 

그래서 캘린더를 어떻게 구현하였을까?!

 

시간이 지나니 코테에서 무언가를 구현하는 것과 살짝 비슷했다.

코테를 더 많이 공부했다면 더 수월하게 했을 수도..?

 

하여튼!

구현에 있어, 프로젝트에서 사용한 방법은 주마다 배열을 생성하여 날짜를 넣는 것이다.

 

첫 번째 배열에는, 이전 달의 날짜인 28일부터 31일까지, 현재 달의 1, 2, 3일에 대한 Date 타입의 값이 들어 있다.

마찬가지로 세 번째 배열에는, 11일부터 17일까지의 Date 타입 값이 들어 있다.

 

이러한 배열을 순회하여 차례로 나타내는 방법을 활용하였다.

 

코드 확인

 

우선 new Date()를 통해 현재 날짜useState에 담아둔다.

const [currentDate, setCurrentDate] = useState(new Date());

const year = currentDate.getFullYear();
const month = currentDate.getMonth();

 

 

달력에 표시될 첫날과 마지막 날 찾기

달력에 표시될 첫 날과 마지막 날이 필요하다.

위에서 보인 이미지처럼 달력의 첫날은 1일이 아닌, 이전 달의 28일이다.

 

// 현재 달의 첫 날
const firstDayOfMonth = new Date(year, month, 1);
// 달력 시작 날짜를 현재 달의 첫 날의 주의 일요일로 설정
const startDay = new Date(firstDayOfMonth);
startDay.setDate(1 - firstDayOfMonth.getDay());

 

firstDayOfMonth.getDay()는 현재 달의 첫 날짜의 요일을 숫자로 반환한다.

일요일은 0, 월요일은 1, ..., 토요일은 6으로 표현된다.

예를 들어, 만약 5월 1일이 수요일이라면 3을 반환한다.

 

해당 코드는 현재 달의 첫 날짜가 속한 주의 일요일 날짜, 즉 달력에 표시될 첫날을 나타낸다.

만약 현재 달의 첫 날짜가 수요일이라면, (1 - 3) 계산을 통해 -2가 된다.

이는 현재 달의 첫 날짜보다 2일 전, 즉 그 주의 일요일을 의미한다.

startDay.setDate(-2) -> 현재 달의 첫 날짜보다 이틀 전

 

// 현재 달의 마지막 날
const lastDayOfMonth = new Date(year, month + 1, 0);
// 달력 끝 날짜를 현재 달의 마지막 날의 주의 토요일로 설정
const endDay = new Date(lastDayOfMonth);
endDay.setDate(lastDayOfMonth.getDate() + (6 - lastDayOfMonth.getDay()));

 

위 코드처럼 new Date의 세 번째 인자가 0이라면 해당 달의 마지막 날을 불러온다.

 

위 코드는 달력의 마지막 날짜를 현재 달의 마지막 날짜가 속한 주의 토요일로 설정한다.

현재 달의 마지막 날짜가 목요일이라면, lastDayOfMonth.getDay()는 4를 반환한다.

이때 (6 - 4) 계산을 통해 2가 되며, 현재 달의 마지막 날짜에서 2일 후, 즉 그 주의 토요일을 의미한다.

만약 현재 달의 마지막 날이 1월 31일이라면, 구현할 달력에는 2일 후인 2월 2일까지 나타날 것이다.

 


주 단위로 배열에 날짜 넣기

이제 주 단위로 배열에 날짜를 넣을 차례이다.

해당 방법은 위에서 설명한 내용이 있으니 코드와 주석을 참고하면 될 것 같다.

/** startDay부터 endDay까지의 날짜를 주 단위로 그룹화하는 함수 */
const groupDatesByWeek = (startDay: Date, endDay: Date) => {
  const weeks = []; // 최종적으로 주 단위로 그룹화된 날짜 배열들을 저장할 배열
  let currentWeek = []; // 현재 처리 중인 주를 나타내는 배열
  let currentDate = new Date(startDay); // 반복 처리를 위한 현재 날짜 변수, 시작 날짜로 초기화

  // 시작 날짜부터 끝 날짜까지 반복
  while (currentDate <= endDay) {
    currentWeek.push(new Date(currentDate)); // 현재 날짜를 현재 주에 추가
    // 현재 주가 7일을 모두 채웠거나 현재 날짜가 토요일인 경우
    if (currentWeek.length === 7 || currentDate.getDay() === 6) {
      weeks.push(currentWeek); // 완성된 주를 weeks 배열에 추가
      currentWeek = []; // 새로운 주를 시작하기 위해 currentWeek을 재초기화
    }
    currentDate.setDate(currentDate.getDate() + 1); // 현재 날짜를 다음 날로 변경
  }

  // 마지막 주 처리 (만약 남아있다면)
  if (currentWeek.length > 0) {
    weeks.push(currentWeek); // 남아 있는 날짜가 있다면 마지막 주로 weeks에 추가
  }

  return weeks; // 주 단위로 그룹화된 날짜 배열들을 반환
};

 

 

스타일만 잘 적용한다면...

짠!


 

추가로 이전 달, 다음 날로 넘기는 코드!

const handlePrevMonth = () => {
  // 이전 달로 이동
  setCurrentDate(
    new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1)
  );
};

const handleNextMonth = () => {
  // 다음 달로 이동
  setCurrentDate(
    new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1)
  );
};

 

끝!


 

오타, 글이나 코드 개선 이야기는 언제나 환영입니다!!!