FullCalendar with React

Genie
8 min readNov 23, 2023

--

프로젝트에서 캘린더를 사용해서 일정을 관리하는 기능이 필요해서 이번에 사용한 캘린더 라이브러리인 FullCalendar를 소개해 보려 한다.

FullCalendar는 자바스크립트 기반 오픈소스 라이브러리로 달력이나 일정 기능 구현 시 사용하는 라이브러리이다.

라이브러리 설치

npm install --save @fullcalendar/react @fullcalendar/core @fullcalendar/daygrid @fullcalendar/interaction

설치한 버전은 6.1.9 이었고, 플러그인도 같이 설치해주었다.

  • @fullcalendar/react: React 컴포넌트 제공
  • @fullcalendar/core: Calendar class 제공
  • @fullcalendar/interaction: dateClick , selectable , event drag n-drop & resizing 액션을 감지하는데 필요
  • @fullcalendar/daygrid: 월별 및 DayGrid 뷰 제공

props 설정

<FullCalendar> 컴포넌트는 FullCalendar의 모든 옵션을 탑재할 수 있다. 그중에서 내가 사용한 옵션은 아래와 같다.

  1. locale

locale 설정을 안하면 영어로 기본설정이 되어 있어서 locale=”kr”로 설정해주었다.

2. plugins

원하는 기능에 따라서 설치하는 플러그인이 다르다. 이번 기능에는 dateClick과 dayGrid뷰가 필요했기 때문에 plugins={[dayGridPlugin, interactionPlugin]} 을 주었다.

3. headerToolbar

캘린더 상단 툴바의 종류, 순서를 정의할 수 있다. left: 'prev', center: 'title', right: 'next' 로 설정해주었다.

4. events

달력에 표시될 이벤트를 배열로 주면된다.

5. eventContent

이벤트 content를 정의할 수 있다. 콜백 함수로 주면 관련 이벤트 데이터가 변경될 때마다 호출된다.

6. dateClick

사용자가 날짜나 시간을 클릭하면 트리거된다. 날짜를 선택하면 일정을 등록할 수 있는 다이얼로그가 뜨게 설정했다.

7. datesSet

캘린더의 범위가 변경되면 DOM이 업데이트된 후에 호출된다. 캘린더에서 이전달 또는 다음달 버튼을 누르면 캘린더 범위가 변경된다. 그 때마다 서버에서 데이터를 다시 불러오기 위해서 사용했다.

소스코드

import { DatesSetArg, EventContentArg } from '@fullcalendar/core';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin, { DateClickArg } from '@fullcalendar/interaction';
import FullCalendar from '@fullcalendar/react';
import { Box } from '@mui/material';
import { useQuery } from '@tanstack/react-query';
import { getProductSchedule } from 'apis/product';
import ScheduleDialog from 'components/product/register/ScheduleDialog';
import { PRODUCT } from 'constants/@queryKeys';
import { format } from 'date-fns';
import { useState } from 'react';
import { IGetScheduleForm } from 'types/product';

const FullCalendarPage = () => {
const [period, setPeriod] = useState<IGetScheduleForm>({
startDate: null,
endDate: null,
});
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
const [isOpenScheduleDialog, setIsOpenScheduleDialog] = useState(false);

const { data: scheduleList } = useQuery(
[PRODUCT.SCHEDULE, period.startDate],
() =>
getProductSchedule({
startDate: period.startDate,
endDate: period.endDate,
}),
{
enabled: !!period.startDate,
}
);

const events = scheduleList?.map((schedule) => {
const openDate = new Date(schedule.openDate);

return {
title: schedule.isAllDay ? '종일' : format(openDate, 'HH:mm'),
start: openDate,
allDay: schedule.isAllDay,
};
});

const onDateClick = ({ date }: DateClickArg) => {
onOpenScheduleDialog(date);
};

const onChangeDate = ({ startStr, endStr }: DatesSetArg) => {
setPeriod({ startDate: new Date(startStr), endDate: new Date(endStr) });
};

const onOpenScheduleDialog = (selected: Date) => {
setSelectedDate(selected);
setIsOpenScheduleDialog(true);
};

const eventContent = (eventInfo: EventContentArg) => {
const isAllDay = eventInfo.event.allDay;

return (
<Box
sx={{
width: '100%',
backgroundColor: isAllDay ? '#5A90FF' : '#FF8652',
borderRadius: '3px',
p: 0.5,
color: '#fff',
fontWeight: 600,
}}
>
{eventInfo.event.title}
</Box>
);
};

return (
<>
<ScheduleDialog
open={isOpenScheduleDialog}
setOpen={setIsOpenScheduleDialog}
date={selectedDate}
/>
<FullCalendar
locale="kr"
plugins={[dayGridPlugin, interactionPlugin]}
events={events}
datesSet={onChangeDate}
dateClick={onDateClick}
eventContent={eventContent}
headerToolbar={{
left: 'prev',
center: 'title',
right: 'next',
}}
/>
</>
);
};

export default FullCalendarPage;

마치며

라이브러리를 선택할 때, react-calendarFullCalendar를 고민했는데 react-calendar는 moment 라이브러리를 같이 설치해야 해서 FullCalendar를 선택했다. 지금 프로젝트는 이미 date-fns를 사용하고 있었기 때문이다.

아직 일정을 추가해서 이벤트로 등록하는 작업과 일정을 수정, 삭제하는 작업이 남았지만 지금까지 FullCalendar를 사용해 본 결과 Docs가 친절해서 딱히 불편했던 점은 없었다.

--

--