import { useState, useMemo, useCallback } from 'react'; import { MIN_YEAR, WEEKDAYS, MONTH_NAMES } from '@/constants'; import { getTodayKST } from '@/utils'; /** * 캘린더 훅 * 날짜 선택, 월 이동 등 캘린더 로직 제공 * @param {Date|string} initialDate - 초기 날짜 */ export function useCalendar(initialDate = new Date()) { // initialDate가 Date 객체가 아니면 변환 const ensureDate = (date) => { if (date instanceof Date) return date; if (typeof date === 'string') return new Date(date); return new Date(); }; const [currentDate, setCurrentDate] = useState(() => ensureDate(initialDate)); const [selectedDate, setSelectedDate] = useState(getTodayKST()); const year = currentDate.getFullYear(); const month = currentDate.getMonth(); // 캘린더 데이터 계산 const calendarData = useMemo(() => { const firstDay = new Date(year, month, 1).getDay(); const daysInMonth = new Date(year, month + 1, 0).getDate(); const prevMonthDays = new Date(year, month, 0).getDate(); // 캘린더에 표시할 날짜 배열 생성 const days = []; // 이전 달 날짜 for (let i = firstDay - 1; i >= 0; i--) { days.push({ day: prevMonthDays - i, isCurrentMonth: false, date: new Date(year, month - 1, prevMonthDays - i), }); } // 현재 달 날짜 for (let i = 1; i <= daysInMonth; i++) { days.push({ day: i, isCurrentMonth: true, date: new Date(year, month, i), }); } // 다음 달 날짜 (6주 채우기) const remaining = 42 - days.length; // 6주 * 7일 = 42 for (let i = 1; i <= remaining; i++) { days.push({ day: i, isCurrentMonth: false, date: new Date(year, month + 1, i), }); } return { year, month, monthName: MONTH_NAMES[month], firstDay, daysInMonth, prevMonthDays, weekdays: WEEKDAYS, days, }; }, [year, month]); // 이전 월로 이동 가능 여부 const canGoPrevMonth = !(year === MIN_YEAR && month === 0); // 선택 날짜 업데이트 헬퍼 const updateSelectedDate = useCallback((newDate) => { const today = new Date(); if ( newDate.getFullYear() === today.getFullYear() && newDate.getMonth() === today.getMonth() ) { setSelectedDate(getTodayKST()); } else { const firstDay = `${newDate.getFullYear()}-${String(newDate.getMonth() + 1).padStart(2, '0')}-01`; setSelectedDate(firstDay); } }, []); // 이전 월로 이동 const goToPrevMonth = useCallback(() => { if (!canGoPrevMonth) return; const newDate = new Date(year, month - 1, 1); setCurrentDate(newDate); updateSelectedDate(newDate); }, [year, month, canGoPrevMonth, updateSelectedDate]); // 다음 월로 이동 const goToNextMonth = useCallback(() => { const newDate = new Date(year, month + 1, 1); setCurrentDate(newDate); updateSelectedDate(newDate); }, [year, month, updateSelectedDate]); // 특정 월로 이동 const goToMonth = useCallback( (newYear, newMonth) => { const newDate = new Date(newYear, newMonth, 1); setCurrentDate(newDate); updateSelectedDate(newDate); }, [updateSelectedDate] ); // 오늘로 이동 const goToToday = useCallback(() => { const today = new Date(); setCurrentDate(today); setSelectedDate(getTodayKST()); }, []); // 날짜 선택 const selectDate = useCallback( (day) => { const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; setSelectedDate(dateStr); }, [year, month] ); return { ...calendarData, currentDate, selectedDate, canGoPrev: canGoPrevMonth, canGoPrevMonth, goToPrevMonth, goToNextMonth, goToMonth, goToToday, selectDate, setSelectedDate, }; }