# 개발/배포 가이드 ## 개발 모드 ### 실행 ```bash 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` 요청을 백엔드로 프록시 ### 로그 확인 ```bash # 전체 로그 docker compose logs -f # 백엔드만 docker compose logs -f fromis9-backend # 프론트엔드만 docker compose logs -f fromis9-frontend ``` ### 코드 수정 - `frontend/`, `backend/` 폴더가 컨테이너에 마운트됨 - `node_modules`도 호스트 폴더에 직접 설치됨 - 코드 수정 시 자동 반영 (HMR, watch) ### 재시작 ```bash # 백엔드만 재시작 docker compose restart fromis9-backend # 프론트엔드만 재시작 docker compose restart fromis9-frontend # 전체 재시작 docker compose restart ``` --- ## 배포 모드 전환 ### 1. Dockerfile 수정 **backend/Dockerfile:** ```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:** ```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. 빌드 및 실행 ```bash docker compose up -d --build ``` --- ## 환경 변수 (.env) ```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 사이트 설정 ```caddyfile 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 재시작 ```bash 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 ``` **사용 예시:** ```jsx // 공개 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`는 자동 캐싱, 중복 요청 방지, 에러/로딩 상태 관리 제공 **예시:** ```jsx // ❌ 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()), }); ``` **캐시 무효화:** ```jsx import { useQueryClient } from '@tanstack/react-query'; const queryClient = useQueryClient(); // 특정 쿼리 무효화 queryClient.invalidateQueries({ queryKey: ['schedules'] }); // 모든 쿼리 무효화 queryClient.invalidateQueries(); ``` --- ## 유용한 명령어 ```bash # 컨테이너 상태 확인 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 " # Redis 확인 docker exec fromis9-redis redis-cli KEYS "*" ```