diff --git a/docs/frontend-improvement.md b/docs/frontend-improvement.md index e4965a6..930b47f 100644 --- a/docs/frontend-improvement.md +++ b/docs/frontend-improvement.md @@ -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줄 (모달 분리) |