
공식 문서를 기본으로 하고, 부분적으로 ai를 사용해 수정하며 제작했습니다!
프로젝트에서 캘린더 컴포넌트 라이브러리를 사용해야 하는 일이 생겼습니다~
인터넷에서 단순히 검색했을 때,
"FullCalendar를 많이 사용하네 요거로 해보자" 했었는데요.
저희는 사실 일정 관리라는 기능까지 필요하지 않았었습니다.
그런 점을 팀원께서 조언해주어 다른 캘린더를 알아보았습니다.
처음에는 React-Calendar 를 사용해볼까 했습니다.
하지만 Tailwind CSS를 적용하기 어려워보였습니다.
그러던 중 gpt 한테 물어보니 React DayPicker을 추천해주더군요.
구글링하며 진행해보니 응 ...? 어떻게 하는 거지 했습니다.
정답은 공식문서를 참고하고 적용해보는 거였습니다.
Next.js App router
Tailwind CSS 버전은 3.4.17
React DayPicker 버전은 9.6.7를 사용했습니다.
npm install react-day-picker@latest
// layout.tsx
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="flex flex-col h-screen justify-center items-center">
{children}
</div>
);
}
// page.tsx
"use client";
import { DayPicker } from "react-day-picker";
export default function Calenadar() {
return <DayPicker />;
}
이렇게만 가져다가 보여줘도 충분히 괜찮을 것 같아요 ㅎㅎ
하지만 저희는 특정 날짜 리스트에 표시가 되어있는 ui를 보여줘야 하니 커스텀을 차근차근 시작해 보겠습니다 ㅎㅎ
| Prop Name | Type | Description |
|---|---|---|
| locale | Locale | Set the locale. Default is en-US. |
"use client";
import { DayPicker } from "react-day-picker";
import { ko } from "date-fns/locale"; //🔥
export default function Calenadar() {
return <DayPicker locale={ko} />; //🔥
}
아래에서 하겠습니다.
| Prop Name | Type |
|---|---|
| hideNavigation | boolean |
| captionLayout | "label" | "dropdown" | "dropdown-months" | "dropdown-years" |
"use client";
import { DayPicker } from "react-day-picker";
import { ko } from "date-fns/locale";
export default function Calenadar() {
return <DayPicker locale={ko} hideNavigation captionLayout="dropdown" />;
}
dorpdown 부분만 남았으면 좋겠는데,, css를 이제 해봐야겠네요 ^-^
Function: getDefaultClassNames() 결과 찍어보기
"use client";
import { DayPicker, getDefaultClassNames } from "react-day-picker";
import { ko } from "date-fns/locale";
export default function Calenadar() {
const defaultClassNames = getDefaultClassNames();
console.log(defaultClassNames);
return <DayPicker locale={ko} hideNavigation captionLayout="dropdown" />;
}
console.log(defaultClassNames)의 결과
class 이름들이 나옵니다! 이걸로 tailwind css를 적용해 보면 됩니다!{ root: 'rdp-root', chevron: 'rdp-chevron', day: 'rdp-day', day_button: 'rdp-day_button', caption_label: 'rdp-caption_label', dropdowns: 'rdp-dropdowns', dropdown: 'rdp-dropdown', ... }
"use client";
import { DayPicker } from "react-day-picker";
import { ko } from "date-fns/locale";
export default function Calenadar() {
return (
<DayPicker
locale={ko}
hideNavigation
captionLayout="dropdown"
classNames={{
root: ` w-[300px] md:w-[400px] h-[400px] shadow-lg p-5 bg-pink-200 rounded-lg`,
day: `text-center w-[100px] h-[50px]`,
caption_label: `hidden`, // 🔥 위에서 이야기한 caption_label 숨기기
dropdowns: `h-9 flex flex-row-reverse gap-2 `, // 🔥 년도 월 위치 바꾸기기
years_dropdown: `text-center bg-pink-300 rounded-lg`,
months_dropdown: `text-center bg-pink-300 rounded-lg`,
}}
/>
);
}
UI이가 너무 귀엽게 커스텀 되었죠 ㅎㅎ
월은 제대로 0월이라고 작성되어있는데, 년도는 2025라고만 적혀있는게 거슬리네욤!
"use client";
import { DayPicker } from "react-day-picker";
import { ko } from "date-fns/locale";
export default function Calenadar() {
return (
<DayPicker
locale={ko}
hideNavigation
captionLayout="dropdown"
formatters={{
formatYearDropdown: (year) => `${year.getFullYear()}년`,
}} // 🔥이 방식은
classNames={{
root: ` w-[300px] md:w-[400px] h-[400px] shadow-lg p-5 bg-pink-200 rounded-lg`,
day: `text-center w-[100px] h-[50px]`,
caption_label: `hidden`,
dropdowns: `h-9 flex flex-row-reverse gap-2 `,
years_dropdown: `text-center bg-pink-300 rounded-lg`,
months_dropdown: `text-center bg-pink-300 rounded-lg`,
}}
/>
);
}
"use client";
import { DayPicker } from "react-day-picker";
import { ko } from "date-fns/locale";
import { useState } from "react";
export default function Calenadar() {
const [selected, setSelected] = useState<Date>(); // 🔥 선택한 날짜
return (
<DayPicker
locale={ko}
hideNavigation
captionLayout="dropdown"
formatters={{
formatYearDropdown: (year) => `${year.getFullYear()}년`,
}}
mode="single" //🔥날짜를 하루만 선택가능하게 하기
selected={selected} // 🔥
onSelect={setSelected} // 🔥
footer={
selected
? `선택된 날짜: ${selected.toLocaleDateString()}`
: "날짜를 골라봐~"
} // 🔥달력 footer 부분에 표시하기
classNames={{
root: ` w-[300px] md:w-[400px] h-[400px] shadow-lg p-5 bg-pink-200 rounded-lg`,
day: `text-center w-[100px] h-[50px]`,
selected: `bg-pink-400`, // 🔥 selected 한 날짜 표시하기
caption_label: `hidden`,
dropdowns: `h-9 flex flex-row-reverse gap-2 `,
years_dropdown: `text-center bg-pink-300 rounded-lg`,
months_dropdown: `text-center bg-pink-300 rounded-lg`,
}}
/>
);
}
결과입니다~ 일단은 코드가 너무 길어지는 것 같아, 이 날짜 선택한 코드는 제거하고 다음 단계로 가보겠습니다.
저희는 서버에서 준 날짜 리스트를 ui에 표시해야 했어요!
| Prop Name | Type | Description |
|---|---|---|
| components | CustomComponents | Change the components of the calendar. |
| Parameter | Type |
|---|---|
| props | { day: CalendarDay; modifiers: Modifiers; } & ButtonHTMLAttributes<HTMLButtonElement> |
"use client";
import { CalendarDay, DayPicker, Modifiers } from "react-day-picker";
import { ko } from "date-fns/locale";
export default function Calenadar() {
// 🔥 더미 데이터, 날짜 형식을 다르게 하면 오차 범위(month 부분에서 +- 1) 가 생길 수 있습니다.
const answeredDays = [
new Date("2025-04-08"),
new Date("2025-04-09"),
new Date("2025-04-11"),
];
return (
<DayPicker
locale={ko}
hideNavigation
captionLayout="dropdown"
formatters={{
formatYearDropdown: (year) => `${year.getFullYear()}년`,
}}
mode="single" // 🔥 요게 없으면 표시가 안 되네요 ㅎㅎㅎ
modifiers={{ answered: answeredDays }} // 🔥
components={{
DayButton: (
props: {
day: CalendarDay;
modifiers: Modifiers;
} & React.HTMLAttributes<HTMLButtonElement>
) => {
const { day, modifiers, ...rest } = props;
return (
<button
className={`w-full h-full text-center flex flex-col items-center justify-start p-2`}
{...rest}
>
<div>{day.date.getDate()}</div>
{modifiers.answered && (
<div className="w-3 h-3 bg-white rounded-full mt-1" />
)}
</button>
);
},
}} // 🔥조건을 반영한 커스텀 버튼
classNames={{
root: ` w-[300px] md:w-[400px] h-[400px] shadow-lg p-5 bg-pink-200 rounded-lg`,
day: `text-center w-[100px] h-[50px]`,
selected: `bg-pink-400`,
caption_label: `hidden`,
dropdowns: `h-9 flex flex-row-reverse gap-2 `,
years_dropdown: `text-center bg-pink-300 rounded-lg`,
months_dropdown: `text-center bg-pink-300 rounded-lg`,
}}
/>
);
}