캘린더를 구현해보자

제리·2021년 8월 30일
0

목표

  • 캘린더에 필요한 날짜 데이터를 구하는 함수 구현

기획


일반적인 캘린더를 보면 다음과 같이 해당달의 1일~31일(말일)까지 검은색 글씨로 표시되고 있고,
첫주의 일부분이 전달의 마지막주, 마지막주의 일부분의 다음주의 첫주로 음영처리되어 있는것을 볼 수 있다.

따라서 캘린더를 그리기 위해서는 전달의 마지막주, 선택된 달의 모든 주, 다음달의 첫주의 데이터가 필요하다.

그래서 주(Week)를 중점적으로 하여 구현을 진행해볼 예정이다.

구현

먼저 간단하게 date 객체를 인자로 받아서 해당하는 달의 모든 주를 배열로 반환하는 함수를 구현해보자

특정달을 주단위로 반환해주는 함수


const SUNDAY = 0

function getWeeks(_date) {
  const weeks = []; // 주를 담을 배열 초기화
  const date = new Date(_date); // date 인스턴스 복사
  date.setDate(1); // 1일로 설정

  let week = []; // 7일을 담을 주배열 초기화
  
  
  while (true) {
    
    // 반복문을 돌면서 1일부터 말일까지 date객체를 복사하여 week배열에 담습니다
    week.push(new Date(date));

    // 하루를 증가시킵니다
    date.setDate(date.getDate() + 1);

    // 다음달이 되었다면 현재까지 담긴 week를 weeks로 넣고 반복문을 탈출합니다
    if (date.getMonth() != _date.getMonth()) {
      weeks.push(week);
      week = [];
      break;
    }

    // week배열에는 토요일까지만 담고 일요일을 만나면 한주가 마무리된것으로 하여 weeks배열에 week를 넣고 week를 초기화합니다.
    if (date.getDay() == SUNDAY) {
      weeks.push(week);
      week = [];
    }
  }

  return weeks;
}

다음을 실행하면

const today = new Date()
console.log(getWeeks(todady))
// (5) [Array(7), Array(7), Array(7), Array(7), Array(3)]
// 현재 2021년 8월이라서 마지막주가 3일로 끝난다.

다음과 같이 해당하는 달의 모든날짜가 date객체형태의 주단위로 배열에 담긴것을 확인할 수 있다.

그렇다면 남은것은 특정달의 첫주와 마지막주를 구하는 함수를 만들고 이 세개를 합해주면 끝이다!

특정달의 첫주를 반환하는 함수

function getFirstWeek(date) {
  const weeks = getWeeks(date);

  return weeks[0];
}

이미 특정달의 모든 주를 구현했기 때문에 첫주를 구하는것은 매우쉽다. 해당함수를 호출하여 첫번째 인덱스를 반환하면 되기때문!

특정달의 마지막주를 반환하는 함수

그렇다면 마지막주를 반환해주는 함수는 어떨까? 마찬가지로 마지막 인덱스를 반환하면 되기 때문에 쉽게 구현할 수 있다.

function getLastWeek(date) {
  const weeks = getWeeks(date);

  return weeks[weeks.length - 1];
}

이제 이 모든것을 합쳐주는 함수를 구현하기만 하면된다!

특정 달에 필요한 모든주를 반환하는 함수

function getMonth(date) {
  
  // date를 바로 사용하지 않고 1일로 초기화하는 이유는
  // Timezone과 관련된 이슈인데 현재 날짜가 달의 마지막날일떄 
  // month를 +1을 하면 다음달이 아닌 다다음달로 넘어가는 경우가 있다.. 
  // 자세히 알아보면 좋겠지만 현재는 안전하게 1일로 초기화했다.
  const date = new Date(_date);
  date.setDate(1);

  // 이전달과 다음달을 편하게 구하기위해 date객체를 복사
  const beforeMonth = new Date(date);
  const nextMonth = new Date(date);

  // 각각 이전달과 다음달을 세팅해준다.
  beforeMonth.setMonth(date.getMonth() - 1);
  nextMonth.setMonth(date.getMonth() + 1);

  // 이전달의 마지막주를 구한다.
  const beforeMonthLastWeek = getLastWeek(beforeMonth);
  
  // 다음달의 첫번째주를 구한다
  const nextMonthFirstWeek = getFirstWeek(nextMonth);
  
  // 현재달의 모든주를 구한다.
  const month = getWeeks(date);

  // 현재달의 첫주의 길이가 7보다 작다는것은 이전달의 마지막주가 필요하다는 뜻이기 때문에 두 배열을 합쳐서 온전하게 한주가 7일을 표현하도록한다.
  if (month[0].length < 7) {
    const temp = [...beforeMonthLastWeek, ...month[0]];
    // 값을 바로 넣지 않고 temp를 사용한 이유는 마지막으로 반환되는 month배열이 iterator로 잘동작하지 않기 때문인데.. 이유는 잘모르겠다.
    month[0] = temp;
  }

  // 마찬가지로 현재달의 마지막주의 길이가 7보다 작다는것은 다음달의 첫주가 필요하다는 뜻이기 때문에 두 배열을 합쳐서 온전하게 한주가 7일을 표현하도록한다.
  const lastIndex = month.length - 1;
  if (month[lastIndex].length < 7) {
    const temp = [...month[lastIndex], ...nextMonthFirstWeek];
    month[lastIndex] = temp;
  }

  
  // 모든주가 7일로 채워진 배열을 리턴한다.
  return month;
}
const today = new Date()
console.log(getMonth(todady))
// (5) [Array(7), Array(7), Array(7), Array(7), Array(7)]
// 이번에는 마지막주에 다음달의 첫주인 4일이 추가되어 7일로 채워진 모습을 볼 수있다

이제 이 함수를 이용하여 html과 css로 캘린더를 표현해주면 된다!

끝!

profile
흐릿한 잉크가 뚜렷한 기억보다 낫다

0개의 댓글