datePicker & timePicker 직접 구현하기 1탄 에 이어서 datePicker 와 timePicker 를 직접 구현해보기로 했다. 우선 기본적인 달력의 모습을 갖춘 다음에 필요한 기능들을 하나씩 추가할 것이다.
우선 기본적인 달력 틀을 잡고 나서 필요한 기능을 하나씩 추가하기로 했다.
아래와 같은 모양의 달력을 구현하기 위해 어떻게 구성되어 있는지 하나씩 적어보았다.
🔸 달력의 header 부분에는 해당 년도와 월이 표시되어 있다.
🔸 요일을 표시하는 부분이 있다. (일요일 ~ 토요일)
🔸 행x열(7x6) 으로 총 42개 cell 로 되어있다.
🔸 매 월마다 마지막 날짜가 다르다. 1월은 31일까지있고, 2월은 28일 or 29일, 3월은 31일.... 이처럼 매 월마다 마지막 날짜가 다르다.
🔸 아래 2024년 1월 달력을 보면 2023년 12월 31일부터 2024년 2월 10일까지
의 전 월과 다음 월의 날짜가 일부 포함되어 있다.
🔸 <<
, >>
버튼을 클릭하면 이전 월과 다음 월로 넘어갈 수 있다.
🔸 오늘 날짜(8일
) 를 표시해준다.
CalendarHeader 라는 컴포넌트를 생성했다.
useState 를 사용해 현재 연도와 월을 초깃값으로 설정했다.
const [curYear, setCurYear] = useState(new Date().getFullYear())
const [curMonth, setCurMonth] = useState(new Date().getMonth())
<
버튼을 누르면 이전 월로 이동, >
버튼을 누르면 다음 월로 이동하는 함수를 등록해준다.<
버튼에 등록해놓은 이벤트 함수에 if 문을 사용해 curMonth === 0 이면, 즉 1월달이면 (curYear - 1) 를 해주고, 월은 12월달이 되도록 바꿔준다. 그 외에는 (curMonth - 1) 을 해서 이전 월로 이동하도록 코드를 작성했다. const handlePrevButton = (e : React.MouseEvent) => {
if(curMonth === 0 ) {
setCurYear(curYear - 1);
setCurMonth(11);
} else {
setCurMonth(curMonth - 1);
}
}
마찬가지로 >
버튼에 등록해놓은 이벤트 함수에 if 문을 사용해 curMonth === 11 이면, 즉 12월달이면 (curYear + 1) 를 해주고, 월은 1월달이 되도록 바꿔준다. 그 외에는 (curMonth + 1) 을 해서 다음 월로 이동하도록 코드를 작성해준다.
아래와 같이 기본적인 달력의 header 모습을 만들었다.
달력의 요일을 나타내는 DayOfWeek 라는 간단한 컴포넌트를 만들었다.
<thead>
, <tr>
, <th>
태그를 사용해 요일을 나타내는 열을 만들어줬다.
const DayOfWeek = () => {
return (
<thead>
<tr>
<th>일</th>
<th>월</th>
<th>화</th>
<th>수</th>
<th>목</th>
<th>금</th>
<th>토</th>
</tr>
</thead>
);
};
export default DayOfWeek;
이 부분에서는 어떻게 구현해야 할지 고민이 많이 필요했다. 우선, 매 월마다 마지막 날짜가 다르기 때문에 선택한 월의 마지막 날짜를 구한 다음, 1일부터 마지막 날짜가 될 때까지 1씩 더해주면 되겠다는 생각이 들었다. 이를 for문으로 표현하면 다음과 같다.
lastDate 는 해당 월의 마지막 날짜를 의미한다.
year, month 는 매개변수로 받아서 처리한다.
for(let i=1; i<= lastDate; i++) {
console.log(new Date(year, month, i))
}
이렇게 하면 한달에 해당 되는 날짜는 구현할 수 있지만, 행x열(7x6) 으로 총 42개의 cell 로 되어있어 이전 월과 다음 월의 날짜 일부가 포함되어야 한다.
예를들어 2024년 1월 달력을 보면, 2023년 12월 31일
과 2024년 2월 1~10일
까지의 날짜가 포함되어 있다.
즉, 날짜를 렌더링 하기 위해서는 12/31일 부터 날짜를 하나씩 더해주면 된다는 생각이 들었다.
그럼 어떻게 하면 처음 시작값(여기선 12/31일) 을 알 수 있을까 고민이 되었는데, 달력을 계속 들여다 보다가 규칙을 하나 발견했다. 해당 월의 1일에 해당 되는 요일만큼 뒤로 가면 시작값을 구할 수 있을 것 같았다.
예를들어 1/1일은 월요일이므로 getDay() 를 통해 요일을 구하면 1
이 나온다. 1/1 일에서 1만큼 뒤로 가면 즉, 하루 전은 12월31일이 된다!
2024년 2월 달력으로도 실험해보니, 2월 1일은 목요일로 getDay() 를 통해 숫자 4
를 얻었다. 2/1일에서 4일만큼 뒤로 가면 1월 28일이 나온다.
이런식으로 1일에 해당 되는 요일을 구하고 그 숫자만큼 날짜를 빼주면 된다는 규칙이 있었다.
Date.prototype.toLocaleString(locales, [options])
날짜를 읽기 쉬운 문자열로 변환하는 메서드이다.
locale과 opitons 파라미터를 받는다.
사용 예시
new Date().toLocaleString("en", {month : "short"}) /// "Jan"
new Date().toLocaleString("en", {month : "long"}) /// "January"
new Date().getDate() /// 10 (오늘 날짜 2024-1-10)
new Date().getDay() /// 3 (오늘 요일 수요일)
❗매우 헷갈림❗ : 요일은 일요일을 시작으로 0 부터 시작해서 토요일 6 으로 끝난다.
new Date().getMonth() /// 0 (현재 1월달)
❗매우 헷갈림❗ : 월은 1월을 0으로 시작해서 12월이 11로 끝난다.
new Date(2024,0) /// Mon Jan 01 2024 00:00:00 GMT+0900 (한국 표준시)
new Date(2024,1) /// Thu Feb 01 2024 00:00:00 GMT+0900 (한국 표준시)
new Date(2024,0,-2) /// Fri Dec 29 2023 00:00:00 GMT+0900 (한국 표준시)
new Date(2024,1,-3) /// Sun Jan 28 2024 00:00:00 GMT+0900 (한국 표준시)
❗주의할 점❗ : 일자에 0 을 포함하기 때문에 -2 인 경우, 0, -1, -2
즉 3을 빼는 것과 같다.