docs: 일정 관리 페이지 개선 계획 문서 작성
- 5개 파일 분석 (Schedules, ScheduleForm, ScheduleDict, ScheduleBots, ScheduleCategory) - 공통 코드 중복 문제 정리 (colorMap/getColorStyle) - 파일별 개선 사항 및 우선순위 정리 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ecd988aa8a
commit
d2f6670795
1 changed files with 221 additions and 191 deletions
|
|
@ -1,225 +1,255 @@
|
|||
# 프론트엔드 개선 계획서
|
||||
# 일정 관리 페이지 개선 계획
|
||||
|
||||
## 1. 현재 문제점
|
||||
## 대상 파일
|
||||
|
||||
### 1.1 API 구조
|
||||
```
|
||||
현재 구조:
|
||||
api/
|
||||
├── common/client.js # fetch 래퍼
|
||||
├── index.js # 통합 export (혼란)
|
||||
├── pc/
|
||||
│ ├── 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 | ✅ 분리 완료 |
|
||||
| 파일 | 라인 수 | 역할 |
|
||||
|------|---------|------|
|
||||
| Schedules.jsx | 1159 | 일정 목록/검색 |
|
||||
| ScheduleForm.jsx | 765 | 일정 추가/수정 폼 |
|
||||
| ScheduleDict.jsx | 572 | 사전 관리 |
|
||||
| ScheduleBots.jsx | 570 | 봇 관리 |
|
||||
| ScheduleCategory.jsx | 466 | 카테고리 관리 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 개선 계획
|
||||
## 1. 공통 코드 중복 문제
|
||||
|
||||
### 2.1 API 구조 개선
|
||||
### 1.1 colorMap / getColorStyle 중복
|
||||
|
||||
```
|
||||
개선 후:
|
||||
api/
|
||||
├── client.js # fetch 래퍼
|
||||
├── transforms.js # 공통 변환 함수
|
||||
├── public/ # 공개 API (PC/Mobile 공용)
|
||||
│ ├── schedules.js
|
||||
│ ├── albums.js
|
||||
│ └── members.js
|
||||
└── admin/ # 관리자 API
|
||||
├── schedules.js
|
||||
├── albums.js
|
||||
├── members.js
|
||||
├── categories.js
|
||||
├── bots.js
|
||||
└── stats.js
|
||||
**현황:** 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' };
|
||||
};
|
||||
```
|
||||
|
||||
### 2.2 컴포넌트 구조 개선
|
||||
|
||||
**개선안:**
|
||||
```
|
||||
개선 후:
|
||||
components/
|
||||
├── common/ # 공통 (유지)
|
||||
├── mobile/
|
||||
│ ├── 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 # 미리보기 모달
|
||||
utils/color.js 생성
|
||||
├── COLOR_MAP (상수)
|
||||
├── COLOR_OPTIONS (ScheduleCategory에서 사용하는 색상 옵션)
|
||||
└── getColorStyle(color) (함수)
|
||||
```
|
||||
|
||||
### 2.3 중복 코드 제거
|
||||
### 1.2 colorOptions 상수
|
||||
|
||||
#### utils/schedule.js 통합 사용
|
||||
- `Schedules.jsx`의 로컬 함수들 제거
|
||||
- 모든 곳에서 `@/utils/schedule` import
|
||||
**현황:** ScheduleCategory.jsx에만 있지만 확장성 고려
|
||||
|
||||
#### utils/format.js에 decodeHtmlEntities 통합
|
||||
- 중복 정의 제거
|
||||
|
||||
#### api/transforms.js 생성
|
||||
- `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 # 업로드 로직
|
||||
```javascript
|
||||
// ScheduleCategory.jsx:13-22
|
||||
const colorOptions = [
|
||||
{ id: 'blue', name: '파란색', bg: 'bg-blue-500', hex: '#3b82f6' },
|
||||
// ...
|
||||
];
|
||||
```
|
||||
|
||||
#### Schedules.jsx (1471줄)
|
||||
```
|
||||
pages/pc/admin/schedules/
|
||||
├── Schedules.jsx # 메인
|
||||
├── components/
|
||||
│ ├── ScheduleList.jsx # 목록 (가상화)
|
||||
│ ├── ScheduleSearch.jsx # 검색 UI
|
||||
│ └── MonthNavigator.jsx # 월 선택
|
||||
└── hooks/
|
||||
└── useScheduleList.js # 목록 조회 로직
|
||||
**개선안:** `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);
|
||||
```
|
||||
|
||||
#### 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: 구조 정리 (비파괴적) ✅ 완료
|
||||
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
|
||||
#### generateId 일관성
|
||||
|
||||
### Phase 2: 대형 파일 분리 (진행 중)
|
||||
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` 추출 (미리보기 모달)
|
||||
**현황:** `generateId`를 useCallback으로 정의했지만, `parseDict` 내부에서는 인라인으로 같은 로직 사용
|
||||
|
||||
### Phase 3: 추가 개선
|
||||
1. [x] 관리자 페이지용 에러 페이지 추가 (404)
|
||||
2. [ ] 모든 페이지 동작 확인
|
||||
3. [ ] 빌드 확인
|
||||
```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 참조
|
||||
|
||||
---
|
||||
|
||||
## 4. 삭제된 항목
|
||||
### 2.4 ScheduleBots.jsx (570줄)
|
||||
|
||||
### constants/index.js
|
||||
- `CATEGORY_ID` (하드코딩 → API name 비교로 변경)
|
||||
- `CATEGORY_NAMES` (미사용)
|
||||
- `ALBUM_TYPES` (미사용)
|
||||
- `SOCIAL_LINKS.tiktok` (불필요)
|
||||
- `SOCIAL_LINKS.fancafe` (불필요)
|
||||
#### 인라인 컴포넌트 분리
|
||||
|
||||
### API 구조
|
||||
- `api/pc/` 폴더 → `api/public/`, `api/admin/`으로 변경 완료
|
||||
- `api/common/` → `api/client.js`로 통합 완료
|
||||
**현황:** AnimatedNumber, XIcon, MeilisearchIcon이 파일 내부에 정의
|
||||
|
||||
### utils
|
||||
- `format.js`: formatNumber, formatViewCount, formatFileSize, formatDuration, truncateText
|
||||
- `date.js`: nowKST, parseDateKST, isPast, isFuture
|
||||
```javascript
|
||||
// 42-65줄
|
||||
function AnimatedNumber({ value, className = '' }) { ... }
|
||||
|
||||
### hooks
|
||||
- useCalendar, useLightbox, useMediaQuery, useScheduleFiltering, useScheduleSearch
|
||||
// 68-72줄
|
||||
const XIcon = ({ size = 20, fill = 'currentColor' }) => ( ... );
|
||||
|
||||
### stores
|
||||
- useUIStore
|
||||
// 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줄 (모달 분리) |
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue