fromis_9/docs/architecture.md
caadiq e0ab3ce0f8 fix(backend): getUpcomingSchedules 응답 형식 통일
- getUpcomingSchedules가 getMonthlySchedules와 동일한 날짜별 그룹화 형식 반환
- routes/schedules 응답 스키마에 oneOf 추가 (객체/배열 둘 다 허용)
- docs/architecture.md, migration.md 업데이트

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 20:10:26 +09:00

13 KiB

프로젝트 구조

디렉토리 구조

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/    # 추천 검색어
│   │   ├── app.js              # Fastify 앱 설정
│   │   └── server.js           # 진입점
│   ├── Dockerfile              # 백엔드 컨테이너
│   └── package.json
│
├── backend-backup/             # Express 백엔드 (참조용, 마이그레이션 원본)
│
├── frontend/                   # React 프론트엔드 (레거시, frontend-temp로 대체 예정)
│   ├── src/
│   │   ├── api/
│   │   │   ├── public/         # 공개 API
│   │   │   └── admin/          # 어드민 API
│   │   ├── pages/
│   │   │   ├── pc/             # PC 페이지
│   │   │   └── mobile/         # 모바일 페이지
│   │   └── ...
│   └── package.json
│
├── frontend-temp/              # React 프론트엔드 (신규, Strangler Fig 마이그레이션)
│   ├── src/
│   │   ├── api/                # API 클라이언트 (공유)
│   │   │   ├── index.js
│   │   │   ├── client.js       # fetchApi, fetchAuthApi
│   │   │   ├── albums.js
│   │   │   ├── members.js
│   │   │   ├── schedules.js
│   │   │   ├── auth.js
│   │   │   └── admin/          # 관리자 API
│   │   │       ├── albums.js
│   │   │       ├── members.js
│   │   │       ├── schedules.js
│   │   │       ├── categories.js
│   │   │       ├── stats.js
│   │   │       ├── bots.js
│   │   │       └── suggestions.js
│   │   │
│   │   ├── hooks/              # 커스텀 훅 (공유)
│   │   │   ├── index.js
│   │   │   ├── useAlbumData.js
│   │   │   ├── useMemberData.js
│   │   │   ├── useScheduleData.js
│   │   │   ├── useScheduleSearch.js
│   │   │   ├── useCalendar.js
│   │   │   ├── useToast.js
│   │   │   └── useAdminAuth.js
│   │   │
│   │   ├── stores/             # Zustand 스토어 (공유)
│   │   │   ├── index.js
│   │   │   ├── useScheduleStore.js
│   │   │   └── useAuthStore.js
│   │   │
│   │   ├── utils/              # 유틸리티 (공유)
│   │   │   ├── index.js
│   │   │   ├── date.js
│   │   │   └── format.js
│   │   │
│   │   ├── constants/
│   │   │   └── index.js
│   │   │
│   │   ├── components/
│   │   │   ├── index.js
│   │   │   ├── common/         # 디바이스 무관 공통 컴포넌트
│   │   │   │   ├── Loading.jsx
│   │   │   │   ├── ErrorBoundary.jsx
│   │   │   │   ├── Toast.jsx
│   │   │   │   ├── Lightbox.jsx
│   │   │   │   ├── LightboxIndicator.jsx
│   │   │   │   ├── Tooltip.jsx
│   │   │   │   └── ScrollToTop.jsx
│   │   │   ├── pc/             # PC 레이아웃 컴포넌트
│   │   │   │   ├── Layout.jsx
│   │   │   │   ├── Header.jsx
│   │   │   │   └── Footer.jsx
│   │   │   ├── mobile/         # Mobile 레이아웃 컴포넌트
│   │   │   │   ├── Layout.jsx
│   │   │   │   └── MobileNav.jsx
│   │   │   └── admin/          # 관리자 컴포넌트
│   │   │       ├── AdminLayout.jsx
│   │   │       ├── AdminHeader.jsx
│   │   │       ├── ConfirmDialog.jsx
│   │   │       ├── CustomDatePicker.jsx
│   │   │       ├── CustomTimePicker.jsx
│   │   │       └── NumberPicker.jsx
│   │   │
│   │   ├── pages/
│   │   │   ├── index.js
│   │   │   │
│   │   │   ├── home/
│   │   │   │   ├── index.js    # export { PCHome, MobileHome }
│   │   │   │   ├── pc/
│   │   │   │   │   └── Home.jsx
│   │   │   │   └── mobile/
│   │   │   │       └── Home.jsx
│   │   │   │
│   │   │   ├── members/
│   │   │   │   ├── index.js
│   │   │   │   ├── pc/
│   │   │   │   │   └── Members.jsx
│   │   │   │   └── mobile/
│   │   │   │       └── Members.jsx
│   │   │   │
│   │   │   ├── album/
│   │   │   │   ├── index.js
│   │   │   │   ├── pc/
│   │   │   │   │   ├── Album.jsx
│   │   │   │   │   ├── AlbumDetail.jsx
│   │   │   │   │   ├── AlbumGallery.jsx
│   │   │   │   │   └── TrackDetail.jsx
│   │   │   │   └── mobile/
│   │   │   │       ├── Album.jsx
│   │   │   │       ├── AlbumDetail.jsx
│   │   │   │       ├── AlbumGallery.jsx
│   │   │   │       └── TrackDetail.jsx
│   │   │   │
│   │   │   ├── schedule/
│   │   │   │   ├── index.js
│   │   │   │   ├── sections/   # 일정 상세 섹션 (PC 전용)
│   │   │   │   │   ├── DefaultSection.jsx
│   │   │   │   │   ├── XSection.jsx
│   │   │   │   │   └── YoutubeSection.jsx
│   │   │   │   ├── pc/
│   │   │   │   │   ├── Schedule.jsx
│   │   │   │   │   ├── ScheduleDetail.jsx
│   │   │   │   │   └── Birthday.jsx
│   │   │   │   └── mobile/
│   │   │   │       ├── Schedule.jsx
│   │   │   │       └── ScheduleDetail.jsx
│   │   │   │
│   │   │   ├── common/
│   │   │   │   ├── pc/
│   │   │   │   │   └── NotFound.jsx
│   │   │   │   └── mobile/
│   │   │   │       └── NotFound.jsx
│   │   │   │
│   │   │   └── admin/          # 관리자 페이지 (PC 전용)
│   │   │       ├── index.js
│   │   │       ├── Login.jsx
│   │   │       ├── Dashboard.jsx
│   │   │       ├── members/
│   │   │       │   ├── List.jsx
│   │   │       │   └── Edit.jsx
│   │   │       ├── albums/
│   │   │       │   ├── List.jsx
│   │   │       │   ├── Form.jsx
│   │   │       │   └── Photos.jsx
│   │   │       ├── schedules/
│   │   │       │   ├── List.jsx
│   │   │       │   ├── Form.jsx
│   │   │       │   ├── YouTubeForm.jsx
│   │   │       │   ├── XForm.jsx
│   │   │       │   └── YouTubeEditForm.jsx
│   │   │       ├── categories/
│   │   │       │   └── List.jsx
│   │   │       ├── bots/
│   │   │       │   └── Manager.jsx
│   │   │       └── dict/
│   │   │           └── Manager.jsx
│   │   │
│   │   ├── App.jsx             # BrowserView/MobileView 라우팅
│   │   └── 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