이제 다음달 날짜와 저번 달 날짜도 찍어보자.
나중에 이번 달이 아닌 날짜들은 흐린 글씨로 바꾸겠지만, 우선은 날짜를 출력해내는 것부터 집중하자!
일단 덜 어려워보이는 다음달 날짜들부터 도전!
마지막에 lastDateOfMonth가 되면 break 해주는 부분이 있었는데, break 대신 daysOfThisMonth[i][j] = 1;
를 추가해주었다. 이 알고리즘에서 핵심 로직이 어제 날짜 + 1이기 때문에 이렇게 한줄만 추가해주면 다음달 날짜들은 쉽게 찍을 수 있다.
if (daysOfThisMonth[i][j - 1] === lastDateOfMonth) {
daysOfThisMonth[i][j] = 1;
}
일단 저번달이 며칠까지 있는지 구해야만 날짜를 찍을 수 있겠쥐? 그래서
const lastDateOfLastMonth = new Date(
year,
month,
0
).getDate()
// 31
이렇게 lastDateOfLastMonth 변수에 지난 달 마지막 날짜를 담아주었다.
그런데 갑자기 typescript에서 배운 union 타입이 생각나는 것 아니겠음?
그래서 LastDate라는 type에 매달 마지막 날짜로 가능한 타입들을 정의해보았다. 이렇게 type을 주려고 헀는데 type 에러가 뜨네...? 띠용?
type LastDate = 28 | 29 | 30 | 31;
.
.
const lastDateOfLastMonth: LastDate = new Date(year, month, 0).getDate();
// Type 'number' is not assignable to type 'LastDate'
이라는 오류가 뜬다.
new Date(year, month, 0).getDate()는 number type의 값을 반환하는데, typescript 컴파일러가 number 타입의 값을 Last type으로 자동 변환해주지는 않는다고 한다.
타입스크립트 컴파일러: number type인 건 알겠는데 28 | 29 | 30 | 31 중에 하나인지는 확신할 수 없다고!!
라고 외치는 것과 같다.
그렇다면 내가 외쳐줘야한다. 이럴 때 사용할 수 있는 게 type assertion!!
마지막 날짜를 구하는 거여서 28 | 29 | 30 | 31 중에 하나인 값이 나온다규~!~!
라고 내가 강력하게 주장해주는 것이다.
type LastDate = 28 | 29 | 30 | 31;
.
.
const lastDateOfLastMonth: LastDate = new Date(year, month, 0).getDate() as LastDate; // type assertion
type assertion을 사용하여 타입 에러 해결!
🚨 미래에서 왔습니다. type assertion은 권장되지 않는 타입이다. 타입스크립트 컴파일러 대신 내가 타입을 체크하는 것이기 때문이다. 따라서 type assertion 사용을 지양하고, 위의 케이스에서는 타입 가드 함수를 사용할 수 있겠다.
// 타입 가드 함수 function isLastDate(num: number): num is LastDate { return [28, 29, 30, 31].includes(num); } // 타입 가드 함수가 true를 return 했는지 확인함. if (!isLastDate(lastDateOfMonth)) { throw new Error("마지막 날짜가 유효하지 않습니다."); } else { console.log(`${month + 1}월의 마지막 날짜는 ${lastDateOfMonth}일입니다.`); } }
이렇게 지난 달 마지막 날짜를 구해놓고 저번 달 날짜들로 첫째 주를 채워보자.
그런데 이부분.. 많이 헤맸다. 일단 이렇게 코드를 짜봤고 4월의 첫째주는 얼추 출력이 잘 되었다.
// 이중 for문 시작. 이 안에 버그 있음 주의 😈
for (let i = 0; i < daysOfThisMonth.length; i++) {
if (i === 0) {
for (let k = 1; k <= firstDayOfMonth; k++) {
daysOfThisMonth[0][firstDayOfMonth - k] = lastDateOfLastMonth - k + 1;
}
continue;
}
.
.
.
음 ~! 잠시 기뻐했다! (한 3분 정도??!)
이제 내가 짠 알고리즘으로 month만 바꿨을 때 해당 달의 달력이 잘 나와야 한다.
그런데...
1월을 찍어보자..
// 자바스크립트에서 month가 0이면 1월이다. 4월이면 month가 3이다.
const month = 0;
으로 바꿔 출력해봤는데
??? ㅋㅋㅋㅋㅋ
그나마 다행인 것은 첫번째 날짜는 일요일에 잘 찍히는데 나머지 날짜는 어디로 간거...?
난 누구이고 여긴 어디인가
으아 찾았다 ㅠㅠㅠㅠ
다행히도 한 줄만 바꿔주면 되었다.
이랬던 코드를
if (i === 0) {
for (let k = 1; k <= firstDayOfMonth; k++) {
daysOfThisMonth[0][firstDayOfMonth - k] = lastDateOfLastMonth - k + 1;
}
continue; // 이 친구가 말썽쟁이였다.
}
아래처럼 continue를 없앴더니 잘 찍힌다. i === 0인 if문을 통과할 때마다 continue를 해버리는 게 문제였다.
continue를 없앴더니 잘 찍힌다.
if (i === 0) {
for (let k = 1; k <= firstDayOfMonth; k++) {
daysOfThisMonth[0][firstDayOfMonth - k] = lastDateOfLastMonth - k + 1;
}
}
다시 month에 date.getMonth() 대신 0이라는 숫자를 자체적으로 입력해보았다.
// 자바스크립트에서 month가 0이면 1월이다. 4월이면 month가 3이다.
const month = 0;
오 드디어 1월 달력이 찍혔다!!!
리얼 진짜 울트라 캡숑 감격
그런데.. 다시 보니 눈에 밟히는 한 가지 더 ㅋㅋㅋㅋㅋ
다음 달 날짜는 4일까지만 나오고 5일부터는 나올 필요가 없좌나????
그래서 찬찬히 생각해보고 한 줄 더 추가해주었다.
일단 나오지 않기 시작하는 요일은 일요일이고, 일요일이면 else if문에서 로직을 건드려야 한다.(현재 행에서 -1번째 인덱스가 없으니까)
그리고 지난주에 lastDateOfMonth(=== 이번 달의 마지막 날짜)가 포함되어 있으면 반복문을 종료하면 된다.
대신 lastDateOfMonth가 지난달의 마지막 날짜이면 안되므로 간단히 i > 1이라는 조건도 추가해주었다.
else if (daysOfThisMonth[i - 1]?.[6]) {
// 조건 추가 (지난 주에 이번 달 마지막 날짜가 있되, 저번달 날짜면 안 된다.)
if (daysOfThisMonth[i - 1].includes(lastDateOfMonth) && i > 1) break;
daysOfThisMonth[i][j] = daysOfThisMonth[i - 1][6] + 1;
}
오케이 ~ 성공!!
이제 month랑 year를 자체적으로 바꾸면 해당 연/월의 날짜가 모두 잘 출력되는 것을 확인했다. 나는야 버그 잡는 인간 파리채~~~
아직 뭐 그닥 예쁜 달력은 아니지만 그래도 1차적으로 목표했던 기능은 잘 추가해왔다.
다음 시간에는 useState를 사용해서 버튼만 누르면 이전 달, 다음 달, 이전 연도, 다음 연도로 갈 수 있도록 구현해보려고 한다. 퐈이탱!!