React DayPicker + tailwindCSS

백세희·2025년 4월 27일
post-thumbnail

공식 문서를 기본으로 하고, 부분적으로 ai를 사용해 수정하며 제작했습니다!

프로젝트에서 캘린더 컴포넌트 라이브러리를 사용해야 하는 일이 생겼습니다~
인터넷에서 단순히 검색했을 때,
"FullCalendar를 많이 사용하네 요거로 해보자" 했었는데요.

저희는 사실 일정 관리라는 기능까지 필요하지 않았었습니다.
그런 점을 팀원께서 조언해주어 다른 캘린더를 알아보았습니다.

처음에는 React-Calendar 를 사용해볼까 했습니다.
하지만 Tailwind CSS를 적용하기 어려워보였습니다.

그러던 중 gpt 한테 물어보니 React DayPicker을 추천해주더군요.
구글링하며 진행해보니 응 ...? 어떻게 하는 거지 했습니다.

정답은 공식문서를 참고하고 적용해보는 거였습니다.

Next.js App router
Tailwind CSS 버전은 3.4.17
React DayPicker 버전은 9.6.7를 사용했습니다.

1. React DayPicker 설치하기

npm install react-day-picker@latest

2. import 하기

// 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를 보여줘야 하니 커스텀을 차근차근 시작해 보겠습니다 ㅎㅎ

3. Localization, 한국 언어 적용하기

Prop NameTypeDescription
localeLocaleSet 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} />; //🔥
}

3.1 월은 잘 적혀있는데, 년은 왜 없지?

아래에서 하겠습니다.

4. Navigation 말고 dropDown으로 달을 선택하고 싶은데

Prop NameType
hideNavigationboolean
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를 이제 해봐야겠네요 ^-^

5. tailwind css 적용해보기

5.1. 어떻게 적용해야 하는 걸까?

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라고만 적혀있는게 거슬리네욤!

6. years_dopdown 0000 -> 0000년으로 커스텀하기

"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`,
      }}
    />
  );
}

7. 날짜를 선택해 보자

"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`,
      }}
    />
  );
}

결과입니다~ 일단은 코드가 너무 길어지는 것 같아, 이 날짜 선택한 코드는 제거하고 다음 단계로 가보겠습니다.

8.응답 리스트(날짜 리스트)를 달력의 날짜에 표시하기

저희는 서버에서 준 날짜 리스트를 ui에 표시해야 했어요!

  • modifiers 활용
  • components 활용
Prop NameTypeDescription
componentsCustomComponentsChange the components of the calendar.
  • day는 btn이라는 점 참고
ParameterType
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`,
      }}
    />
  );
}

profile
프론트엔드

0개의 댓글