fromis_9/docs/development.md
caadiq 9eb49929c7 YouTube 섹션 스타일 개선 및 문서 업데이트
- PC/모바일 YouTube 상세 페이지 그림자 감소, 배경색 조정
- 모바일 YouTube 섹션을 카드에서 배경 스타일로 변경
- 문서 업데이트: 완료된 마이그레이션 작업 반영, 누락된 API 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 12:57:25 +09:00

5.4 KiB

개발/배포 가이드

개발 모드

실행

cd /docker/fromis_9
docker compose up -d --build

컨테이너 구성

컨테이너 포트 설명
fromis9-frontend 80 Vite 개발 서버, HMR 지원
fromis9-backend 80 Fastify API, --watch 모드
fromis9-meilisearch 7700 검색 엔진
fromis9-redis 6379 캐시
  • Vite가 /api, /docs 요청을 백엔드로 프록시

로그 확인

# 전체 로그
docker compose logs -f

# 백엔드만
docker compose logs -f fromis9-backend

# 프론트엔드만
docker compose logs -f fromis9-frontend

코드 수정

  • frontend/, backend/ 폴더가 컨테이너에 마운트됨
  • node_modules도 호스트 폴더에 직접 설치됨
  • 코드 수정 시 자동 반영 (HMR, watch)

재시작

# 백엔드만 재시작
docker compose restart fromis9-backend

# 프론트엔드만 재시작
docker compose restart fromis9-frontend

# 전체 재시작
docker compose restart

배포 모드 전환

1. Dockerfile 수정

backend/Dockerfile:

# 개발 모드 주석처리
# FROM node:20-alpine
# ...

# 배포 모드 주석해제
FROM node:20-alpine
WORKDIR /app
RUN apk add --no-cache ffmpeg
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

frontend/Dockerfile:

# 개발 모드 주석처리
# FROM node:20-alpine
# ...

# 배포 모드 주석해제
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

2. 빌드 및 실행

docker compose up -d --build

환경 변수 (.env)

# 서버
PORT=80

# 데이터베이스
DB_HOST=mariadb
DB_PORT=3306
DB_USER=...
DB_PASSWORD=...
DB_NAME=fromis9

# Redis
REDIS_HOST=fromis9-redis
REDIS_PORT=6379

# Meilisearch
MEILI_HOST=http://fromis9-meilisearch:7700
MEILI_MASTER_KEY=...

# JWT
JWT_SECRET=...

# AWS S3
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=...
S3_BUCKET=...

# YouTube API
YOUTUBE_API_KEY=...

Caddy 설정

위치: /docker/caddy/Caddyfile

fromis_9 사이트 설정

fromis9.caadiq.co.kr {
    import custom_errors
    reverse_proxy fromis9-frontend:80
}

설정 설명

  • import custom_errors: 공통 에러 페이지 (403, 404, 500, 502, 503)
  • reverse_proxy fromis9-frontend:80: Docker 네트워크로 프론트엔드 컨테이너에 연결
  • 업로드 크기 제한 없음 (Caddy 기본값)

Caddy 재시작

docker exec caddy caddy reload --config /etc/caddy/Caddyfile

네트워크 구조

인터넷 → Caddy (:443) → fromis9-frontend (:80) → fromis9-backend (:80)
                                               ↓
                         MariaDB, Redis, Meilisearch (내부 네트워크)

프론트엔드 개발 가이드

API 클라이언트 구조

src/api/
├── index.js          # fetchApi 유틸 (에러 처리, 토큰 주입)
├── public/           # 공개 API (인증 불필요)
│   ├── albums.js     # getAlbums, getAlbumByName, getTrack
│   ├── members.js    # getMembers
│   └── schedules.js  # getSchedules, getSchedule, getCategories
└── admin/            # 관리자 API (인증 필요)
    ├── auth.js       # login, verifyToken
    ├── albums.js     # createAlbum, updateAlbum, deleteAlbum, ...
    ├── bots.js       # getBots, startBot, stopBot, syncBot
    ├── categories.js # getCategories
    ├── members.js    # updateMember
    ├── schedules.js  # getYoutubeInfo, saveYoutube, getXInfo, saveX, ...
    ├── stats.js      # getStats
    └── suggestions.js # getDict, saveDict

사용 예시:

// 공개 API
import { getSchedules, getSchedule } from '@/api/public/schedules';

// 관리자 API
import { getBots, startBot } from '@/api/admin/bots';

React Query 사용 (데이터 페칭)

데이터 페칭 시 useEffect 대신 useQuery를 사용합니다.

이유:

  • useEffect는 React StrictMode에서 2번 실행됨 (개발 모드)
  • useQuery는 자동 캐싱, 중복 요청 방지, 에러/로딩 상태 관리 제공

예시:

// ❌ Bad - useEffect 사용
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
  fetch('/api/data')
    .then(res => res.json())
    .then(data => setData(data))
    .finally(() => setLoading(false));
}, []);

// ✅ Good - useQuery 사용
import { useQuery } from '@tanstack/react-query';

const { data, isLoading } = useQuery({
  queryKey: ['data'],
  queryFn: () => fetch('/api/data').then(res => res.json()),
});

캐시 무효화:

import { useQueryClient } from '@tanstack/react-query';

const queryClient = useQueryClient();

// 특정 쿼리 무효화
queryClient.invalidateQueries({ queryKey: ['schedules'] });

// 모든 쿼리 무효화
queryClient.invalidateQueries();

유용한 명령어

# 컨테이너 상태 확인
docker compose ps

# 완전 재시작
docker compose down && docker compose up -d --build

# Meilisearch 동기화
curl -X POST https://fromis9.caadiq.co.kr/api/schedules/sync-search \
  -H "Authorization: Bearer <token>"

# Redis 확인
docker exec fromis9-redis redis-cli KEYS "*"