# 일정 관리 페이지 개선 계획 ## 대상 파일 | 파일 | 라인 수 | 역할 | |------|---------|------| | Schedules.jsx | 1159 | 일정 목록/검색 | | ScheduleForm.jsx | 765 | 일정 추가/수정 폼 | | ScheduleDict.jsx | 572 | 사전 관리 | | ScheduleBots.jsx | 570 | 봇 관리 | | ScheduleCategory.jsx | 466 | 카테고리 관리 | --- ## 1. 공통 코드 중복 문제 ### 1.1 colorMap / getColorStyle 중복 **현황:** 3개 파일에서 동일한 코드 반복 ```javascript // Schedules.jsx:206-224 // ScheduleForm.jsx:97-117 // ScheduleCategory.jsx:24-36 const colorMap = { blue: 'bg-blue-500', green: 'bg-green-500', // ... }; const getColorStyle = (color) => { if (!color) return { className: 'bg-gray-500' }; if (color.startsWith('#')) { return { style: { backgroundColor: color } }; } return { className: colorMap[color] || 'bg-gray-500' }; }; ``` **개선안:** ``` utils/color.js 생성 ├── COLOR_MAP (상수) ├── COLOR_OPTIONS (ScheduleCategory에서 사용하는 색상 옵션) └── getColorStyle(color) (함수) ``` ### 1.2 colorOptions 상수 **현황:** ScheduleCategory.jsx에만 있지만 확장성 고려 ```javascript // ScheduleCategory.jsx:13-22 const colorOptions = [ { id: 'blue', name: '파란색', bg: 'bg-blue-500', hex: '#3b82f6' }, // ... ]; ``` **개선안:** `constants/colors.js` 또는 `utils/color.js`에 통합 --- ## 2. 파일별 개선 사항 ### 2.1 Schedules.jsx (1159줄) #### 검색 관련 상태/로직 복잡 **현황:** 검색 관련 상태가 10개 이상, useEffect 5개 ```javascript // 검색 관련 상태 (55-65줄) const [showSuggestions, setShowSuggestions] = useState(false); const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(-1); const [originalSearchQuery, setOriginalSearchQuery] = useState(''); const [suggestions, setSuggestions] = useState([]); const [isLoadingSuggestions, setIsLoadingSuggestions] = useState(false); ``` **개선안:** `useScheduleSearch` 커스텀 훅 분리 ```javascript // hooks/pc/admin/useScheduleSearch.js export function useScheduleSearch() { // 검색 상태 및 로직 캡슐화 return { searchInput, setSearchInput, suggestions, isLoadingSuggestions, handleSearch, handleSuggestionSelect, // ... }; } ``` #### 달력 로직 분리 가능 **현황:** 달력 관련 계산이 컴포넌트 내부에 산재 ```javascript // 161-181줄 const year = currentDate.getFullYear(); const month = currentDate.getMonth(); const firstDay = new Date(year, month, 1).getDay(); // ... ``` **개선안:** 기존 `utils/date.js`에 달력 헬퍼 함수 추가 또는 `useCalendar` 훅 생성 --- ### 2.2 ScheduleForm.jsx (765줄) #### fetchSchedule 함수 중복 설정 **현황:** formData를 두 번 설정 (비효율) ```javascript // 140-158줄: 첫 번째 setFormData setFormData({ title: data.title || '', startDate: data.date ? formatDate(data.date) : '', // ... }); // 163-184줄: 두 번째 setFormData (기존 이미지 처리 시) if (data.images && data.images.length > 0) { setFormData((prev) => ({ ...prev, title: data.title || '', // 중복! // ... })); } ``` **개선안:** 하나의 setFormData로 통합 ```javascript const initialFormData = { title: data.title || '', startDate: data.date ? formatDate(data.date) : '', // ... images: data.images?.map((img) => ({ id: img.id, url: img.image_url })) || [], }; setFormData(initialFormData); ``` --- ### 2.3 ScheduleDict.jsx (572줄) #### generateId 일관성 **현황:** `generateId`를 useCallback으로 정의했지만, `parseDict` 내부에서는 인라인으로 같은 로직 사용 ```javascript // 113-116줄 const generateId = useCallback( () => `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, [] ); // 128줄 (parseDict 내부) id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, ``` **개선안:** `generateId`를 외부 유틸 함수로 분리하거나, parseDict에서 generateId 참조 --- ### 2.4 ScheduleBots.jsx (570줄) #### 인라인 컴포넌트 분리 **현황:** AnimatedNumber, XIcon, MeilisearchIcon이 파일 내부에 정의 ```javascript // 42-65줄 function AnimatedNumber({ value, className = '' }) { ... } // 68-72줄 const XIcon = ({ size = 20, fill = 'currentColor' }) => ( ... ); // 75-128줄 const MeilisearchIcon = ({ size = 20 }) => ( ... ); ``` **개선안:** ``` components/common/ ├── AnimatedNumber.jsx (재사용 가능한 애니메이션 숫자) └── icons/ ├── XIcon.jsx └── MeilisearchIcon.jsx ``` #### 봇 카드 컴포넌트 분리 **현황:** 봇 카드 렌더링이 414-559줄로 약 145줄 **개선안:** ```javascript // components/pc/admin/bot/BotCard.jsx function BotCard({ bot, onToggle, onSync, syncing }) { // ... } ``` --- ### 2.5 ScheduleCategory.jsx (466줄) #### 모달 컴포넌트 인라인 **현황:** 카테고리 추가/수정 모달이 284-445줄로 약 160줄 **개선안:** ```javascript // components/pc/admin/schedule/CategoryFormModal.jsx function CategoryFormModal({ isOpen, onClose, category, onSave }) { // 색상 선택, 폼 로직 포함 } ``` --- ## 3. 개선 우선순위 ### Phase 1: 중복 코드 제거 (빠른 효과) 1. [ ] `utils/color.js` 생성 - colorMap, getColorStyle, COLOR_OPTIONS 통합 2. [ ] 3개 파일에서 import로 교체 ### Phase 2: 커스텀 훅 분리 (복잡도 감소) 1. [ ] `useScheduleSearch` 훅 생성 - Schedules.jsx 검색 로직 분리 2. [ ] 달력 관련 로직 정리 ### Phase 3: 컴포넌트 분리 (재사용성) 1. [ ] `AnimatedNumber` 공통 컴포넌트화 2. [ ] `BotCard` 컴포넌트 분리 3. [ ] `CategoryFormModal` 컴포넌트 분리 4. [ ] SVG 아이콘 분리 (XIcon, MeilisearchIcon) ### Phase 4: 코드 정리 1. [ ] ScheduleForm.jsx - fetchSchedule 중복 제거 2. [ ] ScheduleDict.jsx - generateId 일관성 --- ## 4. 예상 효과 | 항목 | 현재 | 개선 후 | |------|------|---------| | colorMap/getColorStyle 중복 | 3곳 | 1곳 (utils) | | Schedules.jsx 상태 수 | ~20개 | ~12개 (훅 분리) | | ScheduleBots.jsx | 570줄 | ~400줄 (컴포넌트 분리) | | ScheduleCategory.jsx | 466줄 | ~300줄 (모달 분리) |