
문제 상황
React Native로 개발한 근무 일정 관리 앱에서 사용자들이 다양한 반복 조건으로 일정을 등록할 수 있도록 했습니다:
매주 월·수·금
격주 화요일
매월 15일
종료일 없음 (무제한 반복)
하지만 react-native-calendars
라이브러리는 이러한 복잡한 반복 조건을 이해하지 못하고, 단순히 날짜 리스트만 렌더링할 수 있는 구조였습니다.
해결하기
1. getMarkedDatesFromWeeklySchedule
– 반복 조건을 달력 포맷으로 변환
react-native-calendars는 일정에 표시를 하기 위해서는
{"2024-01-15": { marked: true, color: "red" },...};
형태를 받습니다.사용자가 “매주 월·수·금”을 입력하면, 그 달의 날짜 중 월·수·금이면서 시작일~종료일 사이에 있는 날짜를 뽑아내야 합니다.결국 주간 반복 조건을yyyy-MM-dd
키로 된 객체로 변환하는 작업이 필요했습니다.
export function getMarkedDatesFromWeeklySchedule({ schedule, viewMonth }) {
// 1️⃣ 표시할 기간 결정: 기본은 해당 월, 종료일이 없으면 다음달+다다음달까지
const endDate = schedule.endDate ?? endOfMonth(addMonths(viewMonth, 2));
// 2️⃣ 해당 월의 모든 날짜를 뽑아낸 뒤,
const days = eachDayOfInterval({ start: startOfMonth(viewMonth), end: endOfMonth(viewMonth) });
// 3️⃣ 사용자가 선택한 요일 && 기간 안에 있는 날짜만 필터링
const matchedDates = days
.filter(day => selectedWeekDays.includes(getDay(day)) &&
isWithinInterval(day, { start: startDate, end: endDate }))
.map(d => format(d, "yyyy-MM-dd"));
// 4️⃣ react-native-calendars에서 쓸 수 있는 형태로 변환
matchedDates.forEach(dateStr => {
markedDates[dateStr] = { color: sessionColor, selected: true, sessionId: schedule.id };
});
return markedDates;
}
→ 이렇게 하면 월·수·금 패턴이 있는 세션도, 라이브러리에서 바로 쓸 수 있는 markedDates
구조로 변환됩니다.
2. 상태 스토어를 나누기
모든 데이터를 한 곳에 몰아넣으면, 달력 렌더링 한 번 할 때도 전체 일정 데이터가 전부 리렌더링 트리거가 됩니다.그래서 변경 주기와 성격에 따라 스토어를 세 개로 쪼갰습니다.
ScheduleStore → 전체 세션 원본 (CRUD 전용)
DateScheduleStore → “날짜 → 세션 ID 배열” 매핑
CalendarDisplayStore → 실제 달력 UI에서 표시할 색상·이름 등
이렇게 나누면, 예를 들어 색상만 바뀌어도 원본 데이터까지 건드리지 않아 불필요한 연산이 줄어듭니다.
3. generateViewMonthScheduleData
– 모든 세션을 월별 표시 데이터로 변환
핵심은 “모든 반복 조건을 처리한 뒤, 같은 날짜에 여러 세션이 있으면 합쳐주는” 것입니다.
schedules.forEach(session => {
// 반복 옵션에 따라 날짜 생성 함수 선택
switch (session.repeatOption) {
case "weekly": sessionMarkedDates = getMarkedDatesFromWeeklySchedule({ ... }); break;
// ...
}
// 날짜별 표시 데이터 병합
Object.entries(sessionMarkedDates).forEach(([date, item]) => {
(markedDates[date] ||= []).push(item);
});
// 날짜별 세션 ID 병합
sessionDates.forEach(date => {
(dateSchedule[date] ||= []).push(session.id);
});
});
사용자가 "매주 월·수·금, 3개월간" 같은 조건을 입력하면, 이를 실제 날짜 목록으로 변환해야 했습니다. → 이렇게 하면 월간 보기에서 “15일” 같은 칸에 여러 색상의 마커를 표시할 수 있습니다.
4. 커스텀 Day 컴포넌트
라이브러리 기본 Day 컴포넌트는 색상 하나만 칠할 수 있습니다.우리는 하루에 여러 세션을 색 막대로 보여주고 싶었기 때문에, 직접 구현했습니다.
오늘 날짜는 테두리 강조
선택된 날짜는 배경색 변경
calendarDisplayItems 배열만큼 색 막대 렌더링
PureComponent
로 감싸서 props가 바뀌지 않으면 재렌더링을 막았습니다.
5. 성능 최적화
월별 데이터만 미리 계산 → 무제한 반복도 해당 월만 계산
useCallback으로 dayComponent 메모이제이션
PureComponent로 불필요한 재렌더 방지
결론 요약
순수 함수로 반복 규칙 → 날짜 목록 변환 (테스트 가능, 유지보수 용이)
상태 스토어 분리로 데이터 성격별 최적화
UI 어댑터를 만들어 라이브러리 제약 극복