CSS의 display의 grid 속성을 이용해서 제작한 달력입니다.
완성 코드
전체 코드
const makeCalendar = (date) => {
const currentYear = new Date(date).getFullYear();
const currentMonth = new Date(date).getMonth() + 1;
const firstDay = new Date(date.setDate(1)).getDay();
const lastDay = new Date(currentYear, currentMonth, 0).getDate();
const limitDay = firstDay + lastDay;
const nextDay = Math.ceil(limitDay / 7) * 7;
let htmlDummy = '';
for (let i = 0; i < firstDay; i++) {
htmlDummy += `<div class="noColor"></div>`;
}
for (let i = 1; i <= lastDay; i++) {
htmlDummy += `<div>${i}</div>`;
}
for (let i = limitDay; i < nextDay; i++) {
htmlDummy += `<div class="noColor"></div>`;
}
document.querySelector(`.dateBoard`).innerHTML = htmlDummy;
document.querySelector(`.dateTitle`).innerText = `${currentYear}년 ${currentMonth}월`;
}
코드 풀이
.grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: 5px;
}
그리드 속성을 사용하여 한 줄에 7개의 박스만 오도록 고정시켜 주는 게 첫 번째 단계입니다.
기존에는 열을 나누는 기준이 빨간색 박스로 한번 감싸고 안에 7개의 박스를 채우고의 반복이라면, 그리드는 빨간색 박스 안에 한 열의 7개 박스 규칙을 지정하여 여러 개의 빨간색 박스를 감쌀 필요가 없어집니다.
// Date 객체 선언
const date = new Date();
- 현재 날짜를 가져와 달력을 만들기 위해서는 자바스크립트의 Date 객체를 사용해야 합니다.
- Date 객체는 로컬 시간대를 기준으로, 날짜와 시간 등등... 다양한 정보를 가지고 있습니다.
// 현재의 년도와 월 받아오기
const currentYear = new Date(date).getFullYear();
const currentMonth = new Date(date).getMonth() + 1;
제작한 함수를 이용하여 date 객체에서 필요한 정보만을 가져와 사용할 수 있게 되었습니다.
먼저 보내온 Date 객체에 담겨 있는 년, 월 정보를 가져와 줍니다.
getMonth 메서드는 0부터 월 정보를 반환합니다. 그대로 사용할 수 없으니 1을 더해줍니다.
// 첫날의 요일 구하기 - 초기 시작위치를 위해서
const firstDay = new Date(date.setDate(1)).getDay();
// 마지막 날짜 구하기
const lastDay = new Date(currentYear, currentMonth, 0).getDate();
위에서 구한 날짜 정보를 가지고 지난달의 마지막 요일과 이번 달의 마지막 날을 구해줍니다.
1. 현재 달의 첫 요일
Date 객체의 getDay() 메서드는 요일 정보를 가져올 수 있는 날짜 메서드입니다. Date.setDate() 메서드를 이용해서 날짜를 1일로 설정해 준 후 날짜 정보를 가져와줍니다.
2. 현재 달의 마지막 날짜
이번 달의 마지막 날 또한 ( 년도, 월, 0 )으로 해당 달의 마지막 날으로 지정된 객체를 생성해 주고 getDate() 메서드로 날짜를 가져와줍니다.
let htmlDummy = '';
// 한달전 날짜 표시하기
for (let i = 0; i < firstDay; i++) {
htmlDummy += `<div class="noColor"></div>`;
}
// 이번달 날짜 표시하기
for (let i = 1; i <= lastDay; i++) {
htmlDummy += `<div>${i}</div>`;
}
- grid 속성을 이용하였기 때문에 별도의 요소를 감쌀 필요 없이 박스를 추가해 줄 수 있습니다.
- htmlDummy 변수에 미리 구해둔 첫 날짜 요일만큼 빈 박스를 추가해줍니다.
그 후 이번 달의 마지막 날 값을 가지고 1일부터 마지막 날까지 반복문을 돌려 날짜 정보를 가진 박스를 추가해 줍니다.
저번 달 날짜 박스와 현재 달 날짜 박스까지 추가를 해줬습니다. 하지만 이번 달 정보만 표시했기에 위 이미지와 같은 빈 공간이 생기게 됩니다. 그러므로 남은 박스를 채워서 달력을 완성시켜줘야 합니다.
// 남은 박스만큼 다음달 날짜 표시
const limitDay = firstDay + lastDay;
const nextDay = Math.ceil(limitDay / 7) * 7;
// 다음달 날짜 표시하기
for (let i = limitDay; i < nextDay; i++) {
htmlDummy += `<div class="noColor"></div>`;
}
지금까지 추가한 날짜 박스를 시작점으로 마지막 열에 비어있는 박스 개수를 수식으로 계산하고 해당 값만큼 박스를 추가해 줍니다.
한열에 7개의 박스가 들어가니 무조건 7의 배수로 올림 시켜주는 수식을 통해 구현합니다.
// 날짜 박스 표시하기
document.querySelector(`.dateBoard`)
.innerHTML = htmlDummy;
// 현재 날짜 정보 표시하기
document.querySelector(`.dateTitle`)
.innerText = `${currentYear}년 ${currentMonth}월`;
이제 만들어둔 htmlDummy를 그리드 스타일이 적용된 요소에 추가해 주고, 현재 제작된 달력의 년, 월을 알 수 있도록 타이틀도 수정해 주었습니다.
// Date 객체 생성
const date = new Date();
// date 객체를 받는 달력 생성 함수
makeCalendar(date);
// 이전달 이동
document.querySelector(`.prevDay`).onclick = () => {
makeCalendar(new Date(date.setMonth(date.getMonth() - 1)));
}
// 다음달 이동
document.querySelector(`.nextDay`).onclick = () => {
makeCalendar(new Date(date.setMonth(date.getMonth() + 1)));
}
이전달, 다음 달 버튼을 만들어 준 다음 해당 버튼에 이벤트를 걸어줍니다.
date 객체에 setMonth 메서드를 이용하여 객체를 파싱 시키고 새로운 객체를 만들어주고, 달력 생성 함수에 넘겨서 새로운 달력을 만들어 줍니다.
특정일에 내용 추가하기
- 전체 코드 (더보기 클릭) -
// 임시 데이터
const data = [
{ date: '2022-10-15', content: '테스트1' },
{ date: '2022-10-03', content: '테스트2' },
{ date: '2022-10-15', content: '테스트3' },
{ date: '2022-10-26', content: '테스트4' },
{ date: '2022-10-21', content: '테스트5' },
];
// 데이터 가공
const calendarList = data.reduce(
(acc, v) =>
({ ...acc, [v.date]: [...(acc[v.date] || []), v.content] })
, {}
);
// pad method
Number.prototype.pad = function() {
return this > 9 ? this : '0' + this;
}
const makeCalendar = (date) => {
const currentYear = new Date(date).getFullYear();
const currentMonth = new Date(date).getMonth() + 1;
const firstDay = new Date(date.setDate(1)).getDay();
const lastDay = new Date(currentYear, currentMonth, 0).getDate();
const limitDay = firstDay + lastDay;
const nextDay = Math.ceil(limitDay / 7) * 7;
let htmlDummy = '';
for (let i = 0; i < firstDay; i++) {
htmlDummy += `<div class="noColor"></div>`;
}
for (let i = 1; i <= lastDay; i++) {
const date = `${currentYear}-${currentMonth.pad()}-${i.pad()}`
htmlDummy += `
<div>
${i}
<p>
${calendarList[date]?.join('</p><p>') || ''}
</p>
</div>
`;
}
for (let i = limitDay; i < nextDay; i++) {
htmlDummy += `<div class="noColor"></div>`;
}
document.querySelector(`.dateBoard`).innerHTML = htmlDummy;
document.querySelector(`.dateTitle`).innerText = `${currentYear}년 ${currentMonth}월`;
}
const date = new Date();
makeCalendar(date);
// 임시 데이터
const data = [
{ date: '2022-10-15', content: '테스트1' },
{ date: '2022-10-03', content: '테스트2' },
{ date: '2022-10-15', content: '테스트3' },
{ date: '2022-10-26', content: '테스트4' },
{ date: '2022-10-21', content: '테스트5' },
];
// 데이터 가공
const calendarList = data.reduce(
(acc, v) =>
({ ...acc, [v.date]: [...(acc[v.date] || []), v.content] })
, {}
);
일자가 표시된 달력에 내용을 표시하는 방법은 여러 가지가 존재합니다. 날짜(date)와 내용(content) 정보가 포함된 데이터를 { 날짜: 내용 Array } 형식의 데이터로 만들어줍니다.
좀 더 쉬운 코드
데이터를 가공하는 코드를 풀어서 작성해보았습니다. 객체에 각 날짜별로 배열을 생성해주고 내용을 채워줍니다.
// initalValue에 해당하는 변수
const calendarList = {};
// 반복문
for (let index = 0; index < data.length; index++) {
const currentValue = data[index];
// 객체값이 배열이 아닐 경우 -> [ ...(acc[v.date] || []) ] 코드에 해당
if (!calendarList[currentValue.date]) {
calendarList[currentValue.date] = [];
}
// accumulator에 누산하는 방식, 기존과 동일
// Array.concat -> 배열을 합치는 메서드
calendarList[currentValue.date] = calendarList[currentValue.date].concat(currentValue.content);
}
// pad method ( 2 -> 02 )
Number.prototype.pad = function() {
return this > 9 ? this : '0' + this;
}
// 이번달 날짜 표시하기
for (let i = 1; i <= lastDay; i++) {
// 날짜 지정 (YYYY-MM-DD)
const date = `${currentYear}-${currentMonth.pad()}-${i.pad()}`;
htmlDummy += `
<div>
${i}
<p>${calendarList[date]?.join('</p><p>') || ''}</p>
</div>
`;
}
이번 달의 날짜를 추가해 주는 반복문에서 만들어준 데이터를 활용하기 위해서 각각 해당하는 날짜(YYYY-MM-DD)를 구성해 줍니다. 구성해 준 date 키 값을 이용해 내용 데이터를 찾아주고 표시해 줍니다.
참고자료