달력 컴포넌트 만들기 - 날짜 구현을 위한 유틸 함수 만들기

jh·2024년 6월 13일

달력 컴포넌트를 구현하기에 앞서, 필요한 유틸함수들을 만들어 보았습니다

필요한 유틸함수

달력을 만들 때 필요한 값들은
1. 선택된 날짜에 해당하는 달의 첫번째 날

  • 2024년 6월을 선택했다고 하면, 6월 1일에 대한 정보가 필요하다(6월 1일에 대해서 알아야 함)
  1. 선택된 날짜에 해당하는 달의 마지막 날
  • 2024년 6월을 선택했다고 하면, 6월의 마지막날(30일)에 대한 정보가 필요하다(6월의 마지막 날이 언제이고, 무슨 요일인지에 대해서 알아야 함)

다른 값들도 물론 필요하지만, 여기서 나온 값들을 기반으로 만들어지는 게 대부분이기 때문에 따로 함수로 만들지는 않을 것 같다

기본 메서드 유틸함수로 만들기

const isDate = (date: unknown) => {
  if (date instanceof Date === false) {
    throw new Error(`date 형식이 올바르지 않습니다`)
  }
  return date
}

const getYear = (date: Date) => isDate(date).getFullYear()

const getMonth = (date: Date) => isDate(date).getMonth()
/**Day는 0에서 6까지로 일~토를 나타냄 */
const getDay = (date: Date) => isDate(date).getDay()

date.getMonth() , date.getDay() 를 반복해서 쓰는게 귀찮아서, 따로 함수로 만들었다

  • isDate 라는 type checking을 위한 함수를 쓰긴 했는데, 굳이 안써도 될것같다

첫번째/마지막 날 구하기

/**특정 달의 첫번째 날(1일)에 해당하는 Date 객체를 리턴하는 함수 */
export const getFirstDateOfMonth = (date: Date): Date => {
  const [year, month] = [getYear(date), getMonth(date)]
  return isDate(new Date(year, month, 1))
}

/**특정 달의 마지막 날이 몇일인지 구하는 함수 */
export const getLastDateOfMonth = (date: Date): Date => {
  const [year, month] = [getYear(date), getMonth(date)]
  return isDate(new Date(year, month + 1, 0))
}
new Date();
new Date(value);
new Date(dateString);

new Date(year, monthIndex);
new Date(year, monthIndex, day);
new Date(year, monthIndex, day, hours);
new Date(year, monthIndex, day, hours, minutes);
new Date(year, monthIndex, day, hours, minutes, seconds);
new Date(year, monthIndex, day, hours, minutes, seconds, milliseconds);

date 객체의 경우 인자를 통해 원하는 시간대의 Date 객체를 만들 수가 있는데,

해당 년,월을 알고 있고 이 달의 첫번째 날을 구하려면


new Date(,,1) //day를 1로 설정하면 된다

마지막 날을 구하고 싶으면

new Date(,+1,0) //` 다음 월의 0일` = `현재 월의 마지막일` 

일주일의 시작요일에 따른 대응

우리나라에서 사용하는 달력을 보면 대부분
일 월 화 수 목 금 토
순으로 되어있다

  • 솔직히 월~일이 자연스러운 것 같은데 찾아보니 미국과 캐나다는 아예 공식적으로 일~토 로 고정이고, 유럽이랑 우리나라는 대중적으로 월~일로 쓴다고 한다
  • 하지만 우리나라는 달력에는 일~토로 기입하는 것이 일반적이고 (미국의 영향을 받아서라는 썰이 있다), 유럽의 경우 월~일로 기입하는 경우가 일반적이라고 한다

중요한 건 나라별로 익숙한 한 주를 시작하는 요일이 다를 수 있다는 것이다
하지만 JS에서는 그런 기능이 없기에 (JS는 무조건 일~토를 0~6으로 나타낸다) 변환하는 과정이 필요하다

const weekdays = {
  "ko-KR": {
    days: [
      "일요일",
      "월요일",
      "화요일",
      "수요일",
      "목요일",
      "금요일",
      "토요일",
    ],
    weekStartDay: 0, // 일요일부터 시작
  },
  "fr-FR": {
    days: [
      "Dimanche",
      "Lundi",
      "Mardi",
      "Mercredi",
      "Jeudi",
      "Vendredi",
      "Samedi",
    ],
    weekStartDay: 1, // 월요일부터 시작
  },
} as const

key값으로는 locales가 들어가고, value는 daysweekStartDay로 나뉘는데,
days는 일요일부터 시작해서 토요일까지를 각 나라 언어로 나타낸 값이고, weekStartDay 는 그 나라에서 일반적인 한주의 시작이 무슨 요일인지를 나타낸다(0이면 일요일,1이면 월요일...)

export const getWeekDays = (locale: keyof typeof weekdays = "ko-KR") => {
  const { days, weekStartDay } = weekdays[locale]

  const newWeekDays = days.reduce(
    (acc, day, i) => {
      const index =
        i - weekStartDay >= 0 ? i - weekStartDay : 7 + (i - weekStartDay)
      acc[index] = { key: i, value: day }
      return acc
    },
    [] as { key: number; value: string }[],
  )

  return newWeekDays
}

이렇게 일~토를 기준으로 정렬되어있는 배열을 각 weekStartDay, 한 주의 시작 요일에 따라 새롭게 정렬해준다

프랑스의 경우 해당 과정을 거치면,

//getWeekDays('fr-FR')
 [
  { key: 1, value: "Lundi" },
  { key: 2, value: "Mardi" },
  { key: 3, value: "Mercredi" },
  { key: 4, value: "Jeudi" },
  { key: 5, value: "Vendredi" },
  { key: 6, value: "Samedi" },
  { key: 0, value: "Dimanche" },
]

이런식으로 월~일 순서에 맞는 배열을 갖게 된다
해당 배열을 가지고 calender의 요일 부분을 채워주면 된다

  • JS식 날짜값은 key에 저장되어 있다

(수정 필요) Intl 사용하기

찾아보니 Intl 을 이용하면 나라별로 요일을 나타내는 값을 따로 만들어놓지 않아도, 알아서 locale 값에 따라 변경해준다

export const getWeekDays = (locale = "k0-KR") => {
  const { days, weekStartDay } = weekdays[locale]
  
  const formatter = new Intl.DateTimeFormat(locale, { weekday: "long" })
  
  const newWeekDays = days.reduce(
    (acc, _, i) => {
      const index =
        i - weekStartDay >= 0 ? i - weekStartDay : 7 + (i - weekStartDay)
      acc[index] = { key: i, value: formatter.format(new Date(0, 0, i)) }
      return acc
    },
    [] as { key: number; value: string }[],
  )

  return newWeekDays
}

Intl.DateTimeFormat 을 통해 formatter를 생성하고 이를 통해 해당 요일에 대한 언어별 설정값을 표시해줄 수 있다

년,월에 대한 정보가 중요한게 아니라 요일에 대한 정보만 필요하기 때문에 new Date(0, 0, i) 같이 0으로 처리했다

이걸 사용해도 각 나라별로 한주의 시작 요일에 대한 값은 가지고 있어야 할 것 같고, 함수 파라미터 타입에 대한 정의도 다시 해줘야 될것 같다

0개의 댓글