배경
기본 달력 모양 | 내 프로젝트 ✨ |
![]() |
![]() |
수많은 달력 라이브러리 중 react-day-calender를 선택하는 것은 세가지 이유였다.
1. 쉽고 빠른 구현을 위한 라이브러리의 필요
2. 쉬운 CSS 커스텀
3. tailwind 지원
이전 프로젝트에서는 달력 컴포넌트를 만들기 위해 MUI를 사용했었는데, CSS 커스텀이 쉽지 않았었다.
마침 react-day-calendar는 CSS 커스텀이 쉽고,
tailwind CSS 기반의 진행하는 프로젝트에 딱이였다.
+ 친절한 공식문서
맨 처음 CSS 커스텀 방식 : tailwind
공식문서에 따르면, classNames ={{}} 에 추가하면 된다고 했다.
import { DayPicker, getDefaultClassNames } from "react-day-picker";
export function MyCalendar() {
const defaultClassNames = getDefaultClassNames();
return (
<DayPicker
mode="single"
classNames={{
today: `border-amber-500`, // Add a border to today's date
selected: `bg-amber-500 border-amber-500 text-white`, // Highlight the selected day
root: `${defaultClassNames.root} shadow-lg p-5`, // Add a shadow to the root element
chevron: `${defaultClassNames.chevron} fill-amber-500` // Change the color of the chevron
}}
/>
);
}
결론적으로 말하자면, 이 방식은 채택되지 않았다.
개발자도구에서 보이는 tailwind classname으로는 한눈에 어떤 태그 부분인지 확인하기 어려웠기 때문이다.
👉 예를 들어 classname이 chevron(화살표) 라면?
[F12] > Element에서 화살표를 찾기 위해 chevron을 찾으면 된다.
😥 하지만 classname이 fill-amber-500이라면?
이게 화살표인지 아닌 지 바로 인식할 수 없다.
이러한 이유로 classname을 그대로 사용할 수 있는 다른 방식을 찾아야했다..
변경한 방식 : CSS variables & module.css
1. CSS variables
react-day-calendar의 기본 스타일은 CSS 변수를 사용하고 있는데, 이를 커스텀하기 위해 오버라이드 할 수 있다.
css 파일을 하나 생성해서 아래와 같이 적고,
.rdp-root {
--rdp-accent-color: indigo; /* Change the accent color to indigo. */
--rdp-accent-background-color: #f0f0f0; /* Change the accent background color. */
/* Add more CSS variables here. */
}
이걸 기본 daypicker css파일을 import 한 다음 순서로 import 한다. // css 파일 우선순위 원칙 (뒤에 선언된 변수가 앞 변수를 덮어씀)
css variables는 여기서 확인할 수 있는데, 그것보다는 개발자 도구로 확인하면 된다..ㅋ
https://daypicker.dev/docs/styling#css-variables
2 - 0. Importing the CSS Module
style.module.css 를 임포트할 수 있고, 이걸 classNames 에 적용하면 된다.
import { DayPicker } from "react-day-picker";
import classNames from "react-day-picker/style.module.css";
console.log(classNames); // Output the class names as parsed by CSS modules.
export function MyDatePicker() {
return <DayPicker mode="single" classNames={classNames} />;
}
하지만 classNames를 한번에 가져오는 방식은 실패했고, 아래와 같이 비슷한 방식으로 진행했다.
2 - 1. Importing the CSS Module
공식 문서에서는 CSS Modules를 사용할 때 classNames prop을 통해 스타일을 적용하는 것을 권장한다.
이는 컴포넌트의 스타일을 더 명시적으로 제어할 수 있게 해주며, CSS Modules의 장점도 얻을 수 있다.
최종 구현
className prop 이름은 기본 className과 같게 설정하여 개발에 용이하도록 했다.
// Calendar.tsx
"use client";
import { ko } from "date-fns/locale";
import { DayPicker, getDefaultClassNames } from "react-day-picker";
import "react-day-picker/style.css";
// css override
import styles from "@/styles/Calendar.module.css";
interface CalendarProps {
date: Date;
onSelect: (date: Date) => void;
}
const Calendar = ({ date, onSelect }: CalendarProps) => {
const defaultClassNames = getDefaultClassNames();
const today = new Date();
return (
<DayPicker
locale={ko}
mode="single"
showOutsideDays
selected={date}
disabled={{ before: today }}
onDayClick={onSelect}
classNames={{
today: `text-black`,
root: `${defaultClassNames.root} ${styles.rdpRoot}`,
nav: `${styles["rdp-nav"]}`,
button_previous: `${styles["rdp-button_previous"]}`,
button_next: `${styles["rdp-button_next"]}`,
month_caption: `${styles["rdp-month_caption"]}`,
caption_label: `${styles["rdp-caption_label"]}`,
months: `${styles["rdp-months"]}`,
month: `${styles["rdp-month"]}`,
weekdays: `${styles["rdp-weekdays"]}`,
weekday: `${styles["rdp-weekday"]}`,
week: `${styles["rdp-week"]}`,
weeks: `${styles["rdp-weeks"]}`,
day: `${styles["rdp-day"]}`,
selected: `${styles["rdp-selected"]}`,
outside: `${styles["rdp-outside"]}`,
chevron: `${styles["rdp-chevron"]}`,
}}
/>
);
};
export default Calendar;
/** Calendar.module.css */
.rdp {
--rdp-nav-height: 24px;
--rdp-nav_button-height: 24px;
--rdp-nav_button-width: 24px;
--rdp-day-width: 32px;
--rdp-day-height: 32px;
--rdp-day_button-height: 32px;
--rdp-day_button-width: 32px;
--rdp-accent-color: #1a1a1a;
}
.rdp-nav {
position: absolute;
}
.rdp-button_previous {
position: absolute;
left: 0;
z-index: 1;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.rdp-button_next {
position: absolute;
left: 100px;
z-index: 1;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.rdp-chevron {
width: 16px;
height: 16px;
}
.rdp-month_caption {
color: #1a1a1a;
font-size: 18px;
font-style: normal;
font-weight: 500;
line-height: 24px;
}
.rdp-caption_label {
margin-left: 24px;
}
.rdp-months {
max-width: 100%;
}
.rdp-month {
display: flex;
flex-direction: column;
gap: 12px;
}
.rdp-weekdays {
display: flex;
justify-content: space-between;
color: #b0b0b0;
}
.rdp-weekday {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.rdp-weeks {
display: flex;
flex-direction: column;
gap: 12.6px;
}
.rdp-week {
display: flex;
width: 100%;
justify-content: space-between;
}
.rdp-day {
width: 32px;
height: 32px;
}
.rdp-selected {
background-color: #59ac6e;
color: #fff !important;
border-radius: 50%;
}
.rdp-outside {
color: #b0b0b0;
}
결과화면
예쁜 달력 컴포넌트가 반응형으로 잘 만들어졌다!
![]() |
![]() |