저번에 이어서 이번에는 일정 목록을 불러오고 추가하는 작업을 해야 한다.
"use client";
import { useState } from "react";
import Calendar from "react-calendar";
import "react-calendar/dist/Calendar.css";
import "../../assets/custom-calendar.css";
type ValuePiece = Date | null;
type Value = ValuePiece | [ValuePiece, ValuePiece];
type Todo = {
title: string;
date: Date;
};
export default function CalendarPage() {
const today = new Date();
const [date, setDate] = useState<Value>(today);
const [todos, setTodos] = useState<Todo[]>([]);
const sampleTodos = [
{
title: "놀기",
date: new Date("2024-07-12"),
},
{
title: "놀기",
date: new Date("2024-07-15"),
},
{
title: "놀기2xxxxxxxxxxxxxxxxx",
date: new Date("2024-07-15"),
},
{
title: "놀기3",
date: new Date("2024-07-15"),
},
{
title: "공부하기 16",
date: new Date("2024-07-16"),
},
{
title: "운동하기 17",
date: new Date("2024-07-17"),
},
{
title: "운동하기 29",
date: new Date("2024-07-29"),
},
{
title: "운동하기 8-2",
date: new Date("2024-08-2"),
},
];
const dateChangeHandler = (newDate: Value) => {
setDate(newDate);
if (newDate instanceof Date) {
const selectedDate = newDate.toISOString().split("T")[0];
const filteredTodos = sampleTodos.filter(
(todo) => todo.date.toISOString().split("T")[0] === selectedDate
);
setTodos(filteredTodos);
} else {
setTodos([]);
}
};
const isSameDay = (d1: Date, d2: Date) => {
return (
d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate()
);
};
const addContent = ({ date }: { date: Date }) => {
let content = [];
if (isSameDay(date, today)) {
content.push(
<p key="today">
Today {date.getMonth() + 1}/{date.getDate()}
</p>
);
} else {
content.push(<br />);
}
const todosForDate = sampleTodos.filter((todo) =>
isSameDay(todo.date, date)
);
for (let i = 0; i < todosForDate.length; i++) {
content.push("✨");
}
return <>{content}</>;
};
return (
<main>
<div className="flex justify-center gap-2">
<Calendar
value={date}
onChange={dateChangeHandler}
locale="en"
prev2Label={null}
next2Label={null}
tileContent={addContent}
/>
<div className="w-[400px] p-4 border-red-500 border-4 rounded-2xl">
일정 내용들
<br />
<p>
{date?.toLocaleString("ko-KR", {
year: "numeric",
month: "long",
day: "2-digit",
})}
</p>
{todos.map((todo, index) => (
<p key={index} className="border-4 py-4">
{todo.title}
</p>
))}
</div>
</div>
</main>
);
}
코드가 깔끔하진 않지만 더미 데이터를 들고 와서 캘린더에 표시하는 기능을 추가했다.

그런데 보다시피 15일 일정이 16일에 출력이 되는 것을 볼 수 있다. 일정들이 하루씩 뒤로 밀려서 나온다...
오류를 살펴보니 date 값이 하루 전날로 표시된다. 7월 16일을 클릭하면 7월 15일이 setDate의 값으로 들어가는 것이다. 그래서 16일 일정에 15일 일정들이 나왔다.
찾아보니 toISOString가 문제였다. toISOString은 기본적으로 UTC 시간(영국 시간)을 기준으로 문자열을 반환하기 때문에 한국과는 차이가 나서 이러한 문제가 발생하는 것이었다.이러한 문제를 해결하기 위해서는 시간 차이를 보정해 주어야 한다.
// 기존 코드
const today = new Date()
// 수정 코드
const offset = new Date().getTimezoneOffset() * 60000;
const today = new Date(Date.now() - offset);
참고로 기존 코드와 수정 코드를 각각 출력해보면 아래와 같다.
console.log(today.toISOString());
// 수정 전
2024-07-15T15:39:15.915Z
// 수정 후
2024-07-16T00:39:14.704Z
아래는 수정된 코드이다.
"use client";
import { useEffect, useState } from "react";
import Calendar from "react-calendar";
import "react-calendar/dist/Calendar.css";
import "../../assets/custom-calendar.css";
type ValuePiece = Date | null;
type Value = ValuePiece | [ValuePiece, ValuePiece];
type Todo = {
title: string;
date: Date;
};
export default function CalendarPage() {
const offset = new Date().getTimezoneOffset() * 60000;
const today = new Date(Date.now() - offset);
const [date, setDate] = useState<Value>(today);
const [todos, setTodos] = useState<Todo[]>([]);
const sampleTodos = [
{
title: "놀기",
date: new Date("2024-07-12"),
},
{
title: "놀기",
date: new Date("2024-07-15"),
},
{
title: "놀기2xxxxxxxxxxxxxxxxx",
date: new Date("2024-07-15"),
},
{
title: "놀기3",
date: new Date("2024-07-15"),
},
{
title: "공부하기 16",
date: new Date("2024-07-16"),
},
{
title: "운동하기 17",
date: new Date("2024-07-17"),
},
{
title: "운동하기 29",
date: new Date("2024-07-29"),
},
{
title: "운동하기 8-2",
date: new Date("2024-08-2"),
},
];
useEffect(() => {
if (date instanceof Date) {
const selectedDate = formattedDate(date);
const filteredTodos = sampleTodos.filter(
(todo) => formattedDate(todo.date) === selectedDate
);
setTodos(filteredTodos);
} else {
setTodos([]);
}
}, [date]);
const formattedDate = (date: Date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
const dateChangeHandler = (newDate: Value) => {
setDate(newDate);
};
const isSameDay = (d1: Date, d2: Date) => {
return (
d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate()
);
};
const addContent = ({ date }: { date: Date }) => {
let content = [];
if (isSameDay(date, today)) {
content.push(
<p key={`today-${date.getDate()}`}>
Today {date.getMonth() + 1}/{date.getDate()}
</p>
);
} else {
content.push(<br key={`break-${date.getDate()}`} />);
}
const todosForDate = sampleTodos.filter((todo) =>
isSameDay(todo.date, date)
);
for (let i = 0; i < todosForDate.length; i++) {
content.push(<span key={`todo-${i}-${date.getDate()}`}>✨</span>);
}
return <>{content}</>;
};
return (
<main>
<div className="flex justify-center gap-2">
<Calendar
value={date}
onChange={dateChangeHandler}
locale="en"
prev2Label={null}
next2Label={null}
tileContent={addContent}
/>
<div className="w-[400px] p-4 border-red-500 border-4 rounded-2xl">
일정 내용들
<br />
<p>
{date?.toLocaleString("ko-KR", {
year: "numeric",
month: "long",
day: "2-digit",
})}
</p>
{todos.map((todo, index) => (
<p key={index} className="border-4 py-4">
{todo.title}
</p>
))}
</div>
</div>
</main>
);
}
나중에 데이터베이스를 연결해서 추가, 삭제 등을 구현하고 코드를 다듬어야겠다.