docs: 일정 관리 페이지 개선 계획 문서 작성

- 5개 파일 분석 (Schedules, ScheduleForm, ScheduleDict, ScheduleBots, ScheduleCategory)
- 공통 코드 중복 문제 정리 (colorMap/getColorStyle)
- 파일별 개선 사항 및 우선순위 정리

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-01-23 09:28:09 +09:00
parent ecd988aa8a
commit d2f6670795

View file

@ -1,225 +1,255 @@
# 프론트엔드 개선 계획서 # 일정 관리 페이지 개선 계획
## 1. 현재 문제점 ## 대상 파일
### 1.1 API 구조 | 파일 | 라인 수 | 역할 |
``` |------|---------|------|
현재 구조: | Schedules.jsx | 1159 | 일정 목록/검색 |
api/ | ScheduleForm.jsx | 765 | 일정 추가/수정 폼 |
├── common/client.js # fetch 래퍼 | ScheduleDict.jsx | 572 | 사전 관리 |
├── index.js # 통합 export (혼란) | ScheduleBots.jsx | 570 | 봇 관리 |
├── pc/ | ScheduleCategory.jsx | 466 | 카테고리 관리 |
│ ├── admin/ # 관리자 API
│ ├── common/members.js # PC 공통?
│ └── public/ # 공개 API (어드민 함수도 섞여있음)
```
**문제점:**
- PC와 모바일이 같은 API를 사용하는데 `pc/` 폴더 안에 있음
- `api/pc/public/schedules.js`에 어드민 API 함수도 포함됨
- `transformSchedule` 함수가 여러 파일에 중복
- `api/index.js`에서 같은 함수가 여러 번 export됨
### 1.2 컴포넌트 구조
```
현재 구조:
components/
├── common/ # 공통 (OK)
├── mobile/ # 모바일 (폴더 구분 없음)
├── pc/
│ ├── admin/ # 관리자 (schedule 폴더만 있음)
│ └── public/ # 공개 (폴더 구분 없음)
```
**문제점:**
- `admin/``public/` 내부에 기능별 폴더 구분이 없음
- 파일이 많아지면 찾기 어려움
### 1.3 중복 코드
- `getMemberList`: 16개 파일에서 사용, 일부는 로컬 정의
- `decodeHtmlEntities`: 여러 파일에 중복 정의
- `transformSchedule`: api/pc/public/schedules.js, api/pc/admin/schedules.js에 중복
- `Schedules.jsx`에 유틸 함수들이 로컬로 재정의됨
### 1.4 대형 파일
| 파일 | 원래 라인 수 | 현재 라인 수 | 상태 |
|------|-------------|-------------|------|
| AlbumPhotos.jsx | 1536 | 1033 | ✅ 분리 완료 |
| Schedules.jsx | 1465 | 1159 | ✅ 분리 완료 |
| ScheduleForm.jsx | 1047 | 765 | ✅ 분리 완료 |
| ScheduleDict.jsx | 714 | 572 | ✅ 분리 완료 |
| AlbumForm.jsx | 631 | 443 | ✅ 분리 완료 |
--- ---
## 2. 개선 계획 ## 1. 공통 코드 중복 문제
### 2.1 API 구조 개선 ### 1.1 colorMap / getColorStyle 중복
``` **현황:** 3개 파일에서 동일한 코드 반복
개선 후:
api/ ```javascript
├── client.js # fetch 래퍼 // Schedules.jsx:206-224
├── transforms.js # 공통 변환 함수 // ScheduleForm.jsx:97-117
├── public/ # 공개 API (PC/Mobile 공용) // ScheduleCategory.jsx:24-36
│ ├── schedules.js const colorMap = {
│ ├── albums.js blue: 'bg-blue-500',
│ └── members.js green: 'bg-green-500',
└── admin/ # 관리자 API // ...
├── schedules.js };
├── albums.js
├── members.js const getColorStyle = (color) => {
├── categories.js if (!color) return { className: 'bg-gray-500' };
├── bots.js if (color.startsWith('#')) {
└── stats.js return { style: { backgroundColor: color } };
}
return { className: colorMap[color] || 'bg-gray-500' };
};
``` ```
### 2.2 컴포넌트 구조 개선 **개선안:**
``` ```
개선 후: utils/color.js 생성
components/ ├── COLOR_MAP (상수)
├── common/ # 공통 (유지) ├── COLOR_OPTIONS (ScheduleCategory에서 사용하는 색상 옵션)
├── mobile/ └── getColorStyle(color) (함수)
│ ├── layout/ # Layout, Header, BottomNav
│ └── schedule/ # ScheduleCard, BirthdayCard 등
├── pc/
│ ├── public/
│ │ ├── layout/ # Layout, Header, Footer
│ │ └── schedule/ # Calendar, ScheduleCard 등
│ └── admin/
│ ├── layout/ # Layout, Header
│ ├── common/ # ConfirmDialog, DatePicker 등
│ ├── schedule/ # ✅ 추가된 컴포넌트들:
│ │ ├── AdminScheduleCard.jsx
│ │ ├── CategorySelector.jsx
│ │ ├── ScheduleItem.jsx # 일정 아이템 (리스트용)
│ │ ├── LocationSearchDialog.jsx # 장소 검색 모달
│ │ ├── MemberSelector.jsx # 멤버 선택 UI
│ │ ├── ImageUploader.jsx # 이미지 업로드
│ │ └── WordItem.jsx # 사전 단어 아이템
│ └── album/ # ✅ 추가된 컴포넌트들:
│ ├── TrackItem.jsx # 트랙 입력 폼
│ ├── PendingFileItem.jsx # 업로드 대기 파일
│ ├── BulkEditPanel.jsx # 일괄 편집 도구
│ ├── PhotoGrid.jsx # 사진/티저 그리드
│ └── PhotoPreviewModal.jsx # 미리보기 모달
``` ```
### 2.3 중복 코드 제거 ### 1.2 colorOptions 상수
#### utils/schedule.js 통합 사용 **현황:** ScheduleCategory.jsx에만 있지만 확장성 고려
- `Schedules.jsx`의 로컬 함수들 제거
- 모든 곳에서 `@/utils/schedule` import
#### utils/format.js에 decodeHtmlEntities 통합 ```javascript
- 중복 정의 제거 // ScheduleCategory.jsx:13-22
const colorOptions = [
#### api/transforms.js 생성 { id: 'blue', name: '파란색', bg: 'bg-blue-500', hex: '#3b82f6' },
- `transformSchedule` 함수 통합 // ...
];
### 2.4 대형 파일 분리
#### AlbumPhotos.jsx (1536줄)
```
pages/pc/admin/albums/
├── AlbumPhotos.jsx # 메인 (상태 관리)
├── components/
│ ├── PhotoUploader.jsx # 업로드 UI
│ ├── PhotoGrid.jsx # 사진 그리드
│ ├── PhotoPreview.jsx # 미리보기 모달
│ └── BulkEditTools.jsx # 일괄 편집 도구
└── hooks/
└── usePhotoUpload.js # 업로드 로직
``` ```
#### Schedules.jsx (1471줄) **개선안:** `constants/colors.js` 또는 `utils/color.js`에 통합
```
pages/pc/admin/schedules/ ---
├── Schedules.jsx # 메인
├── components/ ## 2. 파일별 개선 사항
│ ├── ScheduleList.jsx # 목록 (가상화)
│ ├── ScheduleSearch.jsx # 검색 UI ### 2.1 Schedules.jsx (1159줄)
│ └── MonthNavigator.jsx # 월 선택
└── hooks/ #### 검색 관련 상태/로직 복잡
└── useScheduleList.js # 목록 조회 로직
**현황:** 검색 관련 상태가 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);
``` ```
#### ScheduleForm.jsx (1046줄) **개선안:** `useScheduleSearch` 커스텀 훅 분리
```javascript
// hooks/pc/admin/useScheduleSearch.js
export function useScheduleSearch() {
// 검색 상태 및 로직 캡슐화
return {
searchInput, setSearchInput,
suggestions, isLoadingSuggestions,
handleSearch, handleSuggestionSelect,
// ...
};
}
``` ```
pages/pc/admin/schedules/
├── ScheduleForm.jsx # 메인 #### 달력 로직 분리 가능
└── components/
├── BasicInfoForm.jsx # 기본 정보 **현황:** 달력 관련 계산이 컴포넌트 내부에 산재
├── MemberSelector.jsx # 멤버 선택
└── SourceInput.jsx # 출처 입력 ```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);
``` ```
--- ---
## 3. 작업 순서 ### 2.3 ScheduleDict.jsx (572줄)
### Phase 1: 구조 정리 (비파괴적) ✅ 완료 #### generateId 일관성
1. [x] API 구조 개선 (폴더 재배치)
- `api/pc/`, `api/common/``api/public/`, `api/admin/`
2. [x] 미사용 코드 제거
- utils/format.js, utils/date.js 미사용 함수
- constants/index.js 미사용 상수
- 미사용 hooks, stores
3. [x] 컴포넌트 폴더 구조화 (layout/, common/, schedule/ 등)
4. [x] CATEGORY_ID 하드코딩 제거
- constants에서 삭제
- 카테고리 API 데이터의 `name`으로 비교하도록 변경
5. [x] 봇 관리 페이지 애니메이션 추가
- 페이지 stagger 애니메이션
- 통계 카드 AnimatedNumber
### Phase 2: 대형 파일 분리 (진행 중) **현황:** `generateId`를 useCallback으로 정의했지만, `parseDict` 내부에서는 인라인으로 같은 로직 사용
1. [x] Schedules.jsx 분리 (1465줄 → 1159줄, 306줄 감소)
- `ScheduleItem.jsx` 컴포넌트 추출
- 검색 모드와 일반 모드에서 공통 사용
2. [x] ScheduleForm.jsx 분리 (1047줄 → 765줄, 282줄 감소)
- `LocationSearchDialog.jsx` 추출 (장소 검색 모달)
- `MemberSelector.jsx` 추출 (멤버 선택 UI)
- `ImageUploader.jsx` 추출 (이미지 업로드 및 드래그앤드롭)
3. [x] ScheduleDict.jsx 분리 (714줄 → 572줄, 142줄 감소)
- `WordItem.jsx` 추출 (단어 테이블 행 컴포넌트)
- `POS_TAGS` 상수 함께 분리
4. [x] AlbumForm.jsx 분리 (631줄 → 443줄, 188줄 감소)
- `CustomSelect.jsx` 추출 (공통 드롭다운 → common/)
- `TrackItem.jsx` 추출 (트랙 입력 폼 → album/)
5. [x] AlbumPhotos.jsx 분리 (1536줄 → 1033줄, 503줄 감소)
- `PendingFileItem.jsx` 추출 (업로드 대기 파일 아이템)
- `BulkEditPanel.jsx` 추출 (일괄 편집 도구)
- `PhotoGrid.jsx` 추출 (사진/티저 그리드)
- `PhotoPreviewModal.jsx` 추출 (미리보기 모달)
### Phase 3: 추가 개선 ```javascript
1. [x] 관리자 페이지용 에러 페이지 추가 (404) // 113-116줄
2. [ ] 모든 페이지 동작 확인 const generateId = useCallback(
3. [ ] 빌드 확인 () => `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
[]
);
// 128줄 (parseDict 내부)
id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
```
**개선안:** `generateId`를 외부 유틸 함수로 분리하거나, parseDict에서 generateId 참조
--- ---
## 4. 삭제된 항목 ### 2.4 ScheduleBots.jsx (570줄)
### constants/index.js #### 인라인 컴포넌트 분리
- `CATEGORY_ID` (하드코딩 → API name 비교로 변경)
- `CATEGORY_NAMES` (미사용)
- `ALBUM_TYPES` (미사용)
- `SOCIAL_LINKS.tiktok` (불필요)
- `SOCIAL_LINKS.fancafe` (불필요)
### API 구조 **현황:** AnimatedNumber, XIcon, MeilisearchIcon이 파일 내부에 정의
- `api/pc/` 폴더 → `api/public/`, `api/admin/`으로 변경 완료
- `api/common/``api/client.js`로 통합 완료
### utils ```javascript
- `format.js`: formatNumber, formatViewCount, formatFileSize, formatDuration, truncateText // 42-65줄
- `date.js`: nowKST, parseDateKST, isPast, isFuture function AnimatedNumber({ value, className = '' }) { ... }
### hooks // 68-72줄
- useCalendar, useLightbox, useMediaQuery, useScheduleFiltering, useScheduleSearch const XIcon = ({ size = 20, fill = 'currentColor' }) => ( ... );
### stores // 75-128줄
- useUIStore 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줄 (모달 분리) |