- PC 컴포넌트를 components/pc/로 이동 (Calendar, ScheduleCard, BirthdayCard, CategoryFilter) - Mobile 컴포넌트를 components/mobile/로 이동 (Mobile 접두사 제거) - components/schedule/에는 공용 코드만 유지 (confetti.js, AdminScheduleCard) - Schedule, Home 페이지의 import 경로 업데이트 - 관련 문서 업데이트 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
451 lines
13 KiB
Markdown
451 lines
13 KiB
Markdown
# Express → Fastify 마이그레이션
|
|
|
|
## 개요
|
|
|
|
`backend-backup/` (Express) → `backend/` (Fastify)로 마이그레이션 완료
|
|
|
|
## 완료된 작업
|
|
|
|
### 서버 기반
|
|
- [x] Fastify 앱 구조 (`src/app.js`, `src/server.js`)
|
|
- [x] 플러그인 시스템 (`src/plugins/`)
|
|
- db.js (MariaDB)
|
|
- redis.js
|
|
- auth.js (JWT)
|
|
- meilisearch.js
|
|
- scheduler.js (봇 스케줄러)
|
|
|
|
### API 라우트 (`src/routes/`)
|
|
- [x] 인증 (`/api/auth`)
|
|
- POST /login - 로그인
|
|
- GET /verify - 토큰 검증
|
|
- [x] 멤버 (`/api/members`)
|
|
- GET / - 목록 조회
|
|
- GET /:name - 상세 조회
|
|
- PUT /:name - 수정 (이미지 업로드 포함)
|
|
- [x] 앨범 (`/api/albums`)
|
|
- GET / - 목록 조회
|
|
- GET /:id - ID로 조회
|
|
- GET /by-name/:name - 이름으로 조회
|
|
- GET /by-name/:albumName/track/:trackTitle - 트랙 조회
|
|
- POST / - 생성
|
|
- PUT /:id - 수정
|
|
- DELETE /:id - 삭제
|
|
- 사진 관리 (`/api/albums/:id/photos`)
|
|
- GET / - 목록
|
|
- POST / - 업로드
|
|
- PUT /:photoId - 수정
|
|
- DELETE /:photoId - 삭제
|
|
- 티저 관리 (`/api/albums/:id/teasers`)
|
|
- GET / - 목록
|
|
- POST / - 업로드
|
|
- DELETE /:teaserId - 삭제
|
|
- [x] 일정 (`/api/schedules`)
|
|
- GET / - 월별 조회 (생일 포함)
|
|
- GET /?search= - Meilisearch 검색
|
|
- GET /:id - 상세 조회
|
|
- DELETE /:id - 삭제
|
|
- POST /sync-search - Meilisearch 동기화
|
|
- [x] 추천 검색어 (`/api/schedules/suggestions`)
|
|
- GET / - 추천 검색어 조회
|
|
- GET /popular - 인기 검색어 조회
|
|
- POST /save - 검색어 저장
|
|
- GET /dict - 사용자 사전 조회 (관리자)
|
|
- PUT /dict - 사용자 사전 저장 (관리자)
|
|
- [x] 통계 (`/api/stats`)
|
|
- GET / - 대시보드 통계
|
|
|
|
### 관리자 API (`src/routes/admin/`)
|
|
- [x] 봇 관리 (`/api/admin/bots`)
|
|
- GET / - 봇 목록
|
|
- POST /:id/start - 봇 시작
|
|
- POST /:id/stop - 봇 정지
|
|
- POST /:id/sync-all - 전체 동기화
|
|
- GET /quota-warning - 할당량 경고 조회
|
|
- DELETE /quota-warning - 할당량 경고 해제
|
|
- [x] YouTube 관리 (`/api/admin/youtube`)
|
|
- GET /video-info - 영상 정보 조회
|
|
- POST /schedule - 일정 저장
|
|
- PUT /schedule/:id - 일정 수정
|
|
- [x] X 관리 (`/api/admin/x`)
|
|
- GET /post-info - 게시글 정보 조회
|
|
- POST /schedule - 일정 저장
|
|
|
|
### 서비스 (`src/services/`)
|
|
- [x] YouTube 봇 (`services/youtube/`)
|
|
- 영상 자동 수집
|
|
- 채널별 필터링 (제목 필터, 멤버 추출)
|
|
- [x] X(Twitter) 봇 (`services/x/`)
|
|
- Nitter 스크래핑
|
|
- 이미지 URL 추출
|
|
- [x] Meilisearch 검색 (`services/meilisearch/`)
|
|
- 일정 검색
|
|
- 전체 동기화
|
|
- [x] 추천 검색어 (`services/suggestions/`)
|
|
- 형태소 분석 (kiwi-nlp)
|
|
- bi-gram 빈도
|
|
- 초성 검색
|
|
- 사용자 사전 관리
|
|
- [x] 이미지 업로드 (`services/image.js`)
|
|
- 앨범 커버
|
|
- 멤버 이미지
|
|
- 앨범 사진/티저
|
|
|
|
## 남은 작업 (미구현)
|
|
|
|
### 일반 일정 CRUD
|
|
- [ ] POST /api/schedules - 일정 생성 (일반)
|
|
- [ ] PUT /api/schedules/:id - 일정 수정 (일반)
|
|
|
|
※ 현재는 YouTube/X 전용 일정 생성 API만 구현됨
|
|
|
|
### 카테고리 관리
|
|
- [ ] POST /api/schedule-categories - 생성
|
|
- [ ] PUT /api/schedule-categories/:id - 수정
|
|
- [ ] DELETE /api/schedule-categories/:id - 삭제
|
|
- [ ] PUT /api/schedule-categories-order - 순서 변경
|
|
|
|
※ GET은 구현됨 (목록 조회)
|
|
|
|
### 기타
|
|
- [ ] GET /api/kakao/places - 카카오 장소 검색 프록시
|
|
|
|
## 파일 비교표
|
|
|
|
| Express (backend-backup) | Fastify (backend) | 상태 |
|
|
|--------------------------|-------------------|------|
|
|
| routes/admin.js (로그인) | routes/auth.js | 완료 |
|
|
| routes/admin.js (앨범 CRUD) | routes/albums/index.js | 완료 |
|
|
| routes/admin.js (사진/티저) | routes/albums/photos.js, teasers.js | 완료 |
|
|
| routes/admin.js (멤버 수정) | routes/members/index.js | 완료 |
|
|
| routes/admin.js (일정 삭제) | routes/schedules/index.js | 완료 |
|
|
| routes/admin.js (일정 생성/수정) | - | 미완료 |
|
|
| routes/admin.js (카테고리 CUD) | - | 미완료 |
|
|
| routes/admin.js (봇 관리) | routes/admin/bots.js | 완료 |
|
|
| routes/admin.js (할당량) | routes/admin/bots.js | 완료 |
|
|
| routes/admin.js (카카오) | - | 미완료 |
|
|
| - | routes/admin/youtube.js | 신규 |
|
|
| - | routes/admin/x.js | 신규 |
|
|
| routes/albums.js | routes/albums/index.js | 완료 |
|
|
| routes/members.js | routes/members/index.js | 완료 |
|
|
| routes/schedules.js | routes/schedules/index.js | 완료 |
|
|
| routes/stats.js | routes/stats/index.js | 완료 |
|
|
| services/youtube-bot.js | services/youtube/ | 완료 |
|
|
| services/youtube-scheduler.js | plugins/scheduler.js | 완료 |
|
|
| services/x-bot.js | services/x/ | 완료 |
|
|
| services/meilisearch.js | services/meilisearch/ | 완료 |
|
|
| services/meilisearch-bot.js | services/meilisearch/ | 완료 |
|
|
| services/suggestions.js | services/suggestions/ | 완료 |
|
|
|
|
## 참고 사항
|
|
|
|
- 기존 Express 코드는 `backend-backup/` 폴더에 보존
|
|
- 마이그레이션 시 기존 코드 참조하여 동일 기능 구현
|
|
- DB 스키마 변경 사항:
|
|
- `tracks` → `album_tracks` (이름 변경)
|
|
- `venues` → `concert_venues` (이름 변경)
|
|
|
|
---
|
|
|
|
# 프론트엔드 마이그레이션 (Strangler Fig)
|
|
|
|
## 개요
|
|
|
|
`frontend/` (레거시) → `frontend-temp/` (신규)로 점진적 마이그레이션
|
|
|
|
## 설계 원칙
|
|
|
|
1. **react-device-detect 사용**
|
|
- App.jsx에서 `BrowserView`/`MobileView`로 PC/Mobile 완전 분리
|
|
- User Agent 기반 디바이스 감지
|
|
|
|
2. **기능별 폴더 + pc/mobile 하위 폴더**
|
|
- 각 페이지 폴더 내에 `pc/`, `mobile/` 서브폴더
|
|
- 비즈니스 로직(api, hooks, stores)은 최상위에서 공유
|
|
|
|
3. **React Query 사용**
|
|
- `useQuery`로 데이터 패칭 및 캐싱
|
|
|
|
4. **관리자 페이지는 PC 전용**
|
|
- mobile 폴더 없이 단일 구조
|
|
|
|
## App.jsx 라우팅 구조
|
|
|
|
```jsx
|
|
import { BrowserView, MobileView } from 'react-device-detect';
|
|
|
|
function App() {
|
|
return (
|
|
<BrowserRouter>
|
|
<BrowserView>
|
|
<Routes>
|
|
{/* Admin routes */}
|
|
<Route path="/admin" element={<AdminLogin />} />
|
|
<Route path="/admin/*" element={<AdminRoutes />} />
|
|
|
|
{/* Public routes with PC Layout */}
|
|
<Route element={<PCLayout />}>
|
|
<Route path="/" element={<PCHome />} />
|
|
<Route path="/members" element={<PCMembers />} />
|
|
<Route path="/album" element={<PCAlbum />} />
|
|
<Route path="/album/:name" element={<PCAlbumDetail />} />
|
|
...
|
|
</Route>
|
|
</Routes>
|
|
</BrowserView>
|
|
|
|
<MobileView>
|
|
<Routes>
|
|
<Route element={<MobileLayout />}>
|
|
<Route path="/" element={<MobileHome />} />
|
|
<Route path="/members" element={<MobileMembers />} />
|
|
...
|
|
</Route>
|
|
</Routes>
|
|
</MobileView>
|
|
</BrowserRouter>
|
|
);
|
|
}
|
|
```
|
|
|
|
## 전체 마이그레이션 체크리스트
|
|
|
|
### API 계층
|
|
|
|
#### 공개 API
|
|
- [x] client.js (fetchApi, fetchAuthApi)
|
|
- [x] albums.js
|
|
- [x] members.js
|
|
- [x] schedules.js
|
|
- [x] auth.js
|
|
|
|
#### 관리자 API (`api/admin/`)
|
|
- [ ] albums.js
|
|
- [ ] members.js
|
|
- [ ] schedules.js
|
|
- [ ] categories.js
|
|
- [ ] stats.js
|
|
- [ ] bots.js
|
|
- [ ] suggestions.js
|
|
|
|
### 훅 (hooks/)
|
|
- [x] useAlbumData.js
|
|
- [x] useMemberData.js
|
|
- [x] useScheduleData.js
|
|
- [x] useScheduleSearch.js
|
|
- [x] useScheduleFiltering.js
|
|
- [x] useCalendar.js
|
|
- [x] useMediaQuery.js
|
|
- [x] useAdminAuth.js
|
|
- [ ] useToast.js (Toast 컴포넌트는 있으나 훅은 미구현)
|
|
|
|
### 스토어 (stores/)
|
|
- [x] useScheduleStore.js
|
|
- [x] useAuthStore.js
|
|
- [x] useUIStore.js
|
|
|
|
### 상수 (constants/)
|
|
- [x] index.js (CATEGORY_ID, MIN_YEAR, SEARCH_LIMIT 등)
|
|
|
|
### 유틸리티 (utils/)
|
|
- [x] index.js
|
|
- [x] date.js
|
|
- [x] format.js
|
|
- [x] cn.js (className 유틸)
|
|
- [x] schedule.js (일정 관련 유틸)
|
|
|
|
### 공통 컴포넌트 (components/common/)
|
|
- [x] Loading.jsx
|
|
- [x] ErrorBoundary.jsx
|
|
- [x] Toast.jsx
|
|
- [x] Lightbox.jsx
|
|
- [x] LightboxIndicator.jsx
|
|
- [x] Tooltip.jsx
|
|
- [x] ScrollToTop.jsx
|
|
|
|
### PC 컴포넌트 (components/pc/)
|
|
- [x] Layout.jsx
|
|
- [x] Header.jsx
|
|
- [x] Footer.jsx
|
|
- [x] Calendar.jsx
|
|
- [x] ScheduleCard.jsx
|
|
- [x] BirthdayCard.jsx
|
|
- [x] CategoryFilter.jsx
|
|
|
|
### Mobile 컴포넌트 (components/mobile/)
|
|
- [x] Layout.jsx
|
|
- [x] Calendar.jsx
|
|
- [x] ScheduleCard.jsx
|
|
- [x] ScheduleListCard.jsx
|
|
- [x] ScheduleSearchCard.jsx
|
|
- [x] BirthdayCard.jsx
|
|
|
|
### 공용 일정 컴포넌트 (components/schedule/)
|
|
- [x] confetti.js (fireBirthdayConfetti)
|
|
- [x] AdminScheduleCard.jsx
|
|
|
|
### 관리자 컴포넌트 (components/admin/)
|
|
- [ ] AdminLayout.jsx
|
|
- [ ] AdminHeader.jsx
|
|
- [ ] ConfirmDialog.jsx
|
|
- [ ] CustomDatePicker.jsx
|
|
- [ ] CustomTimePicker.jsx
|
|
- [ ] NumberPicker.jsx
|
|
|
|
### 페이지 - Home (pages/home/)
|
|
- [x] pc/Home.jsx
|
|
- [x] mobile/Home.jsx
|
|
|
|
### 페이지 - Members (pages/members/)
|
|
- [x] pc/Members.jsx
|
|
- [x] mobile/Members.jsx
|
|
|
|
### 페이지 - Album (pages/album/)
|
|
- [x] pc/Album.jsx
|
|
- [x] pc/AlbumDetail.jsx
|
|
- [x] pc/AlbumGallery.jsx
|
|
- [x] pc/TrackDetail.jsx
|
|
- [x] mobile/Album.jsx
|
|
- [x] mobile/AlbumDetail.jsx
|
|
- [x] mobile/AlbumGallery.jsx
|
|
- [x] mobile/TrackDetail.jsx
|
|
|
|
### 페이지 - Schedule (pages/schedule/)
|
|
- [x] sections/DefaultSection.jsx
|
|
- [x] sections/XSection.jsx
|
|
- [x] sections/YoutubeSection.jsx
|
|
- [x] sections/utils.js
|
|
- [x] sections/index.js
|
|
- [x] pc/Schedule.jsx
|
|
- [x] pc/ScheduleDetail.jsx
|
|
- [x] pc/Birthday.jsx
|
|
- [x] mobile/Schedule.jsx
|
|
- [x] mobile/ScheduleDetail.jsx
|
|
- [x] mobile/Birthday.jsx
|
|
|
|
### 페이지 - Common (pages/common/)
|
|
- [ ] pc/NotFound.jsx
|
|
- [ ] mobile/NotFound.jsx
|
|
|
|
### 페이지 - Admin (pages/admin/) - PC 전용
|
|
- [ ] Login.jsx
|
|
- [ ] Dashboard.jsx
|
|
- [ ] members/List.jsx
|
|
- [ ] members/Edit.jsx
|
|
- [ ] albums/List.jsx
|
|
- [ ] albums/Form.jsx
|
|
- [ ] albums/Photos.jsx
|
|
- [ ] schedules/List.jsx
|
|
- [ ] schedules/Form.jsx
|
|
- [ ] schedules/YouTubeForm.jsx
|
|
- [ ] schedules/XForm.jsx
|
|
- [ ] schedules/YouTubeEditForm.jsx
|
|
- [ ] categories/List.jsx
|
|
- [ ] bots/Manager.jsx
|
|
- [ ] dict/Manager.jsx
|
|
|
|
### 기타
|
|
- [x] App.jsx (BrowserView/MobileView 라우팅)
|
|
- [x] main.jsx
|
|
|
|
### CSS 파일
|
|
- [x] index.css
|
|
- [x] mobile.css (모바일 전용 스타일, 달력 등)
|
|
- [x] pc.css (PC 전용 스타일)
|
|
|
|
### 기타 파일
|
|
- [ ] data/dummy.js (개발용 더미 데이터, 필요 시 추가)
|
|
- [ ] .env (VITE_KAKAO_JS_KEY - 콘서트 장소 검색용, 관리자 기능에서 사용)
|
|
- [ ] public/favicon.ico (필요 시 추가)
|
|
|
|
## 사용 라이브러리 (package.json)
|
|
|
|
| 라이브러리 | 용도 | 사용 위치 |
|
|
|-----------|------|----------|
|
|
| react-device-detect | PC/Mobile 분기 | App.jsx |
|
|
| @tanstack/react-query | 데이터 패칭 | 모든 페이지 |
|
|
| @tanstack/react-virtual | 가상화 리스트 | 관리자 일정 목록 |
|
|
| react-calendar | 캘린더 | 일정 페이지 |
|
|
| react-colorful | 색상 선택 | 카테고리 관리 |
|
|
| react-photo-album | 앨범 갤러리 | 앨범 갤러리 |
|
|
| react-infinite-scroll-component | 무한 스크롤 | 일정 검색 |
|
|
| react-intersection-observer | 뷰포트 감지 | 애니메이션 |
|
|
| react-ios-time-picker | 시간 선택 | 일정 폼 |
|
|
| react-linkify | URL 자동 링크 | 일정 상세 |
|
|
| react-window | 가상화 리스트 | 긴 목록 |
|
|
| swiper | 슬라이더 | 앨범 상세 |
|
|
| canvas-confetti | 축하 효과 | 생일 페이지 |
|
|
| framer-motion | 애니메이션 | 전체 |
|
|
| dayjs | 날짜 처리 | 전체 |
|
|
| zustand | 상태 관리 | 전체 |
|
|
|
|
## 특수 패턴 및 주의사항
|
|
|
|
### React Query 고급 사용
|
|
- `useInfiniteQuery` - 일정 검색 무한 스크롤 (mobile/Schedule)
|
|
- `useQuery` - 일반 데이터 패칭
|
|
|
|
### Swiper 사용 시
|
|
```jsx
|
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
|
import { Virtual } from 'swiper/modules';
|
|
import 'swiper/css';
|
|
```
|
|
- 사용 위치: mobile/Members, mobile/AlbumDetail, mobile/AlbumGallery
|
|
|
|
### 가상화 리스트
|
|
- `useVirtualizer` from `@tanstack/react-virtual`
|
|
- 사용 위치: mobile/Schedule, admin/AdminSchedule
|
|
|
|
### 교차 관찰자
|
|
- `useInView` from `react-intersection-observer`
|
|
- 사용 위치: mobile/Schedule (무한 스크롤 트리거)
|
|
|
|
### 축하 효과
|
|
- `canvas-confetti` - 생일 페이지 폭죽 효과
|
|
- 사용 위치: pc/Birthday, mobile/Schedule (생일 일정)
|
|
|
|
### 색상 선택기
|
|
- `HexColorPicker` from `react-colorful`
|
|
- 사용 위치: admin/AdminScheduleCategory
|
|
|
|
### 앨범 갤러리
|
|
- `RowsPhotoAlbum` from `react-photo-album`
|
|
- 사용 위치: pc/AlbumGallery, mobile/AlbumGallery
|
|
|
|
### Kakao API (미구현)
|
|
- 콘서트 장소 검색: `/api/admin/kakao/places`
|
|
- 환경변수: `VITE_KAKAO_JS_KEY`
|
|
- 사용 예정: 콘서트 일정 추가 시 장소 검색
|
|
|
|
## 마이그레이션 진행 상황
|
|
|
|
### 완료된 작업
|
|
- [x] Phase 1: 프로젝트 셋업
|
|
- [x] Phase 2: 유틸리티 및 상수
|
|
- [x] Phase 3: Zustand 스토어
|
|
- [x] Phase 4: API 계층 (공개 API)
|
|
- [x] Phase 5: 커스텀 훅
|
|
- [x] Phase 6: 공통 컴포넌트
|
|
- [x] Phase 7: PC/Mobile 레이아웃 컴포넌트
|
|
- [x] Phase 8: App.jsx (BrowserView/MobileView 라우팅)
|
|
- [x] Phase 9: 공개 페이지 전체
|
|
- Home, Members, Album, AlbumDetail, AlbumGallery, TrackDetail
|
|
- Schedule, ScheduleDetail, Birthday
|
|
|
|
### 미완료 작업
|
|
|
|
#### 공개 영역
|
|
- [ ] NotFound 페이지 (pc/mobile)
|
|
- [ ] useToast 훅
|
|
|
|
#### 관리자 영역 (별도 요청 시 진행)
|
|
- [ ] 관리자 API 전체 (api/admin/)
|
|
- [ ] 관리자 컴포넌트 전체 (components/admin/)
|
|
- [ ] 관리자 페이지 전체 (pages/admin/)
|
|
|
|
### 최종 검증
|
|
- [ ] 모든 라우트 동작 확인
|
|
- [ ] PC/Mobile 전환 테스트
|
|
- [ ] 관리자 기능 테스트
|
|
- [ ] frontend-temp → frontend 교체
|