# CastAD Backend AI 기반 광고 음악 생성 서비스의 백엔드 API 서버입니다. ## 기술 스택 - **Language**: Python 3.13 - **Framework**: FastAPI - **Database**: MySQL (asyncmy 비동기 드라이버), Redis - **ORM**: SQLAlchemy (async) - **Package Manager**: uv - **AI Services**: - OpenAI ChatGPT (가사 생성, 마케팅 분석) - Suno AI (음악 생성) - Creatomate (비디오 생성) ## 프로젝트 구조 ```text app/ ├── core/ # 핵심 설정 및 공통 모듈 (logging, exceptions) ├── database/ # 데이터베이스 세션 및 Redis 설정 ├── dependencies/ # FastAPI 의존성 주입 ├── home/ # 홈 API (크롤링, 영상 생성 요청) ├── lyric/ # 가사 API (가사 생성) ├── song/ # 노래 API (Suno AI 음악 생성) ├── user/ # 사용자 모듈 (카카오 로그인, JWT 인증) ├── video/ # 비디오 관련 모듈 └── utils/ # 유틸리티 (ChatGPT, Suno, 크롤러, 프롬프트) ``` ## API 엔드포인트 ### Home API | Method | Endpoint | 설명 | | ------ | ------------------ | ------------------------------ | | POST | `/crawling` | 네이버 지도 장소 크롤링 | | POST | `/generate` | 기본 영상 생성 요청 | | POST | `/generate/urls` | URL 기반 영상 생성 요청 | | POST | `/generate/upload` | 파일 업로드 기반 영상 생성 요청 | ### Lyric API | Method | Endpoint | 설명 | | ------ | ------------------------- | --------------------------- | | POST | `/lyric/generate` | ChatGPT를 이용한 가사 생성 | | GET | `/lyric/status/{task_id}` | 가사 생성 상태 조회 | | GET | `/lyric/{task_id}` | 가사 상세 조회 | | GET | `/lyrics` | 가사 목록 조회 (페이지네이션) | ### Song API | Method | Endpoint | 설명 | | ------ | -------------------------- | ----------------------------- | | POST | `/song/generate` | Suno AI를 이용한 노래 생성 요청 | | GET | `/song/status/{task_id}` | 노래 생성 상태 조회 (폴링) | ## 환경 설정 `.env` 파일에 다음 환경 변수를 설정합니다: ```env # ================================ # 프로젝트 기본 정보 # ================================ PROJECT_NAME=CastAD # 프로젝트 이름 PROJECT_DOMAIN=localhost:8000 # 프로젝트 도메인 (호스트:포트) PROJECT_VERSION=0.1.0 # 프로젝트 버전 DESCRIPTION=FastAPI 기반 CastAD 프로젝트 # 프로젝트 설명 ADMIN_BASE_URL=/admin # 관리자 페이지 기본 URL SHARE_FRONTEND_URL=https://ado2.o2osolution.ai # 공유 페이지 → 영상 상세 이동 프론트 URL (로컬: http://localhost:3000, 테스트: https://dev.castad.net) SHARE_DEFAULT_IMAGE_URL= # 포스터 없을 때 OG 이미지 (비우면 API /static/images/ado2_image.png) DEBUG=True # 디버그 모드 (True: 개발, False: 운영) # ================================ # MySQL 설정 # ================================ MYSQL_HOST=localhost # MySQL 호스트 주소 MYSQL_PORT=3306 # MySQL 포트 번호 MYSQL_USER=castad-admin # MySQL 사용자명 MYSQL_PASSWORD=o2o1324 # MySQL 비밀번호 MYSQL_DB=castad # 사용할 데이터베이스명 # ================================ # Redis 설정 # ================================ REDIS_HOST=localhost # Redis 호스트 주소 REDIS_PORT=6379 # Redis 포트 번호 # ================================ # CORS 설정 # ================================ CORS_ALLOW_ORIGINS='["*"]' # 허용할 Origin 목록 (JSON 배열 형식) CORS_ALLOW_CREDENTIALS=True # 자격 증명(쿠키 등) 허용 여부 CORS_ALLOW_METHODS='["*"]' # 허용할 HTTP 메서드 (JSON 배열 형식) CORS_ALLOW_HEADERS='["*"]' # 허용할 HTTP 헤더 (JSON 배열 형식) CORS_MAX_AGE=600 # Preflight 요청 캐시 시간 (초) # ================================ # Azure Blob Storage 설정 # ================================ AZURE_BLOB_SAS_TOKEN=your_sas_token # Azure Blob Storage SAS 토큰 AZURE_BLOB_BASE_URL=https://... # Azure Blob Storage 기본 URL # ================================ # Creatomate 템플릿 설정 # ================================ TEMPLATE_ID_VERTICAL=your_template_id # 세로형(9:16) 비디오 템플릿 ID TEMPLATE_DURATION_VERTICAL=60.0 # 세로형 비디오 기본 길이 (초) TEMPLATE_ID_HORIZONTAL=your_template_id # 가로형(16:9) 비디오 템플릿 ID TEMPLATE_DURATION_HORIZONTAL=20.0 # 가로형 비디오 기본 길이 (초) # ================================ # JWT 토큰 설정 # ================================ JWT_SECRET=your_secret_key # JWT 서명용 비밀키 (랜덤 문자열 권장) JWT_ALGORITHM=HS256 # JWT 알고리즘 (기본: HS256) JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60 # Access Token 만료 시간 (분) JWT_REFRESH_TOKEN_EXPIRE_DAYS=7 # Refresh Token 만료 시간 (일) # ================================ # 프롬프트 설정 # ================================ PROMPT_FOLDER_ROOT=./app/utils/prompts # 프롬프트 파일 루트 디렉토리 MARKETING_PROMPT_NAME=marketing_prompt # 마케팅 분석용 프롬프트 파일명 SUMMARIZE_PROMPT_NAME=summarize_prompt # 요약용 프롬프트 파일명 LYLIC_PROMPT_NAME=lyric_prompt # 가사 생성용 프롬프트 파일명 # ================================ # 로그 설정 # ================================ LOG_CONSOLE_ENABLED=True # 콘솔 로그 출력 여부 LOG_FILE_ENABLED=True # 파일 로그 저장 여부 LOG_LEVEL=DEBUG # 전체 로그 레벨 (DEBUG, INFO, WARNING, ERROR, CRITICAL) LOG_CONSOLE_LEVEL=DEBUG # 콘솔 출력 로그 레벨 LOG_FILE_LEVEL=DEBUG # 파일 저장 로그 레벨 LOG_MAX_SIZE_MB=15 # 로그 파일 최대 크기 (MB) LOG_BACKUP_COUNT=30 # 로그 백업 파일 보관 개수 LOG_DIR=logs # 로그 저장 디렉토리 경로 # - 절대 경로: 해당 경로 사용 # - 상대 경로: 프로젝트 루트 기준 # - /www/log/uvicorn 존재 시: 자동으로 해당 경로 사용 (운영) ``` ## 실행 방법 ### uv 설치 ```bash # Windows (PowerShell) powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" # macOS / Linux curl -LsSf https://astral.sh/uv/install.sh | sh ``` ### 의존성 설치 ```bash # 기본 설치 (uv가 자동으로 가상환경 생성) uv sync # 이미 venv를 만든 경우 (기존 가상환경 활성화 필요) uv sync --active playwright install playwright install-deps ``` ### 서버 실행 ```bash # 개발 서버 실행 fastapi dev main.py # 프로덕션 서버 실행 fastapi run main.py ``` ### 운영 업로드 및 메모리 한도 `POST /api/image/upload/blob`은 애플리케이션에서 파일당 15 MiB까지만 허용합니다. 운영 Nginx에서는 multipart 오버헤드를 고려해 이 엔드포인트의 요청 본문을 25 MiB로 제한합니다. 앱의 요청당 파일 합계 상한은 20 MiB이며, 나머지 5 MiB는 multipart 헤더와 `images_json`을 위한 여유입니다. 한 task에는 최대 100개 이미지만 누적할 수 있습니다. 200 MiB 이상의 요청을 허용하도록 Nginx 한도를 올리지 마세요. 프론트엔드는 이미지를 압축한 뒤 파일 한 개씩 전송해야 합니다. 운영 Nginx 설정은 이 저장소에서 관리되지 않으므로, 기존 `location = /api/image/upload/blob` 블록 안에서 다음 스니펫을 include합니다. ```nginx include /배포경로/deploy/nginx/ado2-image-upload-limit.conf; ``` 기존 설정이 prefix location만 사용한다면 그 블록의 `proxy_pass` 및 헤더 설정을 그대로 유지한 채, exact location을 추가하고 동일한 프록시 설정을 적용해야 합니다. 반영 전후에 실제 로드된 설정과 문법을 확인합니다. ```bash sudo nginx -T | grep -n -E 'server_name|image/upload/blob|client_max_body_size' sudo nginx -t sudo systemctl reload nginx ``` `proxy_request_buffering off`는 이 스니펫에 포함하지 않았습니다. 이 옵션만으로 FastAPI의 multipart 파싱이 Azure 청크 스트리밍으로 바뀌지는 않으며, 느린 클라이언트 연결이 애플리케이션을 직접 점유하는 시간이 늘어날 수 있습니다. Compose로 API를 실행하는 서버에서는 리소스 override를 함께 적용합니다. 이 override는 API 포트를 기본적으로 `127.0.0.1:8000`에만 바인딩해 외부 클라이언트가 Nginx의 요청 크기 제한을 우회하지 못하게 합니다. 운영 Nginx가 별도 컨테이너라면 호스트 포트를 공개하는 대신 두 서비스를 같은 내부 Docker 네트워크에 연결하세요. 부득이하게 `APP_BIND_ADDRESS`를 바꿀 때도 방화벽에서 8000 포트의 외부 접근을 차단해야 합니다. `!override` 구문을 위해 Docker Compose 2.24.4 이상이 필요합니다. ```bash docker compose -f docker-compose.yml -f compose.resources.yaml config --quiet docker compose -f docker-compose.yml -f compose.resources.yaml up -d --force-recreate app docker inspect castad-app \ --format 'memory={{.HostConfig.Memory}} reservation={{.HostConfig.MemoryReservation}} swap={{.HostConfig.MemorySwap}}' ``` 기본값은 hard limit 2 GiB, reservation 512 MiB이며 추가 swap은 허용하지 않습니다. 호스트 용량과 실제 렌더링 부하를 측정한 뒤 `APP_MEMORY_LIMIT`/`APP_MEMORY_RESERVATION`으로 조정할 수 있습니다. 예를 들어 `APP_MEMORY_LIMIT=3g`를 설정하면 hard limit와 swap limit가 함께 3 GiB로 변경됩니다. 주의: 현재 저장소의 Dockerfile은 Uvicorn을 실행하지만 운영 로그 파일명에는 Gunicorn이 나타납니다. 운영 프로세스가 호스트의 systemd/Gunicorn으로 직접 실행 중이라면 이 Compose 제한은 적용되지 않습니다. 배포 전에 실제 실행 주체를 확인하고, Compose 컨테이너가 아니라면 Gunicorn을 loopback 또는 Unix socket에만 bind하고 해당 서비스 관리자의 메모리 제한을 별도로 설정해야 합니다. 외부에서 앱 포트로 직접 접근할 수 있으면 Nginx의 25 MiB 제한을 우회할 수 있습니다. ## API 문서 서버 실행 후 `/docs` 에서 Scalar API 문서를 확인할 수 있습니다. ## 서버 아키텍처 ### 전체 시스템 흐름 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Client (Web/Mobile) │ └─────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ FastAPI Application │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ Auth API │ │ Home API │ │ Lyric API │ │ Song/Video API │ │ │ │ (카카오) │ │ (크롤링) │ │ (가사생성) │ │ (음악/영상 생성) │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │ └─────────┼────────────────┼────────────────┼─────────────────────┼───────────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ Kakao OAuth │ │ Naver Maps │ │ ChatGPT │ │ External AI Services │ │ (로그인) │ │ (크롤링) │ │ (OpenAI) │ │ ┌───────┐ ┌──────────┐ │ └─────────────────┘ └─────────────┘ └─────────────┘ │ │ Suno │ │Creatomate│ │ │ │ (음악) │ │ (영상) │ │ │ └───────┘ └──────────┘ │ └─────────────────────────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ Data Layer │ │ ┌─────────────┐ ┌─────────────────────┐ │ │ │ MySQL │ │ Azure Blob Storage │ │ │ │ (메인 DB) │ │ (미디어 저장소) │ │ │ └─────────────┘ └─────────────────────┘ │ │ ┌─────────────┐ │ │ │ Redis │ │ │ │ (캐시/세션) │ │ │ └─────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### 광고 콘텐츠 생성 플로우 ``` ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ 1. 입력 │───▶│ 2. 크롤링 │───▶│ 3. 가사 │───▶│ 4. 음악 │───▶│ 5. 영상 │ │ │ │ │ │ 생성 │ │ 생성 │ │ 생성 │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ┌────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │장소 URL │ │Naver Maps│ │ ChatGPT │ │ Suno AI │ │Creatomate│ │or 이미지 │ │ 크롤러 │ │ API │ │ API │ │ API │ └────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │장소 정보 │ │ 광고 가사 │ │ MP3 │ │ 광고 영상 │ │이미지 수집 │ │ 텍스트 │ │ 파일 │ │ 파일 │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ ``` ### 인증 플로우 (카카오 OAuth) ``` ┌────────┐ ┌────────────┐ ┌───────────┐ ┌────────────┐ │ Client │ │ CastAD │ │ Kakao │ │ MySQL │ │ │ │ Backend │ │ OAuth │ │ │ └───┬────┘ └─────┬──────┘ └─────┬─────┘ └─────┬──────┘ │ │ │ │ │ 1. 로그인 요청 │ │ │ │───────────────▶│ │ │ │ │ │ │ │ 2. 카카오 URL │ │ │ │◀───────────────│ │ │ │ │ │ │ │ 3. 카카오 로그인 │ │ │ │────────────────────────────────▶ │ │ │ │ │ │ │ 4. 인가 코드 │ │ │ │◀──────────────────────────────── │ │ │ │ │ │ │ 5. 콜백 (code) │ │ │ │───────────────▶│ 6. 토큰 요청 │ │ │ │─────────────────▶│ │ │ │ 7. Access Token │ │ │ │◀─────────────────│ │ │ │ │ │ │ │ 8. 사용자 저장/조회 │ │ │ │─────────────────────────────────▶ │ │ │◀───────────────────────────────── │ │ │ │ │ │ 9. JWT 토큰 발급 │ │ │ │◀───────────────│ │ │ │ │ │ │ ``` testAc