- DebutCard 컴포넌트 추가 (PC/모바일) - DebutCelebrationDialog 축하 다이얼로그 추가 - Fromis9Logo SVG 컴포넌트 추가 - 기념일 카테고리 추가 (ID: 9) - 데뷔일(2018.01.24) 및 주년 일정 자동 생성 - 폭죽 효과 추가 (fireDebutConfetti) - 카테고리 정보 DB에서 동적 조회하도록 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
330 lines
16 KiB
Markdown
330 lines
16 KiB
Markdown
# 프로젝트 구조
|
|
|
|
## 디렉토리 구조
|
|
|
|
```
|
|
fromis_9/
|
|
├── backend/ # Fastify 백엔드
|
|
│ ├── src/
|
|
│ │ ├── config/
|
|
│ │ │ ├── index.js # 환경변수 통합 관리
|
|
│ │ │ └── bots.js # 봇 설정 (YouTube, X)
|
|
│ │ ├── plugins/ # Fastify 플러그인
|
|
│ │ │ ├── db.js # MariaDB 연결
|
|
│ │ │ ├── redis.js # Redis 연결
|
|
│ │ │ ├── auth.js # JWT 인증
|
|
│ │ │ ├── meilisearch.js # 검색 엔진
|
|
│ │ │ └── scheduler.js # 봇 스케줄러
|
|
│ │ ├── routes/ # API 라우트
|
|
│ │ │ ├── admin/ # 관리자 API
|
|
│ │ │ │ ├── bots.js # 봇 관리
|
|
│ │ │ │ ├── youtube.js # YouTube 일정 관리
|
|
│ │ │ │ └── x.js # X 일정 관리
|
|
│ │ │ ├── albums/
|
|
│ │ │ │ ├── index.js # 앨범 CRUD
|
|
│ │ │ │ ├── photos.js # 앨범 사진 관리
|
|
│ │ │ │ └── teasers.js # 앨범 티저 관리
|
|
│ │ │ ├── auth.js # 인증 (로그인, 토큰 검증)
|
|
│ │ │ ├── members/
|
|
│ │ │ │ └── index.js # 멤버 조회/수정
|
|
│ │ │ ├── schedules/
|
|
│ │ │ │ ├── index.js # 일정 조회/검색/삭제
|
|
│ │ │ │ └── suggestions.js # 추천 검색어
|
|
│ │ │ ├── stats/
|
|
│ │ │ │ └── index.js # 통계 조회
|
|
│ │ │ └── index.js # 라우트 등록
|
|
│ │ ├── services/ # 비즈니스 로직
|
|
│ │ │ ├── youtube/ # YouTube 봇
|
|
│ │ │ ├── x/ # X(Twitter) 봇
|
|
│ │ │ ├── meilisearch/ # 검색 서비스
|
|
│ │ │ └── suggestions/ # 추천 검색어
|
|
│ │ ├── utils/ # 유틸리티
|
|
│ │ │ ├── cache.js # Redis 캐시 헬퍼 (SCAN 사용)
|
|
│ │ │ ├── date.js # 날짜 유틸 (KST 변환)
|
|
│ │ │ ├── error.js # 에러 응답 헬퍼
|
|
│ │ │ ├── logger.js # 로깅 유틸
|
|
│ │ │ └── transaction.js # DB 트랜잭션 래퍼
|
|
│ │ ├── app.js # Fastify 앱 설정
|
|
│ │ └── server.js # 진입점
|
|
│ ├── Dockerfile # 백엔드 컨테이너
|
|
│ └── package.json
|
|
│
|
|
├── frontend/ # React 프론트엔드
|
|
│ ├── src/
|
|
│ │ ├── api/ # API 클라이언트
|
|
│ │ │ ├── index.js
|
|
│ │ │ ├── client.js # fetchApi, fetchAuthApi
|
|
│ │ │ ├── public/ # 공개 API
|
|
│ │ │ │ ├── albums.js
|
|
│ │ │ │ ├── members.js
|
|
│ │ │ │ └── schedules.js
|
|
│ │ │ └── admin/ # 관리자 API
|
|
│ │ │ ├── albums.js
|
|
│ │ │ ├── members.js
|
|
│ │ │ ├── schedules.js
|
|
│ │ │ ├── categories.js
|
|
│ │ │ ├── stats.js
|
|
│ │ │ ├── bots.js
|
|
│ │ │ ├── auth.js
|
|
│ │ │ └── suggestions.js
|
|
│ │ │
|
|
│ │ ├── hooks/ # 커스텀 훅
|
|
│ │ │ ├── index.js
|
|
│ │ │ ├── common/ # 공통 훅
|
|
│ │ │ │ └── useToast.js
|
|
│ │ │ └── pc/
|
|
│ │ │ └── admin/ # 관리자 훅
|
|
│ │ │ ├── useAdminAuth.js
|
|
│ │ │ └── useScheduleSearch.js
|
|
│ │ │
|
|
│ │ ├── stores/ # Zustand 스토어
|
|
│ │ │ ├── index.js
|
|
│ │ │ ├── useScheduleStore.js
|
|
│ │ │ └── useAuthStore.js
|
|
│ │ │
|
|
│ │ ├── utils/ # 유틸리티
|
|
│ │ │ ├── index.js
|
|
│ │ │ ├── cn.js # className 병합
|
|
│ │ │ ├── color.js # 색상 상수/유틸
|
|
│ │ │ ├── confetti.js # 축하 효과 (생일, 데뷔/주년)
|
|
│ │ │ ├── date.js # 날짜 포맷
|
|
│ │ │ ├── format.js # 문자열 포맷
|
|
│ │ │ ├── schedule.js # 일정 관련 유틸
|
|
│ │ │ └── youtube.js # YouTube URL 파싱
|
|
│ │ │
|
|
│ │ ├── constants/
|
|
│ │ │ └── index.js # 상수 정의
|
|
│ │ │
|
|
│ │ ├── components/
|
|
│ │ │ ├── index.js
|
|
│ │ │ ├── common/ # 공통 컴포넌트
|
|
│ │ │ │ ├── Loading.jsx
|
|
│ │ │ │ ├── ErrorBoundary.jsx
|
|
│ │ │ │ ├── ErrorMessage.jsx
|
|
│ │ │ │ ├── Toast.jsx
|
|
│ │ │ │ ├── Tooltip.jsx
|
|
│ │ │ │ ├── Lightbox.jsx
|
|
│ │ │ │ ├── MobileLightbox.jsx
|
|
│ │ │ │ ├── LightboxIndicator.jsx
|
|
│ │ │ │ ├── AnimatedNumber.jsx
|
|
│ │ │ │ ├── ScrollToTop.jsx
|
|
│ │ │ │ ├── Fromis9Logo.jsx # 프로미스나인 로고 SVG
|
|
│ │ │ │ └── DebutCelebrationDialog.jsx # 데뷔/주년 축하 다이얼로그
|
|
│ │ │ │
|
|
│ │ │ ├── pc/
|
|
│ │ │ │ ├── public/ # PC 공개 컴포넌트
|
|
│ │ │ │ │ ├── layout/
|
|
│ │ │ │ │ │ ├── Layout.jsx
|
|
│ │ │ │ │ │ ├── Header.jsx
|
|
│ │ │ │ │ │ └── Footer.jsx
|
|
│ │ │ │ │ └── schedule/
|
|
│ │ │ │ │ ├── Calendar.jsx
|
|
│ │ │ │ │ ├── ScheduleCard.jsx
|
|
│ │ │ │ │ ├── BirthdayCard.jsx
|
|
│ │ │ │ │ ├── DebutCard.jsx # 데뷔/주년 카드
|
|
│ │ │ │ │ └── CategoryFilter.jsx
|
|
│ │ │ │ │
|
|
│ │ │ │ └── admin/ # PC 관리자 컴포넌트
|
|
│ │ │ │ ├── layout/
|
|
│ │ │ │ │ ├── Layout.jsx
|
|
│ │ │ │ │ └── Header.jsx
|
|
│ │ │ │ ├── common/
|
|
│ │ │ │ │ ├── ConfirmDialog.jsx
|
|
│ │ │ │ │ ├── DatePicker.jsx
|
|
│ │ │ │ │ ├── TimePicker.jsx
|
|
│ │ │ │ │ ├── NumberPicker.jsx
|
|
│ │ │ │ │ └── CustomSelect.jsx
|
|
│ │ │ │ ├── schedule/
|
|
│ │ │ │ │ ├── AdminScheduleCard.jsx
|
|
│ │ │ │ │ ├── ScheduleItem.jsx
|
|
│ │ │ │ │ ├── CategorySelector.jsx
|
|
│ │ │ │ │ ├── CategoryFormModal.jsx
|
|
│ │ │ │ │ ├── MemberSelector.jsx
|
|
│ │ │ │ │ ├── ImageUploader.jsx
|
|
│ │ │ │ │ ├── LocationSearchDialog.jsx
|
|
│ │ │ │ │ └── WordItem.jsx
|
|
│ │ │ │ ├── album/
|
|
│ │ │ │ │ ├── TrackItem.jsx
|
|
│ │ │ │ │ ├── PhotoGrid.jsx
|
|
│ │ │ │ │ ├── PhotoPreviewModal.jsx
|
|
│ │ │ │ │ ├── PendingFileItem.jsx
|
|
│ │ │ │ │ └── BulkEditPanel.jsx
|
|
│ │ │ │ └── bot/
|
|
│ │ │ │ └── BotCard.jsx
|
|
│ │ │ │
|
|
│ │ │ └── mobile/ # 모바일 컴포넌트
|
|
│ │ │ ├── layout/
|
|
│ │ │ │ ├── Layout.jsx
|
|
│ │ │ │ ├── Header.jsx
|
|
│ │ │ │ └── BottomNav.jsx
|
|
│ │ │ └── schedule/
|
|
│ │ │ ├── Calendar.jsx
|
|
│ │ │ ├── ScheduleCard.jsx
|
|
│ │ │ ├── ScheduleListCard.jsx
|
|
│ │ │ ├── ScheduleSearchCard.jsx
|
|
│ │ │ ├── BirthdayCard.jsx
|
|
│ │ │ └── DebutCard.jsx # 데뷔/주년 카드
|
|
│ │ │
|
|
│ │ ├── pages/
|
|
│ │ │ ├── pc/
|
|
│ │ │ │ ├── public/ # PC 공개 페이지
|
|
│ │ │ │ │ ├── home/
|
|
│ │ │ │ │ │ └── Home.jsx
|
|
│ │ │ │ │ ├── members/
|
|
│ │ │ │ │ │ └── Members.jsx
|
|
│ │ │ │ │ ├── album/
|
|
│ │ │ │ │ │ ├── Album.jsx
|
|
│ │ │ │ │ │ ├── AlbumDetail.jsx
|
|
│ │ │ │ │ │ ├── AlbumGallery.jsx
|
|
│ │ │ │ │ │ └── TrackDetail.jsx
|
|
│ │ │ │ │ ├── schedule/
|
|
│ │ │ │ │ │ ├── Schedule.jsx
|
|
│ │ │ │ │ │ ├── ScheduleDetail.jsx
|
|
│ │ │ │ │ │ ├── Birthday.jsx
|
|
│ │ │ │ │ │ └── sections/
|
|
│ │ │ │ │ │ ├── DefaultSection.jsx
|
|
│ │ │ │ │ │ ├── YoutubeSection.jsx
|
|
│ │ │ │ │ │ └── XSection.jsx
|
|
│ │ │ │ │ └── common/
|
|
│ │ │ │ │ └── NotFound.jsx
|
|
│ │ │ │ │
|
|
│ │ │ │ └── admin/ # PC 관리자 페이지
|
|
│ │ │ │ ├── Login.jsx
|
|
│ │ │ │ ├── Dashboard.jsx
|
|
│ │ │ │ ├── members/
|
|
│ │ │ │ │ ├── Members.jsx
|
|
│ │ │ │ │ └── MemberEdit.jsx
|
|
│ │ │ │ ├── albums/
|
|
│ │ │ │ │ ├── Albums.jsx
|
|
│ │ │ │ │ ├── AlbumForm.jsx
|
|
│ │ │ │ │ └── AlbumPhotos.jsx
|
|
│ │ │ │ └── schedules/
|
|
│ │ │ │ ├── Schedules.jsx
|
|
│ │ │ │ ├── ScheduleForm.jsx
|
|
│ │ │ │ ├── ScheduleDict.jsx
|
|
│ │ │ │ ├── ScheduleBots.jsx
|
|
│ │ │ │ ├── ScheduleCategory.jsx
|
|
│ │ │ │ ├── form/
|
|
│ │ │ │ │ ├── YouTubeForm.jsx
|
|
│ │ │ │ │ └── XForm.jsx
|
|
│ │ │ │ └── edit/
|
|
│ │ │ │ └── YouTubeEdit.jsx
|
|
│ │ │ │
|
|
│ │ │ └── mobile/ # 모바일 페이지
|
|
│ │ │ ├── home/
|
|
│ │ │ │ └── Home.jsx
|
|
│ │ │ ├── members/
|
|
│ │ │ │ └── Members.jsx
|
|
│ │ │ ├── album/
|
|
│ │ │ │ ├── Album.jsx
|
|
│ │ │ │ ├── AlbumDetail.jsx
|
|
│ │ │ │ ├── AlbumGallery.jsx
|
|
│ │ │ │ └── TrackDetail.jsx
|
|
│ │ │ ├── schedule/
|
|
│ │ │ │ ├── Schedule.jsx
|
|
│ │ │ │ └── ScheduleDetail.jsx
|
|
│ │ │ └── common/
|
|
│ │ │ └── NotFound.jsx
|
|
│ │ │
|
|
│ │ ├── routes/ # 라우트 정의
|
|
│ │ │ ├── index.js # 라우트 export
|
|
│ │ │ ├── pc/
|
|
│ │ │ │ ├── admin/
|
|
│ │ │ │ │ └── index.jsx # PC 관리자 라우트
|
|
│ │ │ │ └── public/
|
|
│ │ │ │ └── index.jsx # PC 공개 라우트
|
|
│ │ │ └── mobile/
|
|
│ │ │ └── index.jsx # 모바일 라우트
|
|
│ │ │
|
|
│ │ ├── App.jsx # PC/모바일 분기
|
|
│ │ └── main.jsx
|
|
│ │
|
|
│ ├── vite.config.js
|
|
│ ├── tailwind.config.js
|
|
│ ├── Dockerfile
|
|
│ └── package.json
|
|
│
|
|
├── docker-compose.yml
|
|
└── .env
|
|
```
|
|
|
|
## 서비스 구성
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Caddy │
|
|
│ (리버스 프록시) │
|
|
└─────────────────────┬───────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ fromis9-frontend (:80) │
|
|
│ Vite 개발서버 │
|
|
│ (프록시: /api → backend) │
|
|
└─────────────────────┬───────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ fromis9-backend (:80) │
|
|
│ Fastify API │
|
|
└─────────────────────┬───────────────────────────────────┘
|
|
│
|
|
┌────────────┼────────────┬────────────┐
|
|
│ │ │ │
|
|
▼ ▼ ▼ ▼
|
|
┌───────────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
|
|
│ MariaDB │ │Meilisearch│ │ Redis │ │ Nitter │
|
|
│ (외부 DB망) │ │ (검색엔진) │ │ (캐시) │ │ (X 스크랩) │
|
|
└───────────────┘ └───────────┘ └───────────┘ └───────────┘
|
|
```
|
|
|
|
## 데이터베이스
|
|
|
|
### 테이블 목록 (25개)
|
|
|
|
#### 사용자/인증
|
|
- `admin_users` - 관리자 계정
|
|
|
|
#### 멤버
|
|
- `members` - 멤버 정보 (이름, 생년월일, 인스타그램 등)
|
|
- `member_nicknames` - 멤버 별명 (검색용)
|
|
|
|
#### 앨범
|
|
- `albums` - 앨범 정보 (제목, 발매일, 커버 이미지 등)
|
|
- `album_tracks` - 앨범 트랙 (곡명, 작사/작곡, 가사 등)
|
|
- `album_photos` - 앨범 컨셉 포토
|
|
- `album_photo_members` - 컨셉 포토-멤버 연결
|
|
- `album_teasers` - 앨범 티저 이미지/영상
|
|
|
|
#### 일정
|
|
- `schedules` - 일정 (제목, 날짜, 시간 등)
|
|
- `schedule_categories` - 일정 카테고리 (유튜브, X, 콘서트 등)
|
|
- `schedule_members` - 일정-멤버 연결
|
|
- `schedule_images` - 일정 첨부 이미지
|
|
- `schedule_youtube` - YouTube 영상 연결 정보
|
|
- `schedule_x` - X(Twitter) 게시물 연결 정보
|
|
- `schedule_concert` - 콘서트 일정 추가 정보
|
|
|
|
#### 콘서트
|
|
- `concert_venues` - 콘서트 장소 정보
|
|
- `concert_series` - 콘서트 시리즈 (투어 등)
|
|
- `concert_series_md` - 콘서트 MD 상품
|
|
- `concert_setlists` - 콘서트 셋리스트
|
|
- `concert_setlist_members` - 셋리스트-멤버 연결
|
|
|
|
#### X(Twitter) 프로필
|
|
- `x_profiles` - X 프로필 캐시 (프로필 이미지, 이름 등)
|
|
|
|
#### 이미지
|
|
- `images` - 이미지 메타데이터 (3개 해상도 URL)
|
|
|
|
#### 추천 검색어
|
|
- `suggestion_queries` - 검색 쿼리 로그
|
|
- `suggestion_word_pairs` - 단어 bi-gram 빈도
|
|
- `suggestion_chosung` - 초성 검색 매핑
|
|
|
|
### 검색 인덱스 (Meilisearch)
|
|
- `schedules` - 일정 검색용 인덱스
|
|
- 검색 필드: title, member_names, description, source_name, category_name
|
|
- 필터: category_id, date
|
|
- 정렬: date, time
|