- 5개 파일 분석 (Schedules, ScheduleForm, ScheduleDict, ScheduleBots, ScheduleCategory) - 공통 코드 중복 문제 정리 (colorMap/getColorStyle) - 파일별 개선 사항 및 우선순위 정리 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
255 lines
6.1 KiB
Markdown
255 lines
6.1 KiB
Markdown
# 일정 관리 페이지 개선 계획
|
|
|
|
## 대상 파일
|
|
|
|
| 파일 | 라인 수 | 역할 |
|
|
|------|---------|------|
|
|
| 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줄 (모달 분리) |
|