commit 086d898b6861007e68ee13dcea0c48fbd5a7aead Author: bluebamus Date: Mon Dec 15 13:51:06 2025 +0900 first commit diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..452ac00 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +FROM ubuntu:24.04 + +WORKDIR /app + +ENV DEBIAN_FRONTEND=noninteractive +ENV NODE_ENV=production + +# 시스템 패키지 설치 +RUN apt-get update && apt-get install -y \ + curl \ + wget \ + gnupg \ + lsb-release \ + apt-transport-https \ + ca-certificates \ + git \ + build-essential \ + python3 \ + && rm -rf /var/lib/apt/lists/* + +# Node.js 및 npm 설치 +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \ + apt-get install -y nodejs && \ + rm -rf /var/lib/apt/lists/* + +# 포트 개방 +EXPOSE 3000 3001 + +# 컨테이너 실행 상태 유지 +CMD ["tail", "-f", "/dev/null"] diff --git a/castad-data/.env.example b/castad-data/.env.example new file mode 100644 index 0000000..45ea3bf --- /dev/null +++ b/castad-data/.env.example @@ -0,0 +1,193 @@ +# ============================================ +# CaStAD v3.2.0 개발 환경 설정 파일 +# ============================================ +# 이 파일을 .env로 복사하여 사용하세요 +# cp .env.example .env +# ============================================ + +# ============================================ +# 🔧 기본 설정 (Basic Configuration) +# ============================================ + +# Node 환경 설정 +# - development: 개발 모드 (상세 로그, 핫 리로딩) +# - production: 프로덕션 모드 (최적화, 보안 강화) +NODE_ENV=development + +# 백엔드 서버 포트 (Express.js) +# - 기본값: 3001 +# - Vite 프록시가 이 포트로 API 요청을 전달합니다 +PORT=3001 + +# JWT 인증 시크릿 키 +# - 사용자 인증 토큰 생성에 사용 +# - 프로덕션에서는 반드시 복잡한 랜덤 문자열로 변경하세요! +# - 예: openssl rand -base64 32 +JWT_SECRET=dev-secret-key-change-in-production + +# URL 설정 (개발 환경) +# - FRONTEND_URL: 프론트엔드 URL (Vite 개발 서버) +# - BACKEND_URL: 백엔드 API URL +# - ALLOWED_ORIGINS: CORS 허용 도메인 (쉼표로 구분) +FRONTEND_URL=http://localhost:5173 +BACKEND_URL=http://localhost:3001 +ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000 + +# ============================================ +# 🔑 필수 API 키 (Required APIs) +# ============================================ +# 이 키들이 없으면 핵심 기능이 작동하지 않습니다 + +# Google Gemini AI API +# - 용도: AI 텍스트 생성, 마케팅 문구 작성, AI 매직 라이트 +# - 발급: https://ai.google.dev/ (Google AI Studio) +# - 무료 할당량: 분당 60회 요청 +# - 참고: 서버와 프론트엔드 모두에서 사용 (VITE_ 접두사 필요) +VITE_GEMINI_API_KEY=your-gemini-api-key + +# Suno AI API +# - 용도: AI 음악 생성 (배경음악, 광고 음악) +# - 발급: https://suno.ai/ 또는 프록시 서비스 사용 +# - 프록시 예시: https://github.com/gcui-art/suno-api +SUNO_API_KEY=your-suno-api-key + +# ============================================ +# 🎪 축제 연동 API (Festival Integration) +# ============================================ + +# 한국관광공사 Tour API +# - 용도: 전국 축제/행사 정보 조회, 지역별 축제 연동 +# - 발급: https://api.visitkorea.or.kr/ (회원가입 후 키 발급) +# - 무료 할당량: 일 1,000회 +# - 서비스: KorService2 (최신 버전) +TOURAPI_KEY=your-tour-api-key +TOURAPI_ENDPOINT=https://apis.data.go.kr/B551011/KorService2 + +# ============================================ +# 📍 지오코딩 API (Geocoding) +# ============================================ + +# Kakao REST API +# - 용도: 주소 → 좌표 변환, 펜션 위치 기반 근처 축제 검색 +# - 발급: https://developers.kakao.com/ (앱 생성 후 REST API 키) +# - 무료 할당량: 일 30,000회 +KAKAO_REST_KEY=your-kakao-rest-api-key + +# ============================================ +# 📺 YouTube 업로드 (YouTube Upload) +# ============================================ +# OAuth 2.0 방식 - 사용자별 개인 채널 연동 + +# YouTube OAuth 설정 +# - 설정 파일: server/client_secret.json +# - 발급 방법: +# 1. Google Cloud Console (https://console.cloud.google.com) +# 2. "사용자 인증 정보" → "OAuth 2.0 클라이언트 ID" 생성 +# 3. 리디렉션 URI 추가: http://localhost:3001/api/youtube/oauth/callback +# 4. JSON 다운로드 → server/client_secret.json으로 저장 +# - 주의: 개발/테스트 앱은 "Google에서 확인하지 않은 앱" 경고가 표시됨 + +# ============================================ +# 📊 Google Cloud Billing (선택) +# ============================================ +# BigQuery를 통한 클라우드 비용 분석 + +# Google Cloud Billing 설정 +# - 용도: API 사용량 비용 추적, 비용 분석 대시보드 +# - 전제 조건: +# 1. Google Cloud 프로젝트에서 결제 내보내기 → BigQuery 설정 +# 2. 서비스 계정 생성 (BigQuery 읽기 권한) +# 3. 서비스 계정 키 JSON 다운로드 +# - KEY_PATH: 서비스 계정 키 파일 경로 +# - PROJECT_ID: Google Cloud 프로젝트 ID +# - DATASET_ID: BigQuery 데이터셋 ID +GOOGLE_BILLING_KEY_PATH=./server/google-billing-key.json +GOOGLE_CLOUD_PROJECT_ID=your-project-id +GOOGLE_BILLING_DATASET_ID=billing_export + +# ============================================ +# 📧 이메일 서비스 (선택) +# ============================================ + +# Resend 이메일 API +# - 용도: 비밀번호 재설정, 알림 이메일 발송 +# - 발급: https://resend.com/ (가입 후 API 키 발급) +# - 무료 할당량: 월 100통 +# - 설정 안 하면 이메일 기능 비활성화 (앱은 정상 작동) +RESEND_API_KEY=your-resend-api-key +RESEND_FROM_EMAIL=CastAD + +# ============================================ +# 🔐 소셜 로그인 (선택) +# ============================================ + +# Google OAuth 로그인 +# - 용도: "Google로 로그인" 기능 +# - 발급: Google Cloud Console → 사용자 인증 정보 → OAuth 2.0 +# - 리디렉션 URI: http://localhost:3001/auth/google/callback +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret + +# Naver OAuth 로그인 +# - 용도: "네이버로 로그인" 기능 +# - 발급: https://developers.naver.com/ → 애플리케이션 등록 +# - 리디렉션 URI: http://localhost:3001/auth/naver/callback +NAVER_CLIENT_ID=your-naver-client-id +NAVER_CLIENT_SECRET=your-naver-client-secret + +# ============================================ +# 📱 SNS 연동 (선택) +# ============================================ + +# Instagram 서비스 (Python 마이크로서비스) +# - 용도: Instagram 자동 업로드 +# - 별도 설치 필요: server/instagram-service/ +# - 의존성: Python 3.8+, Fernet 암호화 +INSTAGRAM_SERVICE_URL=http://localhost:5001 +INSTAGRAM_SERVICE_PORT=5001 +INSTAGRAM_ENCRYPTION_KEY=your-fernet-encryption-key + +# TikTok API +# - 용도: TikTok 영상 업로드 +# - 발급: https://developers.tiktok.com/ → 앱 생성 +# - 리디렉션 URI: http://localhost:3001/api/tiktok/oauth/callback +TIKTOK_CLIENT_KEY=your-tiktok-client-key +TIKTOK_CLIENT_SECRET=your-tiktok-client-secret + +# ============================================ +# 🔧 기타 설정 (선택) +# ============================================ + +# Naver 크롤링 쿠키 (선택) +# - 용도: 네이버 지도에서 펜션 정보/사진 크롤링 +# - 획득 방법: 브라우저 개발자 도구 (F12) → Application → Cookies에서 복사 +# - 주의: 쿠키 만료 시 재설정 필요 (또는 관리자 대시보드에서 설정) +# NAVER_COOKIES="NNB=xxx; JSESSIONID=xxx" + +# Instagram 크롤링 쿠키 (선택) +# - 용도: 인스타그램 프로필에서 펜션 사진 크롤링 +# - 획득 방법: 인스타그램 로그인 후 개발자 도구에서 쿠키 복사 +# - 필수 쿠키: sessionid, csrftoken, ds_user_id +# - 주의: 쿠키 만료 시 재설정 필요 (또는 관리자 대시보드에서 설정) +# INSTAGRAM_COOKIES="sessionid=xxx; csrftoken=xxx; ds_user_id=xxx" + +# ============================================ +# 📝 설정 체크리스트 +# ============================================ +# 최소 필수 설정 (앱 실행에 필요): +# ✅ JWT_SECRET +# ✅ VITE_GEMINI_API_KEY +# ✅ SUNO_API_KEY +# +# 권장 설정 (주요 기능 활성화): +# ⬜ TOURAPI_KEY (축제 연동) +# ⬜ KAKAO_REST_KEY (위치 기반 축제 검색) +# ⬜ YouTube client_secret.json (YouTube 업로드) +# +# 선택 설정 (부가 기능): +# ⬜ RESEND_API_KEY (이메일 발송) +# ⬜ GOOGLE_CLIENT_ID/SECRET (Google 로그인) +# ⬜ NAVER_CLIENT_ID/SECRET (네이버 로그인) +# ⬜ TIKTOK_CLIENT_KEY/SECRET (TikTok 업로드) +# ⬜ INSTAGRAM_* (Instagram 업로드) +# ⬜ GOOGLE_BILLING_* (비용 분석) diff --git a/castad-data/.env.production.example b/castad-data/.env.production.example new file mode 100644 index 0000000..f61cb4f --- /dev/null +++ b/castad-data/.env.production.example @@ -0,0 +1,173 @@ +# ============================================ +# CaStAD v3.2.0 프로덕션 환경 설정 파일 +# ============================================ +# 도메인: castad1.ktenterprise.net +# 서버 배포 시 이 파일을 .env로 복사하여 사용 +# cp .env.production.example .env +# ============================================ + +# ============================================ +# 🔧 기본 설정 (Basic Configuration) +# ============================================ + +# Node 환경 - 반드시 production으로 설정 +NODE_ENV=production + +# 백엔드 서버 포트 +# - nginx가 이 포트로 프록시합니다 +PORT=3001 + +# JWT 인증 시크릿 키 +# - ⚠️ 반드시 복잡한 랜덤 문자열로 변경하세요! +# - 생성 명령: openssl rand -base64 32 +JWT_SECRET=your-super-secret-jwt-key-change-this-in-production + +# URL 설정 (프로덕션) +# - 실제 도메인으로 설정 +# - HTTPS 필수 +ALLOWED_ORIGINS=https://castad1.ktenterprise.net +FRONTEND_URL=https://castad1.ktenterprise.net +BACKEND_URL=https://castad1.ktenterprise.net + +# ============================================ +# 🔑 필수 API 키 (Required APIs) +# ============================================ +# ⚠️ 이 키들이 없으면 핵심 기능이 작동하지 않습니다 + +# Google Gemini AI API +# - 용도: AI 텍스트 생성, 마케팅 문구 작성, AI 매직 라이트 +# - 발급: https://ai.google.dev/ (Google AI Studio) +# - 무료 할당량: 분당 60회 요청, 일 1,500회 +# - 유료 전환 시 사용량 기반 과금 +VITE_GEMINI_API_KEY=your-gemini-api-key + +# Suno AI API +# - 용도: AI 음악 생성 (배경음악, 광고 음악) +# - 발급: Suno API 프록시 서비스 사용 +# - 참고: https://github.com/gcui-art/suno-api +SUNO_API_KEY=your-suno-api-key + +# ============================================ +# 🎪 축제 연동 API (Festival Integration) +# ============================================ + +# 한국관광공사 Tour API +# - 용도: 전국 축제/행사 정보 조회, 지역별 축제 연동 +# - 발급: https://api.visitkorea.or.kr/ (회원가입 후 키 발급) +# - 무료 할당량: 일 1,000회 +# - 서비스: KorService2 사용 +TOURAPI_KEY=your-tour-api-key +TOURAPI_ENDPOINT=https://apis.data.go.kr/B551011/KorService2 + +# ============================================ +# 📍 지오코딩 API (Geocoding) +# ============================================ + +# Kakao REST API +# - 용도: 주소 → 좌표 변환, 펜션 위치 기반 근처 축제 검색 +# - 발급: https://developers.kakao.com/ +# - 무료 할당량: 일 30,000회 +KAKAO_REST_KEY=your-kakao-rest-api-key + +# ============================================ +# 📺 YouTube 업로드 (YouTube Upload) +# ============================================ +# OAuth 2.0 방식 - 사용자별 개인 채널 연동 + +# YouTube OAuth 설정 +# - 설정 파일: server/client_secret.json (별도 생성 필요) +# - 설정 방법: +# 1. Google Cloud Console → 사용자 인증 정보 +# 2. OAuth 2.0 클라이언트 ID 생성 (웹 애플리케이션) +# 3. 승인된 리디렉션 URI 추가: +# https://castad1.ktenterprise.net/api/youtube/oauth/callback +# 4. JSON 다운로드 → server/client_secret.json으로 저장 +# - 앱 게시: 테스트 사용자 100명 이상이면 앱 검증 필요 + +# ============================================ +# 📊 Google Cloud Billing (선택) +# ============================================ +# BigQuery를 통한 API 비용 분석 - 관리자 대시보드 기능 + +# Google Cloud Billing 설정 +# - 용도: Gemini/Suno 등 API 사용 비용 추적 +# - 설정 파일: server/google-billing-key.json (서비스 계정 키) +# - 설정 방법: +# 1. Google Cloud Console → 결제 → 결제 내보내기 → BigQuery +# 2. IAM → 서비스 계정 생성 (BigQuery 데이터 뷰어 역할) +# 3. 키 생성 → JSON 다운로드 → server/google-billing-key.json +GOOGLE_BILLING_KEY_PATH=./server/google-billing-key.json +GOOGLE_CLOUD_PROJECT_ID=your-project-id +GOOGLE_BILLING_DATASET_ID=billing_export + +# ============================================ +# 📧 이메일 서비스 (선택) +# ============================================ + +# Resend 이메일 API +# - 용도: 비밀번호 재설정, 알림 이메일 +# - 발급: https://resend.com/ +# - 설정 안 하면 이메일 기능 비활성화 (앱은 정상 작동) +# - 프로덕션: 도메인 인증 필요 +RESEND_API_KEY=your-resend-api-key +RESEND_FROM_EMAIL=CastAD + +# ============================================ +# 🔐 소셜 로그인 (선택) +# ============================================ + +# Google OAuth 로그인 +# - 리디렉션 URI: https://castad1.ktenterprise.net/auth/google/callback +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret + +# Naver OAuth 로그인 +# - 리디렉션 URI: https://castad1.ktenterprise.net/auth/naver/callback +NAVER_CLIENT_ID=your-naver-client-id +NAVER_CLIENT_SECRET=your-naver-client-secret + +# ============================================ +# 📱 SNS 연동 (선택) +# ============================================ + +# Instagram 서비스 (Python 마이크로서비스) +# - 별도 설치 필요: server/instagram-service/ +# - PM2로 별도 프로세스로 실행 +INSTAGRAM_SERVICE_URL=http://localhost:5001 +INSTAGRAM_SERVICE_PORT=5001 +INSTAGRAM_ENCRYPTION_KEY=your-fernet-encryption-key + +# TikTok API +# - 리디렉션 URI: https://castad1.ktenterprise.net/api/tiktok/oauth/callback +TIKTOK_CLIENT_KEY=your-tiktok-client-key +TIKTOK_CLIENT_SECRET=your-tiktok-client-secret + +# ============================================ +# 📋 프로덕션 배포 체크리스트 +# ============================================ +# +# 🔴 필수 (앱 실행에 반드시 필요): +# ✅ NODE_ENV=production +# ✅ JWT_SECRET (랜덤 문자열로 변경) +# ✅ VITE_GEMINI_API_KEY +# ✅ SUNO_API_KEY +# ✅ FRONTEND_URL, BACKEND_URL (실제 도메인) +# +# 🟡 권장 (주요 기능): +# ⬜ TOURAPI_KEY (축제 연동) +# ⬜ KAKAO_REST_KEY (위치 기반 축제 검색) +# ⬜ server/client_secret.json (YouTube 업로드) +# +# 🟢 선택 (부가 기능): +# ⬜ RESEND_API_KEY (이메일) +# ⬜ GOOGLE_CLIENT_ID/SECRET (Google 로그인) +# ⬜ NAVER_CLIENT_ID/SECRET (네이버 로그인) +# ⬜ TIKTOK_CLIENT_KEY/SECRET (TikTok) +# ⬜ INSTAGRAM_* (Instagram) +# ⬜ GOOGLE_BILLING_* (비용 분석) +# +# 🔒 보안 파일 (git에 포함하지 않음): +# ⬜ .env (이 파일) +# ⬜ server/client_secret.json (YouTube OAuth) +# ⬜ server/google-billing-key.json (BigQuery) +# ⬜ server/youtube-tokens.json (사용자 토큰, 자동생성) diff --git a/castad-data/.gitignore b/castad-data/.gitignore new file mode 100644 index 0000000..6164900 --- /dev/null +++ b/castad-data/.gitignore @@ -0,0 +1,56 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +*.tar.gz +server/downloads/* +!server/downloads/.gitkeep +server/database.sqlite + +# Environment +.env +.env.* +!.env.example +!.env.production.example + +# Sensitive files (API keys, credentials) +server/google-billing-key.json +server/client_secret.json +server/youtube-tokens.json +**/tokens.json + +# Analysis files +*_analysis_*.txt + +# Windows Zone.Identifier files +*:Zone.Identifier + +# Python +__pycache__/ +*.py[cod] +*$py.class +venv/ +*.egg-info/ + +# Data files +data/ diff --git a/castad-data/App.tsx b/castad-data/App.tsx new file mode 100644 index 0000000..8a2b09e --- /dev/null +++ b/castad-data/App.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; +import { AuthProvider, useAuth } from './src/contexts/AuthContext'; +import { LanguageProvider } from './src/contexts/LanguageContext'; +import { ThemeProvider } from './src/contexts/ThemeContext'; +import Navbar from './components/Navbar'; +import CastADApp from './src/pages/CastADApp'; +import LoginPage from './src/pages/LoginPage'; +import RegisterPage from './src/pages/RegisterPage'; +import ForgotPasswordPage from './src/pages/ForgotPasswordPage'; +import ResetPasswordPage from './src/pages/ResetPasswordPage'; +import VerifyEmailPage from './src/pages/VerifyEmailPage'; +import OAuthCallbackPage from './src/pages/OAuthCallbackPage'; +import AdminDashboard from './src/pages/AdminDashboard'; +import LandingPage from './src/pages/LandingPage'; +import BrandPage from './src/pages/BrandPage'; +import CreditsPage from './src/pages/CreditsPage'; +import './src/styles/globals.css'; +import './src/styles/text-effects.css'; +import './src/styles/animations.css'; + +// 홈 라우트: 로그인 여부에 따라 랜딩 또는 SaaS 앱 표시 +const HomeRoute: React.FC = () => { + const { user } = useAuth(); + return user ? : ; +}; + +// SaaS 앱 보호 라우트: 로그인 필요 +const ProtectedAppRoute: React.FC = () => { + const { user } = useAuth(); + return user ? : ; +}; + +// 랜딩 페이지용 라우트 (Navbar 포함) +const PublicRoutes: React.FC = () => { + return ( + <> + +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
+ + ); +}; + +const AppRoutes: React.FC = () => { + const { user } = useAuth(); + + // 로그인된 사용자는 SaaS 앱으로 (Navbar 없음, Sidebar 사용) + if (user) { + return ( + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ); + } + + // 로그인되지 않은 사용자는 공개 페이지로 + return ; +}; + +const App: React.FC = () => { + return ( + + + + + + + + + + ); +}; + +export default App; diff --git a/castad-data/CLAUDE.md b/castad-data/CLAUDE.md new file mode 100644 index 0000000..9d91ac6 --- /dev/null +++ b/castad-data/CLAUDE.md @@ -0,0 +1,75 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +CastAD (formerly ADo4) is an AI-powered marketing video generation platform for pension (accommodation) businesses. It uses Google Gemini for content generation/TTS and Suno AI for music creation, with Puppeteer for server-side video rendering. + +## Development Commands + +```bash +# Start both frontend and backend concurrently (recommended) +./start.sh + +# Or run manually: +npm run dev # Run frontend + backend together +npm run build # Build frontend for production +cd server && node index.js # Run backend only + +# Install dependencies +npm install # Frontend dependencies +cd server && npm install # Backend dependencies +npm run build:all # Install all + build frontend +``` + +## Architecture + +### Three-Layer Structure +- **Frontend**: React 19 + TypeScript + Vite (port 3000/5173) +- **Backend**: Express.js + SQLite (port 3001) +- **External APIs**: Google Gemini, Suno AI, YouTube Data API + +### Key Directories +- `components/` - React UI components (InputForm, Navbar, ResultPlayer, etc.) +- `src/pages/` - Page components (GeneratorPage, Dashboard, AdminDashboard, Login/Register) +- `src/contexts/` - Global state (AuthContext for JWT auth, LanguageContext for i18n) +- `src/locales.ts` - Multi-language translations (ko, en, ja, zh, th, vi) +- `services/` - Frontend API services (geminiService, sunoService, ffmpegService, naverService) +- `server/` - Express backend + - `index.js` - Main server with auth, rendering, and API routes + - `db.js` - SQLite schema (users, history tables) + - `geminiBackendService.js` - Server-side Gemini operations + - `youtubeService.js` - YouTube upload via OAuth + - `downloads/` - Generated video storage + - `temp/` - Temporary rendering files + +### Data Flow +1. User inputs business info/photos → GeneratorPage.tsx +2. Frontend calls Gemini API for creative content → geminiService.ts +3. Music generation via Suno proxy → sunoService.ts +4. Render request sent to backend → server/index.js +5. Puppeteer captures video, FFmpeg merges audio → final MP4 in downloads/ + +### Authentication +- JWT tokens stored in localStorage +- Auth middleware in server/index.js (`authenticateToken`, `requireAdmin`) +- Roles: 'user' and 'admin' +- Default admin: `admin` / `admin123` + +### Environment Variables (.env in root) +``` +VITE_GEMINI_API_KEY= # Required: Google AI Studio API key +SUNO_API_KEY= # Required: Suno AI proxy key +JWT_SECRET= # Required: JWT signing secret +FRONTEND_URL= # Optional: For CORS (default: http://localhost:5173) +PORT= # Optional: Backend port (default: 3001) +``` + +## Tech Notes + +- Vite proxies `/api`, `/render`, `/downloads`, `/temp` to backend (port 3001) +- Path alias `@/` maps to project root +- SQLite database at `server/database.sqlite` (not tracked in git) +- Video rendering uses Puppeteer headless Chrome + FFmpeg +- Multi-language support: UI language separate from content generation language diff --git a/castad-data/DEPLOY.md b/castad-data/DEPLOY.md new file mode 100644 index 0000000..9996f17 --- /dev/null +++ b/castad-data/DEPLOY.md @@ -0,0 +1,279 @@ +# CaStAD 서버 배포 가이드 + +## 도메인 정보 +- **Primary**: https://castad.ktenterprise.net +- **Secondary**: https://ado2.whitedonkey.kr + +--- + +## 1. 서버 요구사항 + +| 항목 | 최소 | 권장 | +|------|------|------| +| **CPU** | 2 Core | 4 Core | +| **RAM** | 4GB | 8GB | +| **Storage** | 50GB | 100GB SSD | +| **OS** | Ubuntu 20.04+ | Ubuntu 22.04 LTS | + +### 필수 소프트웨어 + +```bash +# Node.js 18+ +curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - +sudo apt-get install -y nodejs + +# PM2 (프로세스 관리) +sudo npm install -g pm2 + +# Nginx +sudo apt-get install -y nginx + +# FFmpeg (영상 렌더링) +sudo apt-get install -y ffmpeg + +# Python 3 (Instagram 서비스) +sudo apt-get install -y python3 python3-pip + +# Chromium (Puppeteer용) +sudo apt-get install -y chromium-browser + +# Certbot (SSL 인증서) +sudo apt-get install -y certbot python3-certbot-nginx +``` + +--- + +## 2. 프로젝트 클론 + +```bash +# 디렉토리 생성 +sudo mkdir -p /var/www/castad +sudo chown $USER:$USER /var/www/castad + +# Git 클론 +cd /var/www/castad +git clone https://github.com/waabaa/19-claude-saas-castad.git . + +# 실행 권한 부여 +chmod +x startserver.sh +``` + +--- + +## 3. 환경 변수 설정 + +```bash +# .env 파일 생성 +cp .env.production.example .env + +# 편집 +nano .env +``` + +**필수 변경 항목:** +```bash +# JWT 시크릿 (반드시 변경!) +JWT_SECRET=your-unique-secret-key-here + +# Gemini API 키 +VITE_GEMINI_API_KEY=your-key + +# Suno API 키 +SUNO_API_KEY=your-key + +# Instagram 암호화 키 (생성 명령) +python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" +``` + +--- + +## 4. YouTube OAuth 설정 + +```bash +# Google Cloud Console에서 다운로드한 파일을 복사 +cp /path/to/client_secret.json server/client_secret.json +``` + +--- + +## 5. Nginx 설정 + +```bash +# Nginx 설정 파일 복사 +sudo cp nginx/castad.conf /etc/nginx/sites-available/castad.conf + +# 심볼릭 링크 생성 +sudo ln -s /etc/nginx/sites-available/castad.conf /etc/nginx/sites-enabled/ + +# 기본 사이트 비활성화 (선택) +sudo rm /etc/nginx/sites-enabled/default + +# 문법 검사 +sudo nginx -t + +# Nginx 재시작 +sudo systemctl restart nginx +``` + +--- + +## 6. SSL 인증서 발급 + +```bash +# Let's Encrypt SSL 인증서 발급 +sudo certbot --nginx -d castad.ktenterprise.net -d ado2.whitedonkey.kr + +# 자동 갱신 테스트 +sudo certbot renew --dry-run +``` + +**Nginx 설정 업데이트 (자동 수정됨):** +인증서 경로가 다를 경우 `nginx/castad.conf` 수정: +```nginx +ssl_certificate /etc/letsencrypt/live/castad.ktenterprise.net/fullchain.pem; +ssl_certificate_key /etc/letsencrypt/live/castad.ktenterprise.net/privkey.pem; +``` + +--- + +## 7. 서버 시작 + +```bash +# 서버 시작 (빌드 포함) +./startserver.sh start + +# 상태 확인 +./startserver.sh status + +# 로그 보기 +./startserver.sh logs +``` + +--- + +## 8. PM2 시작 시 자동 실행 설정 + +```bash +# 현재 상태 저장 +pm2 save + +# 시스템 시작 시 자동 실행 +pm2 startup + +# 표시되는 명령어 실행 (예시) +sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u ubuntu --hp /home/ubuntu +``` + +--- + +## 9. 방화벽 설정 (선택) + +```bash +# UFW 설정 +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp +sudo ufw allow 22/tcp +sudo ufw enable +``` + +--- + +## 명령어 요약 + +| 명령어 | 설명 | +|--------|------| +| `./startserver.sh start` | 서버 시작 (빌드 포함) | +| `./startserver.sh stop` | 서버 중지 | +| `./startserver.sh restart` | 서버 재시작 | +| `./startserver.sh status` | 상태 확인 | +| `./startserver.sh logs` | 로그 보기 | +| `./startserver.sh update` | Git pull + 재빌드 + 재시작 | + +--- + +## 업데이트 방법 + +```bash +cd /var/www/castad + +# 간단한 방법 +./startserver.sh update + +# 또는 수동으로 +git pull origin main +npm install --legacy-peer-deps +cd server && npm install --legacy-peer-deps && cd .. +npm run build +pm2 restart all +``` + +--- + +## 트러블슈팅 + +### 502 Bad Gateway +```bash +# 백엔드 상태 확인 +pm2 status +pm2 logs castad-backend + +# 포트 확인 +sudo netstat -tlnp | grep 3001 +``` + +### SSL 인증서 오류 +```bash +# 인증서 갱신 +sudo certbot renew + +# Nginx 재시작 +sudo systemctl restart nginx +``` + +### Puppeteer 오류 +```bash +# Chromium 설치 확인 +chromium-browser --version + +# 또는 환경변수 설정 +export PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser +``` + +### Instagram 서비스 오류 +```bash +# Python 의존성 재설치 +pip3 install -r server/instagram/requirements.txt + +# 서비스 재시작 +pm2 restart castad-instagram +``` + +--- + +## 백업 + +```bash +# 데이터베이스 백업 +cp server/database.sqlite backups/database_$(date +%Y%m%d).sqlite + +# 업로드 파일 백업 +tar -czvf backups/uploads_$(date +%Y%m%d).tar.gz server/downloads/ +``` + +--- + +## 모니터링 + +```bash +# PM2 모니터링 +pm2 monit + +# 시스템 리소스 +htop + +# Nginx 접속 로그 +tail -f /var/log/nginx/castad_access.log + +# Nginx 에러 로그 +tail -f /var/log/nginx/castad_error.log +``` diff --git a/castad-data/GEMINI.md b/castad-data/GEMINI.md new file mode 100644 index 0000000..6358e3e --- /dev/null +++ b/castad-data/GEMINI.md @@ -0,0 +1,85 @@ +# BizVibe - AI Music Video Generator + +## Project Overview +**BizVibe** is a React-based web application designed to automatically generate marketing music videos for businesses. It leverages the power of Google's Gemini ecosystem and Suno AI to create a comprehensive multimedia package including: + +* **Creative Text:** Ad copy and lyrics/scripts generated by **Gemini 3 Pro**. +* **Audio:** + * Custom songs composed by **Suno AI** (V5 model). + * Professional voiceovers generated by **Gemini TTS** (`gemini-2.5-flash-preview-tts`). +* **Visuals:** + * High-quality ad posters designed by **Gemini 3 Pro Image** (`gemini-3-pro-image-preview`). + * Cinematic video backgrounds created by **Veo** (`veo-3.1-fast-generate-preview`). + +The application provides an end-to-end flow: collecting business details -> generating assets -> previewing the final music video. + +## Architecture & Tech Stack +* **Frontend Framework:** React 19 with Vite. +* **Language:** TypeScript. +* **AI Integration:** + * `@google/genai` SDK for interacting with Gemini and Veo models. + * Custom REST API integration for Suno AI (via CORS proxy). +* **Media Processing:** FFmpeg (WASM) is included in dependencies (`@ffmpeg/ffmpeg`), likely for client-side media assembly. +* **Styling:** Tailwind CSS. +* **State Management:** React Context / Local State. + +## Building and Running + +### Prerequisites +* Node.js (v18+ recommended) +* A valid Google Cloud Project with Gemini API access enabled. + +### Installation +Install the project dependencies: +```bash +npm install +``` + +### Development Server +Start the local development server: +```bash +npm run dev +``` +Access the app at `http://localhost:5173` (default Vite port). + +### Production Build +Create a production-ready build: +```bash +npm run build +``` +Preview the build locally: +```bash +npm run preview +``` + +### Configuration +* **API Key:** The application requires a Google Gemini API Key. + * It checks for `process.env.API_KEY`. + * It also includes an `ApiKeySelector` component that allows users to select/input a key if running in a specific AI Studio environment. +* **Suno API:** The `sunoService.ts` currently contains a hardcoded API key and uses a CORS proxy. *Note: This should be externalized in a production environment.* + +## Development Conventions + +### File Structure +* `src/components/`: Reusable UI components (Input forms, Result players, Loading overlays). +* `src/services/`: specialized modules for API interactions. + * `geminiService.ts`: Handles Text, Image, Video (Veo), and TTS generation. + * `sunoService.ts`: Handles music generation via Suno API. + * `ffmpegService.ts`: Media processing utilities. +* `src/types.ts`: Centralized TypeScript definitions for domain models (`BusinessInfo`, `GeneratedAssets`, etc.). +* `App.tsx`: Main application controller handling the generation workflow state machine. + +### Code Style +* **Components:** Functional components with Hooks. +* **Styling:** Utility-first CSS using Tailwind classes. +* **Async Handling:** `async/await` pattern used extensively in services with error handling for API failures. +* **Type Safety:** Strict TypeScript interfaces for all data models and API responses. + +### Key Workflows +1. **Input:** User submits `BusinessInfo` (including images). +2. **Processing (Sequential/Parallel):** + * Text content is generated first. + * Audio (Song or TTS) is generated using the text. + * Poster image is generated using uploaded images. + * Video background is generated using the Poster image. +3. **Output:** `GeneratedAssets` are aggregated and displayed in the `ResultPlayer`. diff --git a/castad-data/PLAN.md b/castad-data/PLAN.md new file mode 100644 index 0000000..a3e5148 --- /dev/null +++ b/castad-data/PLAN.md @@ -0,0 +1,305 @@ +# CastAD SaaS UI/UX 전체 리디자인 계획 + +## 개요 +POC 상태의 펜션 쇼츠 자동 생성 웹사이트를 프로페셔널한 SaaS 서비스로 전환 + +## 기술 스택 결정 + +| 항목 | 현재 | 변경 후 | +|------|------|---------| +| CSS Framework | Tailwind CDN | Tailwind npm + PostCSS | +| UI Components | 직접 구현 | shadcn/ui (Radix 기반) | +| Icons | lucide-react | lucide-react (유지) | +| Design System | 없음 | CSS Variables 기반 토큰 | +| Animations | index.html 인라인 | tailwind-animate + CSS 파일 분리 | + +## 디자인 방향 + +### 컬러 팔레트 (다크 테마) +- **Background**: `#09090b` (zinc-950) +- **Card**: `#18181b` (zinc-900) +- **Border**: `#27272a` (zinc-800) +- **Primary**: `#a855f7` (purple-500) → `#9333ea` (purple-600) +- **Accent**: `#06b6d4` (cyan-500) +- **Success**: `#22c55e` (green-500) +- **Destructive**: `#ef4444` (red-500) + +### 타이포그래피 +- **Display**: Inter (600-700) +- **Body**: Inter (400) +- **Mono**: JetBrains Mono + +### 레이아웃 원칙 +- 최대 너비: 1280px (7xl) +- 섹션 패딩: 24-32px +- 카드 둥글기: 12px (rounded-xl) +- 일관된 8px 그리드 시스템 + +--- + +## 단계별 구현 계획 + +### Phase 1: 인프라 설정 (기반 작업) + +#### 1.1 Tailwind CSS npm 전환 +- [ ] `tailwindcss`, `postcss`, `autoprefixer` 설치 +- [ ] `tailwind.config.js` 생성 (커스텀 컬러, 폰트, 애니메이션) +- [ ] `postcss.config.js` 생성 +- [ ] `src/styles/globals.css` 생성 (Tailwind directives) +- [ ] index.html에서 CDN 제거 + +#### 1.2 shadcn/ui 초기화 +- [ ] `npx shadcn@latest init` 실행 +- [ ] components.json 설정 (경로, 스타일) +- [ ] `src/components/ui/` 디렉토리 구조 설정 + +#### 1.3 디자인 토큰 설정 +- [ ] CSS variables 정의 (colors, radius, spacing) +- [ ] 다크 테마 기본값 설정 +- [ ] 폰트 로딩 최적화 (Google Fonts → next/font 스타일) + +--- + +### Phase 2: 기본 컴포넌트 구축 + +#### 2.1 shadcn/ui 컴포넌트 설치 +```bash +npx shadcn@latest add button card input label select textarea +npx shadcn@latest add dialog sheet tabs toast sonner +npx shadcn@latest add dropdown-menu avatar badge separator +npx shadcn@latest add progress skeleton scroll-area +npx shadcn@latest add form (react-hook-form + zod) +``` + +#### 2.2 커스텀 컴포넌트 생성 +- [ ] `PageHeader` - 페이지 제목 + 설명 +- [ ] `PageContainer` - 일관된 레이아웃 래퍼 +- [ ] `FeatureCard` - 기능 소개 카드 +- [ ] `PricingCard` - 가격표 카드 +- [ ] `StatCard` - 통계 카드 +- [ ] `VideoPlayer` - 영상 재생기 (기존 ResultPlayer 리팩토링) +- [ ] `LoadingSpinner` - 로딩 인디케이터 +- [ ] `EmptyState` - 빈 상태 표시 + +#### 2.3 레이아웃 컴포넌트 +- [ ] `MainLayout` - 전체 앱 레이아웃 +- [ ] `DashboardLayout` - 대시보드용 사이드바 레이아웃 +- [ ] `AuthLayout` - 로그인/회원가입 전용 레이아웃 + +--- + +### Phase 3: Navbar 리디자인 + +#### 현재 문제점 +- 모바일 메뉴 없음 +- 언어 선택 UI 투박함 +- 사용자 메뉴 단순함 + +#### 개선 사항 +- [ ] 반응형 모바일 메뉴 (Sheet 컴포넌트 활용) +- [ ] 언어 선택 드롭다운 개선 +- [ ] 사용자 아바타 + 드롭다운 메뉴 +- [ ] 네비게이션 링크 호버 효과 +- [ ] 스크롤 시 배경 blur 효과 + +--- + +### Phase 4: Landing Page 리디자인 + +#### 4.1 Hero 섹션 +- [ ] 더 절제된 타이포그래피 (text-4xl → text-5xl) +- [ ] 애니메이션 최적화 (framer-motion 고려) +- [ ] CTA 버튼 개선 (그래디언트 보더) +- [ ] 신뢰도 지표 리디자인 + +#### 4.2 Features 섹션 +- [ ] FeatureCard 컴포넌트로 통일 +- [ ] 아이콘 + 제목 + 설명 구조 +- [ ] 호버 시 미묘한 상승 효과 + +#### 4.3 How It Works +- [ ] 스텝 카드 (번호 + 아이콘 + 설명) +- [ ] 연결선 또는 화살표 그래픽 + +#### 4.4 Success Cases +- [ ] 캐러셀 개선 (자동 재생 + 수동 조작) +- [ ] 후기 카드 디자인 개선 + +#### 4.5 Pricing 섹션 +- [ ] PricingCard 컴포넌트 활용 +- [ ] 인기 플랜 하이라이트 +- [ ] 기능 비교표 추가 고려 + +#### 4.6 Footer +- [ ] 링크 구조화 +- [ ] 소셜 미디어 아이콘 +- [ ] 저작권 정보 + +--- + +### Phase 5: Generator Page 리디자인 + +#### 5.1 InputForm 분할 (47KB → 6개 컴포넌트) +- [ ] `BusinessInfoSection` - 비즈니스 기본 정보 +- [ ] `ImageUploadSection` - 이미지 업로드 +- [ ] `AudioSettingsSection` - 오디오 설정 +- [ ] `VisualSettingsSection` - 비주얼 설정 +- [ ] `CategorySection` - 카테고리 선택 +- [ ] `FormActions` - 제출 버튼 + +#### 5.2 폼 UI 개선 +- [ ] shadcn Form + react-hook-form + zod 적용 +- [ ] 실시간 유효성 검사 +- [ ] 섹션별 접기/펼치기 (Collapsible) +- [ ] 진행 상태 인디케이터 + +#### 5.3 Loading 상태 개선 +- [ ] 스켈레톤 UI +- [ ] 단계별 진행 표시 (Stepper) +- [ ] 취소 기능 + +--- + +### Phase 6: Result Player 리디자인 + +#### 6.1 ResultPlayer 분할 (38KB → 5개 컴포넌트) +- [ ] `VideoPreview` - 영상 미리보기 +- [ ] `AudioControls` - 오디오 컨트롤 +- [ ] `TextOverlayPreview` - 텍스트 오버레이 +- [ ] `DownloadOptions` - 다운로드 옵션 +- [ ] `SharePanel` - 공유 기능 + +#### 6.2 UI 개선 +- [ ] 탭 UI 개선 (Tabs 컴포넌트) +- [ ] 진행률 표시 개선 +- [ ] 토스트 알림 (Sonner) + +--- + +### Phase 7: Dashboard 리디자인 + +#### 7.1 사용자 대시보드 +- [ ] 사이드바 레이아웃 적용 +- [ ] 통계 카드 (생성 횟수, 저장 공간 등) +- [ ] 최근 생성물 그리드 +- [ ] 필터링 + 검색 기능 +- [ ] 페이지네이션 + +#### 7.2 관리자 대시보드 +- [ ] DataTable 컴포넌트 (정렬, 필터, 페이지네이션) +- [ ] 사용자 관리 UI 개선 +- [ ] 통계 차트 (선택적) + +--- + +### Phase 8: Auth Pages 리디자인 + +#### 8.1 Login/Register +- [ ] AuthLayout 적용 +- [ ] 폼 유효성 검사 개선 +- [ ] 소셜 로그인 버튼 (UI만, 추후 구현) +- [ ] 비밀번호 강도 표시 + +--- + +### Phase 9: 반응형 및 접근성 + +#### 9.1 반응형 +- [ ] 모든 페이지 모바일 테스트 +- [ ] 터치 타겟 크기 확인 (44x44px 최소) +- [ ] 가로 스크롤 제거 + +#### 9.2 접근성 +- [ ] ARIA 라벨 추가 +- [ ] 키보드 네비게이션 확인 +- [ ] 색상 대비 검사 (WCAG AA) +- [ ] 포커스 상태 시각화 + +--- + +### Phase 10: 마무리 + +#### 10.1 성능 최적화 +- [ ] 이미지 최적화 (lazy loading) +- [ ] 번들 사이즈 분석 +- [ ] 불필요한 의존성 제거 + +#### 10.2 코드 정리 +- [ ] 사용하지 않는 코드 제거 +- [ ] TypeScript 타입 정리 +- [ ] 컴포넌트 문서화 (주석) + +--- + +## 파일 구조 변경 + +``` +/ +├── src/ +│ ├── components/ +│ │ ├── ui/ # shadcn/ui 컴포넌트 +│ │ │ ├── button.tsx +│ │ │ ├── card.tsx +│ │ │ ├── input.tsx +│ │ │ └── ... +│ │ ├── layout/ # 레이아웃 컴포넌트 +│ │ │ ├── MainLayout.tsx +│ │ │ ├── DashboardLayout.tsx +│ │ │ └── AuthLayout.tsx +│ │ ├── generator/ # Generator 관련 컴포넌트 +│ │ │ ├── BusinessInfoSection.tsx +│ │ │ ├── ImageUploadSection.tsx +│ │ │ └── ... +│ │ ├── player/ # Player 관련 컴포넌트 +│ │ │ ├── VideoPreview.tsx +│ │ │ └── ... +│ │ └── shared/ # 공유 컴포넌트 +│ │ ├── PageHeader.tsx +│ │ ├── FeatureCard.tsx +│ │ └── ... +│ ├── styles/ +│ │ ├── globals.css # Tailwind + CSS variables +│ │ └── animations.css # 텍스트 이펙트 (유지) +│ ├── lib/ +│ │ └── utils.ts # cn() 등 유틸리티 +│ └── pages/ # 페이지 컴포넌트 +├── components/ # 기존 컴포넌트 (점진적 이전) +├── tailwind.config.js # 새로 생성 +├── postcss.config.js # 새로 생성 +└── components.json # shadcn/ui 설정 +``` + +--- + +## 예상 작업량 + +| Phase | 작업 내용 | 파일 수 | +|-------|----------|--------| +| 1 | 인프라 설정 | 5-6 | +| 2 | 기본 컴포넌트 | 15-20 | +| 3 | Navbar | 2-3 | +| 4 | Landing Page | 5-6 | +| 5 | Generator Page | 8-10 | +| 6 | Result Player | 6-8 | +| 7 | Dashboard | 4-5 | +| 8 | Auth Pages | 2-3 | +| 9 | 반응형/접근성 | 전체 수정 | +| 10 | 마무리 | 전체 검토 | + +--- + +## 주의사항 + +1. **점진적 마이그레이션**: 기존 기능이 깨지지 않도록 단계별 진행 +2. **텍스트 이펙트 보존**: 11가지 텍스트 이펙트는 유지 (핵심 기능) +3. **다국어 지원 유지**: i18n 구조 그대로 유지 +4. **백엔드 연동 유지**: API 호출 로직 변경 없음 + +--- + +## 승인 필요 항목 + +진행 전 확인: +1. 이 계획대로 진행해도 될까요? +2. 특별히 우선순위를 높이거나 제외할 부분이 있나요? +3. Phase 1부터 순차적으로 진행할까요? diff --git a/castad-data/README.md b/castad-data/README.md new file mode 100644 index 0000000..9602ae3 --- /dev/null +++ b/castad-data/README.md @@ -0,0 +1,4014 @@ +# CaStAD (카스타드) - AI-Powered Pension Marketing Video Platform + +

+ CaStAD Logo +

+ +**CaStAD**는 펜션/숙박업소를 위한 **AI 기반 마케팅 비디오 자동 생성 및 멀티 플랫폼 통합 관리 SaaS 플랫폼**입니다. +Google Gemini AI와 Suno AI를 활용하여 광고 카피, 음악, 영상을 자동으로 생성하고, OAuth 2.0 기반 YouTube, TikTok 채널 연동을 통해 직접 업로드까지 지원합니다. + +![Project Status](https://img.shields.io/badge/Status-Production-success) +![Version](https://img.shields.io/badge/Version-3.7.0-blue) +![License](https://img.shields.io/badge/License-MIT-green) +![Tech Stack](https://img.shields.io/badge/Stack-React%2019%20%7C%20Node.js%20%7C%20SQLite-yellow) +![Platforms](https://img.shields.io/badge/Platforms-YouTube%20%7C%20TikTok%20%7C%20Instagram-red) + +--- + +## 📚 목차 (Table of Contents) + +### 공통 +- [프로젝트 개요](#-프로젝트-개요) +- [핵심 기능](#-핵심-기능) +- [버전 히스토리](#-버전-히스토리) + +### Part A. 개발자 매뉴얼 (Developer Manual) +- [A1. 시스템 아키텍처](#a1-시스템-아키텍처) +- [A2. 기술 스택](#a2-기술-스택) +- [A3. 프로젝트 구조](#a3-프로젝트-구조) +- [A4. 데이터베이스 스키마](#a4-데이터베이스-스키마) +- [A5. API 명세](#a5-api-명세) +- [A6. 인증 시스템](#a6-인증-시스템) +- [A7. 영상 생성 파이프라인](#a7-영상-생성-파이프라인) +- [A8. 개발 환경 설정](#a8-개발-환경-설정) +- [A9. 코드 기여 가이드](#a9-코드-기여-가이드) + +### Part B. 운용자 매뉴얼 (Operator Manual) +- [B1. 시스템 요구사항](#b1-시스템-요구사항) +- [B2. 설치 및 배포](#b2-설치-및-배포) +- [B3. 환경 변수 설정](#b3-환경-변수-설정) +- [B4. 외부 서비스 연동](#b4-외부-서비스-연동) +- [B5. 관리자 대시보드](#b5-관리자-대시보드) +- [B6. 사용자 관리](#b6-사용자-관리) +- [B7. 플랜 및 크레딧 관리](#b7-플랜-및-크레딧-관리) +- [B8. 사진 관리 시스템](#b8-사진-관리-시스템) +- [B9. 시스템 모니터링](#b9-시스템-모니터링) +- [B10. 백업 및 복구](#b10-백업-및-복구) +- [B11. 트러블슈팅](#b11-트러블슈팅) + +### Part C. 사용자 매뉴얼 (User Manual) +- [C1. 시작하기](#c1-시작하기) +- [C2. 회원가입 및 로그인](#c2-회원가입-및-로그인) +- [C3. 펜션 등록하기](#c3-펜션-등록하기) +- [C4. 영상 만들기](#c4-영상-만들기) +- [C5. 영상 옵션 상세](#c5-영상-옵션-상세) +- [C6. YouTube 연동](#c6-youtube-연동) +- [C7. TikTok 연동](#c7-tiktok-연동) +- [C8. Instagram 연동](#c8-instagram-연동) +- [C9. 영상 관리](#c9-영상-관리) +- [C10. 에셋 관리](#c10-에셋-관리) +- [C11. 계정 설정](#c11-계정-설정) +- [C12. 구독 및 요금제](#c12-구독-및-요금제) +- [C13. 자주 묻는 질문 (FAQ)](#c13-자주-묻는-질문-faq) + +### Part D. 마케터 매뉴얼 (Marketer Manual) +- [D1. CaStAD 마케팅 전략](#d1-castad-마케팅-전략) +- [D2. 타겟 고객 분석](#d2-타겟-고객-분석) +- [D3. 콘텐츠 마케팅](#d3-콘텐츠-마케팅) +- [D4. SNS 마케팅 가이드](#d4-sns-마케팅-가이드) +- [D5. 성과 측정 및 KPI](#d5-성과-측정-및-kpi) +- [D6. 캠페인 사례](#d6-캠페인-사례) + +### Part E. 영업사원 매뉴얼 (Sales Manual) +- [E1. 제품 이해하기](#e1-제품-이해하기) +- [E2. 타겟 시장 및 고객](#e2-타겟-시장-및-고객) +- [E3. 세일즈 피치](#e3-세일즈-피치) +- [E4. 가격 정책 및 협상](#e4-가격-정책-및-협상) +- [E5. 경쟁사 비교](#e5-경쟁사-비교) +- [E6. 이의 처리](#e6-이의-처리) +- [E7. 클로징 전략](#e7-클로징-전략) + +--- + +## 🌟 프로젝트 개요 + +### 비전 +펜션 사업자가 전문 마케팅 지식 없이도 고품질 홍보 영상을 손쉽게 제작하고, YouTube, TikTok 등 멀티 플랫폼에 자동으로 배포할 수 있는 올인원 SaaS 플랫폼. + +### 주요 사용 시나리오 +``` +1. 사업자 회원가입 → 펜션 정보 등록 +2. 네이버/구글 지도 URL 입력 → 자동 정보 수집 +3. AI가 광고 카피, 음악, 영상 생성 +4. YouTube/TikTok 채널 연동 → 원클릭 업로드 +5. 다국어 SEO 최적화 → 해외 고객 유입 +6. 고급 통계 대시보드 → 성과 분석 +``` + +--- + +## ✨ 핵심 기능 + +### 🎵 AI 다국어 음악 생성 (Suno AI v5) + +6개 언어별 맞춤형 로고송/CM송을 자동 생성합니다. + +| 언어 | 지원 장르 | +|------|---------| +| **한국어 (KO)** | K-Pop, 트로트, 발라드, 힙합, R&B, EDM, Jazz, Rock | +| **영어 (EN)** | Pop, Country, Hip-Hop, R&B, EDM, Jazz, Rock, Ballad | +| **일본어 (JP)** | J-Pop, 엔카, 애니메이션, Rock, Jazz, Ballad, EDM | +| **중국어 (CN)** | C-Pop, Mandopop, 전통음악, 발라드, Hip-Hop, EDM | +| **태국어 (TH)** | T-Pop, Luk Thung, Pop, Hip-Hop, Rock, EDM, Jazz | +| **베트남어 (VN)** | V-Pop, 볼레로, Pop, Hip-Hop, Rock, EDM, Jazz | + +**오디오 모드 옵션:** +| 모드 | 설명 | +|------|------| +| **Song** | 가사 포함 노래 생성 | +| **Narration** | TTS 내레이션 | +| **Instrumental** | 가사 없는 악기 연주 | + +**음악 길이:** +- **Short**: 30초 이하 (Verse → Chorus → Outro) +- **Full**: 약 2분 (Verse 1-2 → Chorus → Outro) + +### 🤖 AI 콘텐츠 생성 (Gemini 2.5 Flash) + +| 기능 | 설명 | +|------|------| +| **다국어 광고 카피** | 6개 언어 마케팅 헤드라인 4개 자동 생성 | +| **리뷰 기반 마케팅** | 고객 리뷰 + 평점 → AI 마케팅 설명 작성 | +| **이미지 검수/필터링** | Gemini Vision으로 최고 품질 이미지만 선별 | +| **비디오 배경 생성** | Imagen Video로 동영상 배경 자동 생성 | +| **광고 포스터 생성** | Imagen 3으로 AI 포스터 이미지 생성 | +| **사업 정보 검색** | Google Maps Tool로 펜션 정보 자동 수집 | +| **🧬 비즈니스 DNA 분석** | Google Search Grounding으로 브랜드 DNA 자동 추출 | + +### 🧬 비즈니스 DNA 분석 (NEW!) + +펜션의 브랜드 아이덴티티를 AI가 자동으로 분석합니다. + +| 분석 항목 | 설명 | +|----------|------| +| **톤앤매너** | 브랜드 커뮤니케이션 스타일 (프리미엄, 감성적, 모던 등) | +| **타겟 고객** | 주요/부차 타겟층, 연령대, 라이프스타일 특성 | +| **브랜드 컬러** | 이미지 기반 컬러 팔레트 자동 추출 (5-6색) | +| **키워드** | SEO 최적화 핵심 키워드 + SNS 해시태그 | +| **시각적 스타일** | 인테리어/외관/분위기 분석 + 추천 사진 스타일 | +| **차별화 포인트** | 경쟁력 있는 고유 가치 제안 (USP) 3-5개 | + +**기술 스택:** +- Google Search Grounding: 실시간 웹 검색으로 최신 정보 수집 +- Gemini 2.5 Flash: 멀티모달 분석 (텍스트 + 이미지) +- DNACard 컴포넌트: 분석 결과 시각화 + +### 📸 스마트 이미지 관리 (NEW!) + +| 기능 | 설명 | +|------|------| +| **확장된 크롤링** | 네이버 플레이스에서 최대 30장 자동 수집 | +| **다중 소스** | 메뉴, 업체, 객실, 방문자 이미지 통합 수집 | +| **영구 저장** | 크롤링된 이미지를 펜션 프로필에 자동 저장 | +| **자동 로드** | 펜션 선택 시 저장된 이미지 즉시 불러오기 | +| **선택적 사용** | 전체/개별/랜덤 이미지 선택 모드 | + +### 🎙️ AI 음성 합성 (TTS) + +| 설정 | 옵션 | +|------|------| +| **성별** | 남성 (Male), 여성 (Female) | +| **연령대** | 청년 (Young), 중년 (Middle) | +| **톤** | Professional, Bright, Calm, Energetic | + +**음성 캐릭터:** +- 여성 + 밝은/활기찬 → **Zephyr** (밝고 활기찬 여성음) +- 여성 + 차분한 → **Kore** (침착한 여성음) +- 남성 + 전문적 → **Charon** (전문적인 남성음) +- 남성 + 활기찬 → **Fenrir** (에너지 있는 남성음) + +### 🎬 영상 생성 옵션 + +**화면 비율:** +| 비율 | 용도 | +|------|------| +| **16:9** | YouTube, 웹사이트 | +| **9:16** | TikTok, Instagram 릴스, YouTube Shorts | +| **1:1** | Instagram 피드, 정사각형 | + +**텍스트 효과 (11종 + Custom):** +| 효과 | 설명 | 용도 | +|------|------|------| +| **Cinematic** | Fade 효과, 영화식 | 고급스러운 느낌 | +| **Neon** | Glow 효과, 핑크 네온 | 현대적, 화려함 | +| **Glitch** | 디지털 노이즈 | 트렌디, 청년층 | +| **Typewriter** | 타자기 애니메이션 | 임팩트, 강조 | +| **Bold** | 굵은 대문자 이탤릭 | 강렬함 | +| **Motion** | 움직이는 효과 | 역동성 | +| **LineReveal** | 상하 라인 최소주의 | 세련됨 | +| **Boxed** | 박스 테두리 | 정리된 느낌 | +| **Elegant** | 세리프 이탤릭 | 우아함 | +| **BlockReveal** | 검은 배경 흰 텍스트 | 팝아트, 강조 | +| **Custom** | AI 분석 기반 | 사용자 정의 CSS | + +**화면 전환 효과 (4종):** +| 효과 | 설명 | +|------|------| +| **Mix** | 부드러운 혼합/페이드 | +| **Zoom** | 확대/축소 전환 | +| **Slide** | 밀어내기 전환 | +| **Wipe** | 닦아내기 전환 | + +### 🏠 펜션 카테고리 시스템 (9종) + +| 카테고리 | 설명 | +|----------|------| +| **풀빌라** | 프라이빗 수영장 보유 | +| **오션뷰** | 바다 전망 | +| **산장/계곡** | 자연 속 힐링 | +| **독채** | 프라이빗 공간 | +| **커플펜션** | 로맨틱 분위기 | +| **가족펜션** | 단체 숙박 최적화 | +| **애견동반** | 반려동물 환영 | +| **글램핑** | 캠핑 + 럭셔리 | +| **한옥펜션** | 전통미 | + +> 복수 선택 가능 - AI가 카테고리에 맞는 마케팅 카피와 음악 스타일을 자동 추천 + +### 📺 멀티 플랫폼 연동 + +| 플랫폼 | 인증 | 기능 | +|--------|------|------| +| **YouTube** | OAuth 2.0 | 직접 업로드, 플레이리스트 관리, SEO 자동 생성 | +| **TikTok** | OAuth 2.0 | Direct Post, Inbox 업로드, 프라이버시 설정 | +| **Instagram** | ID/PW | 릴스 업로드, 주간 제한, 2FA 지원 | + +### 💳 크레딧 시스템 + +| 플랜 | 가격 (월) | 월 크레딧 | 펜션 수 | 스토리지 | +|------|----------|---------|---------|----------| +| **Free** | 무료 | 10 | 1개 | 500MB | +| **Basic** | ₩29,000 | 15 | 1개 | 2GB | +| **Pro** | ₩89,000 | 75 | 5개 | 10GB | +| **Business** | ₩249,000 | 무제한 | 무제한 | 50GB | + +### 📊 고급 통계 (v3.0) + +| 기능 | 설명 | +|------|------| +| **대시보드 요약** | 전체 사용자, 영상 생성, 플랫폼 업로드 현황 | +| **사용자 성장 추이** | 일별/주별/월별 신규 가입자 트렌드 | +| **영상 생성 트렌드** | 시간대별 영상 생성 패턴 분석 | +| **플랫폼 업로드 통계** | YouTube/TikTok/Instagram 업로드 비교 | +| **크레딧 사용 분석** | 크레딧 소비 패턴 및 충전 요청 현황 | +| **플랜 분포** | 요금제별 사용자 분포 | +| **Top 사용자** | 활동량 기준 상위 사용자 | +| **지역별 분포** | 펜션 지역별 분포 현황 | +| **수익 예측** | 구독 기반 예상 수익 분석 | +| **시스템 헬스** | 서버 상태, 리소스 사용량 모니터링 | +| **일일 스냅샷** | 매일 자동 통계 기록 | +| **펜션별 분석** | YouTube 조회수, 시청 시간 분석 | + +### 📁 에셋 관리 + +| 에셋 타입 | 소스 | +|----------|------| +| **이미지** | 업로드, 웹 크롤링, AI 생성 | +| **오디오** | Suno AI 생성, TTS | +| **비디오** | 렌더링 결과물 | + +- 플랜별 스토리지 제한 +- 자동 썸네일 생성 +- 메타데이터 관리 (크기, 해상도, 길이) + +### 🎪 축제 연동 시스템 (v3.2.0 신규) + +펜션 근처 축제 정보를 자동으로 가져와 마케팅 콘텐츠에 반영합니다. + +| 기능 | 설명 | +|------|------| +| **데이터 소스** | 한국관광공사 Tour API | +| **자동 동기화** | 관리자가 수동 또는 스케줄 동기화 | +| **지역 기반 추천** | 펜션 주소 기반 가까운 축제 자동 표시 | +| **행정구역 그룹핑** | 시/도 단위로 축제 목록 그룹화 | +| **콘텐츠 연동** | 선택한 축제가 가사/광고 카피에 자동 반영 | + +**축제 데이터 구조:** +```json +{ + "id": 12345, + "title": "강릉 커피 축제", + "sido": "강원", + "sigungu": "강릉시", + "addr1": "강원특별자치도 강릉시 ...", + "event_start_date": "20241201", + "event_end_date": "20241215", + "first_image": "https://..." +} +``` + +**API 엔드포인트:** +| 엔드포인트 | 설명 | +|-----------|------| +| `GET /api/festivals` | 축제 목록 조회 (sido, sigungu 필터) | +| `GET /api/festivals/grouped` | 행정구역별 그룹 조회 | +| `GET /api/festivals/:id` | 축제 상세 정보 | +| `POST /api/admin/sync/festivals` | 축제 데이터 동기화 (관리자) | + +### 💰 Google Cloud Billing 연동 (v3.2.0 신규) + +BigQuery를 통해 실제 Google Cloud 비용을 실시간으로 추적합니다. + +**설정 단계:** +1. Google Cloud Console에서 Billing Export to BigQuery 활성화 +2. 서비스 계정 생성 및 BigQuery 권한 부여 +3. `google-billing-key.json` 파일을 `server/` 폴더에 배치 +4. 환경 변수 설정 + +**환경 변수:** +```env +GOOGLE_BILLING_KEY_PATH=./server/google-billing-key.json +GOOGLE_CLOUD_PROJECT_ID=your-project-id +GOOGLE_BILLING_DATASET_ID=billing_export +``` + +**대시보드 기능:** +| 기능 | 설명 | +|------|------| +| **서비스별 비용** | Vertex AI, Cloud Run 등 서비스별 집계 | +| **일별 트렌드** | 최근 30일 비용 추이 그래프 | +| **SKU 상세** | Gemini API 모델별 사용량 및 비용 | +| **월별 요약** | 월간 비용 요약 및 서비스 분포 | +| **환율 적용** | USD → KRW 자동 환산 (1,350원 기준) | + +**API 엔드포인트:** +| 엔드포인트 | 설명 | +|-----------|------| +| `GET /api/admin/billing/dashboard` | 빌링 대시보드 데이터 | +| `GET /api/admin/billing/status` | 빌링 데이터 사용 가능 여부 | + +### 📊 API 사용량 추적 (v3.2.0 신규) + +모든 외부 API 호출을 추적하여 비용 분석 및 과금 대비 데이터를 제공합니다. + +**추적 대상:** +| 서비스 | 추적 항목 | +|--------|----------| +| **Gemini** | 모델명, 입/출력 토큰, 이미지 수, 지연시간 | +| **Suno** | 오디오 길이, 장르, 생성 시간 | +| **YouTube** | 업로드 영상 수, 용량 | +| **TikTok** | 업로드 수, 상태 | + +**데이터베이스 스키마:** +```sql +CREATE TABLE api_usage_logs ( + id INTEGER PRIMARY KEY, + service TEXT NOT NULL, -- 'gemini', 'suno', 'youtube' + model TEXT, -- 'gemini-2.0-flash', etc. + endpoint TEXT, -- API 엔드포인트 + user_id INTEGER, -- 사용자 ID (과금용) + tokens_input INTEGER, -- 입력 토큰 + tokens_output INTEGER, -- 출력 토큰 + image_count INTEGER, -- 이미지 수 + audio_seconds REAL, -- 오디오 길이 + status TEXT, -- 'success', 'error' + error_message TEXT, + latency_ms INTEGER, -- 응답 시간 + cost_estimate REAL, -- 예상 비용 (USD) + createdAt DATETIME +); +``` + +**관리자 대시보드:** +- 총 API 호출 수 / 예상 비용 +- 서비스별/모델별 사용량 분포 +- 사용자별 사용량 랭킹 (과금 대비) +- 최근 오류 로그 +- 월별 리포트 자동 생성 + +### 🎯 AI 모델 우선순위 시스템 (v3.2.0 신규) + +API 오류 시 자동으로 대체 모델로 전환하는 지능적 Fallback 시스템입니다. + +**이미지 생성 모델 우선순위:** +| 순위 | 모델 | 예상 비용 | 최대 재시도 | +|------|------|----------|------------| +| 1 | gemini-2.0-flash-preview-image-generation | $0.02/이미지 | 2회 | +| 2 | gemini-2.5-flash-image | $0.015/이미지 | 2회 | +| 3 | imagen-3.0-generate-002 | $0.03/이미지 | 1회 | + +**Fallback 트리거:** +- HTTP 429 (Rate Limit Exceeded) +- HTTP 500 (Internal Server Error) +- 타임아웃 (30초 초과) + +**로깅:** +모든 모델 전환 시도는 `api_usage_logs` 테이블에 기록되어 추후 분석에 활용됩니다. + +### ✨ AI 매직 라이트 (v3.2.0 신규) + +펜션 정보를 기반으로 매력적인 마케팅 컨셉 설명을 자동 생성합니다. + +**입력 데이터:** +- 펜션 이름 +- 주소/지역 +- 펜션 카테고리 (풀빌라, 오션뷰 등) +- 선택된 축제 정보 +- 기존 설명 (참고용) + +**출력:** +2-3문장의 감성적이고 매력적인 마케팅 컨셉 설명 + +**사용 방법:** +1. 펜션 기본 정보 입력 +2. 카테고리 선택 +3. 근처 축제 선택 (선택사항) +4. "AI 매직 라이트" 버튼 클릭 +5. 자동 생성된 컨셉 확인/수정 + +**API:** +``` +POST /api/ai/auto-description +Body: { name, address, pensionCategories, selectedFestivals, existingDescription } +Response: { description, success } +``` + +### 🔐 개별 YouTube 연동 (v3.2.0 개선) + +각 사용자가 자신의 YouTube 채널을 연결하여 직접 업로드할 수 있습니다. + +**OAuth 2.0 플로우:** +``` +1. 사용자: "YouTube 연결" 클릭 +2. → Google OAuth 페이지로 리다이렉트 +3. 사용자: Google 계정 로그인 + 권한 승인 +4. → 콜백 URL로 인증 코드 반환 +5. 서버: 인증 코드 → 액세스 토큰 교환 +6. 서버: 토큰을 사용자 DB에 저장 +7. 이후 업로드 시 저장된 토큰 사용 +``` + +**설정 요구사항 (개발자):** +1. Google Cloud Console에서 OAuth 2.0 클라이언트 생성 +2. YouTube Data API v3 활성화 +3. 승인된 리디렉션 URI 등록: + - `http://localhost:3001/api/youtube/oauth/callback` (개발) + - `https://castad1.ktenterprise.net/api/youtube/oauth/callback` (프로덕션) + +**사용자 경험:** +- 설정 페이지에서 "YouTube 연결" 버튼 클릭 +- Google 계정으로 로그인 및 권한 승인 +- 연결 완료 후 자신의 채널에 직접 업로드 가능 + +**API 엔드포인트:** +| 엔드포인트 | 설명 | +|-----------|------| +| `GET /api/youtube/oauth/url` | OAuth 인증 URL 생성 | +| `GET /api/youtube/oauth/callback` | OAuth 콜백 처리 | +| `GET /api/youtube/connection` | 연결 상태 확인 | +| `POST /api/youtube/my-upload` | 사용자 채널에 업로드 | + +--- + +# Part A. 개발자 매뉴얼 (Developer Manual) + +> 이 섹션은 CaStAD의 코드를 이해하고 수정하거나 기여하려는 **개발자**를 위한 기술 문서입니다. + +--- + +## A1. 시스템 아키텍처 + +### 시스템 아키텍처 다이어그램 + +> **SVG 다이어그램 보기:** [public/images/architecture-diagram.svg](public/images/architecture-diagram.svg) + +

+ CaStAD System Architecture +

+ +### 계층별 구조 + +#### 1. 클라이언트 계층 (Frontend) + +| 컴포넌트 | 설명 | +|---------|------| +| **Landing Page** | 마케팅 홈페이지, 기능 소개 | +| **CastAD App** | 메인 SPA 애플리케이션 | +| **Admin Dashboard** | 관리자 전용 대시보드 | +| **React Router v7** | SPA 라우팅 관리 | +| **Context API** | Auth, Language, Theme, UserLevel 전역 상태 | + +#### 2. 서버 계층 (Backend) + +| 모듈 | 역할 | +|------|------| +| **Auth Routes** | 인증/인가 (JWT, OAuth 2.0) | +| **Pension Routes** | 펜션 CRUD, 이미지 관리 | +| **YouTube Routes** | OAuth, 업로드, 플레이리스트 | +| **TikTok Routes** | OAuth, Direct Post, Inbox 업로드 | +| **Instagram Routes** | 계정 연결, Reels 업로드 | +| **Gemini Routes** | AI 콘텐츠 생성 프록시 | +| **Crawling Routes** | 네이버/구글/인스타그램 크롤링 | +| **Render Engine** | Puppeteer + FFmpeg 영상 생성 | + +#### 3. 데이터 계층 + +| 컴포넌트 | 용도 | +|---------|------| +| **SQLite** | 메타데이터, 사용자, 설정 저장 | +| **Puppeteer** | 헤드리스 브라우저 렌더링 | +| **FFmpeg** | 오디오/비디오 병합 | +| **FileSystem** | 생성된 MP4, 이미지 저장 | + +#### 4. 외부 API + +| API | 용도 | +|-----|------| +| **Google Gemini** | 광고 카피, TTS, 이미지 생성 | +| **Suno AI** | AI 음악 생성 | +| **YouTube Data API** | 영상 업로드, 플레이리스트 | +| **TikTok API** | TikTok 영상 게시 | +| **Instagram API** | Reels 업로드 (Python 마이크로서비스) | +| **Tour API** | 한국관광공사 축제 데이터 | +| **Kakao Maps API** | 지오코딩 | + +### 영상 생성 파이프라인 + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 1. INPUT │ +│ 펜션 정보 입력 → 네이버/구글/인스타 크롤링 → 이미지 15장 선택 │ +└─────────────────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 2. AI GENERATION │ +│ 광고 카피 (Gemini) │ 음악 (Suno AI) │ 포스터 (Imagen 3) │ TTS (Gemini) │ +└─────────────────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 3. RENDERING │ +│ HTML/CSS 애니메이션 → Puppeteer 캡처 → FFmpeg 오디오 병합 │ +└─────────────────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 4. POST-PROCESSING │ +│ 해상도 최적화 │ 메타데이터 추출 │ 썸네일 생성 │ +└─────────────────────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 5. UPLOAD │ +│ YouTube (OAuth) │ TikTok (OAuth) │ Instagram (Reels) │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### 데이터 흐름 다이어그램 + +```mermaid +flowchart TD + A[📝 사용자 입력] --> B[🗺️ 네이버/구글/인스타 크롤링] + B --> C[🤖 AI 콘텐츠 생성] + + C --> C1[광고 카피 - Gemini 2.5 Flash] + C --> C2[음악 생성 - Suno AI v5] + C --> C3[포스터 생성 - Imagen 3] + C --> C4[음성 합성 - Gemini TTS] + + C1 & C2 & C3 & C4 --> D[🎬 영상 렌더링] + + D --> D1[HTML/CSS 애니메이션] + D --> D2[Puppeteer 화면 캡처] + D --> D3[FFmpeg 오디오 병합] + + D1 & D2 & D3 --> E[💾 결과물 저장] + E --> E1[(SQLite - 메타데이터)] + E --> E2[📁 FileSystem - MP4] + + E --> F[📺 멀티 플랫폼 업로드] + F -->|OAuth 2.0| F1[YouTube] + F -->|OAuth 2.0| F2[TikTok] + F -->|API| F3[Instagram Reels] +``` + +--- + +## A2. 기술 스택 + +### Frontend +| 카테고리 | 기술 | 버전 | 용도 | +|----------|------|------|------| +| **Core** | React | 19.x | UI 프레임워크 | +| **Language** | TypeScript | 5.x | 타입 안전성 | +| **Build** | Vite | 6.x | 빌드 도구 | +| **Routing** | React Router | 7.x | SPA 라우팅 | +| **Styling** | Tailwind CSS | 3.x | 유틸리티 CSS | +| **UI Components** | shadcn/ui | latest | Radix 기반 컴포넌트 | +| **Icons** | Lucide React | latest | 아이콘 라이브러리 | +| **State** | Context API | - | 전역 상태 관리 | + +### Backend +| 카테고리 | 기술 | 버전 | 용도 | +|----------|------|------|------| +| **Runtime** | Node.js | 22.x | 서버 런타임 | +| **Framework** | Express.js | 4.x | HTTP 서버 | +| **Database** | SQLite3 | 5.x | 경량 RDBMS | +| **Auth** | JWT | - | 토큰 기반 인증 | +| **Crypto** | Bcrypt | 5.x | 비밀번호 해싱 | +| **Rendering** | Puppeteer | 23.x | 헤드리스 브라우저 | +| **Media** | FFmpeg | 7.x | 영상/음성 처리 | +| **Upload** | Multer | 1.x | 파일 업로드 | +| **OAuth** | Google APIs | - | YouTube 연동 | + +### External Services +| 서비스 | 용도 | API | +|--------|------|-----| +| **Google Gemini** | 텍스트/이미지 생성, TTS | REST API | +| **Suno AI** | 음악 생성 | Proxy API | +| **YouTube** | 영상 업로드, 채널 관리 | Data API v3 | +| **TikTok** | 영상 업로드, 계정 연동 | Content Posting API | +| **Instagram** | 영상 업로드 | Basic Display API | +| **Naver Maps** | 업체 정보 크롤링 | Web Scraping | +| **Google Maps** | 업체 정보 크롤링 | Web Scraping | + +--- + +## A3. 프로젝트 구조 + +### 디렉토리 구조 + +``` +19-claude-festival-castad/ +├── 📁 components/ # 공통 React 컴포넌트 +│ ├── InputForm.tsx # 영상 생성 입력 폼 +│ ├── LoadingOverlay.tsx # 로딩 화면 +│ ├── Navbar.tsx # 상단 네비게이션 +│ ├── ResultPlayer.tsx # 결과 영상 플레이어 +│ ├── DNACard.tsx # 펜션 DNA 분석 카드 +│ ├── KoreaMap.tsx # 한국 지도 시각화 +│ ├── OnboardingDialog.tsx # 사용자 온보딩 +│ ├── PensionOnboardingDialog.tsx # 펜션 등록 다이얼로그 +│ ├── YouTubeSEOPreview.tsx # YouTube SEO 프리뷰 +│ └── ui/ # shadcn/ui 컴포넌트 (30+) +│ +├── 📁 src/ +│ ├── 📁 components/ # 앱 전용 컴포넌트 +│ │ ├── layout/ # 레이아웃 컴포넌트 +│ │ │ ├── Sidebar.tsx # 사이드 네비게이션 +│ │ │ ├── TopHeader.tsx# 상단 헤더 +│ │ │ └── SidebarItem.tsx # 사이드바 메뉴 항목 +│ │ └── wizard/ # 마법사 컴포넌트 +│ │ └── StepIndicator.tsx # 단계 표시기 +│ │ +│ ├── 📁 contexts/ # React Context (전역 상태) +│ │ ├── AuthContext.tsx # JWT 인증 상태 +│ │ ├── LanguageContext.tsx # 다국어 (6개국) +│ │ ├── ThemeContext.tsx # 다크/라이트 모드 + 컬러 팔레트 +│ │ └── UserLevelContext.tsx # 사용자 레벨 (Beginner/Pro) +│ │ +│ ├── 📁 pages/ # 페이지 컴포넌트 (12개) +│ │ ├── GeneratorPage.tsx # 레거시 영상 생성 페이지 +│ │ ├── Dashboard.tsx # 레거시 대시보드 +│ │ ├── CastADApp.tsx # 메인 SPA 레이아웃 +│ │ ├── AdminDashboard.tsx # 관리자 대시보드 (6000+ lines) +│ │ ├── LandingPage.tsx # 마케팅 랜딩 +│ │ ├── BrandPage.tsx # 브랜드 소개 +│ │ ├── LoginPage.tsx # 로그인 +│ │ ├── RegisterPage.tsx # 회원가입 +│ │ ├── CreditsPage.tsx # 크레딧 관리 +│ │ ├── OAuthCallbackPage.tsx # OAuth 콜백 +│ │ ├── ForgotPasswordPage.tsx # 비밀번호 찾기 +│ │ └── VerifyEmailPage.tsx # 이메일 인증 +│ │ +│ ├── 📁 views/ # 앱 내 뷰 컴포넌트 (8개) +│ │ ├── DashboardView.tsx # 대시보드 (19.8KB) +│ │ ├── NewProjectView.tsx # 새 영상 생성 마법사 (112KB, 가장 복잡) +│ │ ├── LibraryView.tsx # 영상 라이브러리 (14.5KB) +│ │ ├── PensionsView.tsx # 펜션 프로필 관리 (51.5KB) +│ │ ├── FestivalsView.tsx # 축제 정보 (26.4KB) +│ │ ├── AssetsView.tsx # 에셋 관리 (24.8KB) +│ │ ├── SettingsView.tsx # 설정 (57.9KB) +│ │ └── AccountView.tsx # 계정 정보 (16.2KB) +│ │ +│ ├── 📁 styles/ # 스타일시트 +│ │ └── globals.css # 전역 CSS +│ │ +│ └── locales.ts # 다국어 번역 (KO/EN/JA/ZH/TH/VI) +│ +├── 📁 services/ # 프론트엔드 API 서비스 (7개) +│ ├── geminiService.ts # Gemini AI API (텍스트/이미지/TTS) +│ ├── sunoService.ts # Suno AI 음악 생성 +│ ├── naverService.ts # 네이버 플레이스 크롤링 +│ ├── googlePlacesService.ts # Google Places 크롤링 +│ ├── instagramService.ts # Instagram 프로필 크롤링 (v3.6.0) +│ ├── ffmpegService.ts # FFmpeg WASM (클라이언트) +│ └── audioUtils.ts # 오디오 유틸리티 +│ +├── 📁 server/ # 백엔드 서버 +│ ├── index.js # Express 메인 (7200+ lines, 150+ API) +│ ├── db.js # SQLite 스키마 (850+ lines, 30+ 테이블) +│ ├── geminiBackendService.js # Gemini 백엔드 프록시 +│ ├── youtubeService.js # YouTube Data API v3 +│ ├── tiktokService.js # TikTok API 서비스 +│ ├── statisticsService.js # 고급 통계 분석 +│ ├── 📁 instagram-service/ # Instagram 마이크로서비스 (Python) +│ │ ├── instagram_service.py +│ │ └── requirements.txt +│ ├── 📁 downloads/ # 생성된 영상 저장 +│ ├── 📁 temp/ # 임시 렌더링 파일 +│ └── 📁 user_assets/ # 사용자 업로드 파일 +│ +├── 📁 public/ # 정적 파일 +│ └── images/ +│ ├── castad-logo.svg +│ └── architecture-diagram.svg +│ +├── App.tsx # React 앱 루트 +├── types.ts # TypeScript 타입 정의 +├── vite.config.ts # Vite 설정 (프록시 포함) +├── tailwind.config.js # Tailwind 설정 +├── package.json # 프론트엔드 의존성 +├── start.sh # 통합 실행 스크립트 +├── test_api.sh # API 테스트 스크립트 +├── CLAUDE.md # Claude Code 가이드 +└── .env # 환경 변수 (Git 제외) +``` + +### Context API 상세 + +| Context | Hook | 제공 기능 | +|---------|------|----------| +| **AuthContext** | `useAuth()` | user, token, login, logout, isAdmin | +| **LanguageContext** | `useLanguage()` | language, setLanguage, t(key) | +| **ThemeContext** | `useTheme()` | theme, palette, setTheme, toggleTheme | +| **UserLevelContext** | `useUserLevel()` | level, setLevel, features, isLoading | + +### 프론트엔드 서비스 레이어 + +| 서비스 | 주요 함수 | 용도 | +|--------|----------|------| +| **geminiService** | generateCreativeContent(), synthesizeSpeech(), generateAdPoster(), filterImages() | Gemini API 호출 | +| **sunoService** | generateMusic(), cleanLyricsForSuno() | AI 음악 생성 | +| **naverService** | crawlNaverPlace(), parseNaverPlaceUrl() | 네이버 지도 크롤링 | +| **googlePlacesService** | searchPlace(), getPlaceDetails(), getPlacePhoto() | Google Places 크롤링 | +| **instagramService** | crawlInstagramProfile(), parseInstagramUrl() | 인스타그램 크롤링 | +| **ffmpegService** | mergeVideoWithAudio(), initFFmpeg() | 클라이언트 비디오 처리 | +| **audioUtils** | decodeBase64Audio(), arrayBufferToWav() | 오디오 유틸리티 | + +### 라우팅 구조 + +``` +/ (root) +├── /login → LoginPage +├── /register → RegisterPage +├── /forgot-password → ForgotPasswordPage +├── /verify-email → VerifyEmailPage +├── /reset-password → ResetPasswordPage +├── /oauth/callback → OAuthCallbackPage +├── /landing → LandingPage +├── /brand → BrandPage +├── /credits → CreditsPage (인증 필요) +├── /admin → AdminDashboard (관리자 전용) +└── /app → CastADApp (인증 필요) + ├── ?view=dashboard → DashboardView + ├── ?view=new-project → NewProjectView + ├── ?view=library → LibraryView + ├── ?view=pensions → PensionsView + ├── ?view=festivals → FestivalsView + ├── ?view=assets → AssetsView + ├── ?view=settings → SettingsView + └── ?view=account → AccountView +``` + +--- + +## A4. 데이터베이스 스키마 + +> SQLite 데이터베이스 파일: `server/database.sqlite` + +### 테이블 카테고리별 구성 (총 30+ 테이블) + +#### 1. 사용자 및 인증 (User & Auth) + +| 테이블 | 설명 | 주요 컬럼 | +|--------|------|----------| +| `users` | 사용자 계정, 플랜, 크레딧 | id, username, email, password, role, plan_type, credits, max_pensions, experience_level | +| `credit_requests` | 크레딧 충전 요청 | user_id, requested_credits, status, reason, processed_by | +| `credit_history` | 크레딧 변동 이력 | user_id, amount, type, description, balance_after | +| `activity_logs` | 시스템 활동 로그 | user_id, action_type, ip_address, metadata | + +#### 2. 펜션 관리 (Pension Management) + +| 테이블 | 설명 | 주요 컬럼 | +|--------|------|----------| +| `pension_profiles` | 펜션/숙소 프로필 (다중 지원) | user_id, brand_name, region, address, youtube_playlist_id, mapx, mapy | +| `pension_images` | 펜션 이미지 보관함 | pension_id, filename, source(`naver`/`google`/`instagram`/`upload`), is_priority | +| `daily_auto_generation` | 일일 자동 생성 설정 | pension_id, enabled, generation_time, image_mode, auto_upload_youtube/instagram/tiktok | +| `auto_generation_logs` | 자동 생성 로그 | pension_id, status, youtube_video_id, instagram_media_id, tiktok_video_id | + +#### 3. 영상 및 에셋 (Video & Assets) + +| 테이블 | 설명 | 주요 컬럼 | +|--------|------|----------| +| `history` | 생성된 영상 히스토리 | user_id, pension_id, business_name, details(JSON), final_video_path, poster_path | +| `user_assets` | 사용자 에셋 (이미지/오디오/비디오) | user_id, asset_type, source_type, file_path, file_size, duration | +| `generation_queue` | 자동 생성 작업 큐 | user_id, pension_id, status, scheduled_at, result_video_path | + +#### 4. YouTube 연동 + +| 테이블 | 설명 | 주요 컬럼 | +|--------|------|----------| +| `youtube_connections` | YouTube OAuth 토큰 | user_id, google_email, youtube_channel_id, access_token, refresh_token | +| `youtube_settings` | 업로드 기본 설정 | user_id, default_privacy, default_category_id, auto_upload | +| `youtube_playlists` | 플레이리스트 캐시 | user_id, pension_id, playlist_id, title, item_count | +| `upload_history` | YouTube 업로드 기록 | user_id, pension_id, youtube_video_id, youtube_url, status | +| `youtube_analytics` | YouTube 분석 데이터 캐시 | pension_id, playlist_id, date, views, likes, subscribers_gained | +| `pension_monthly_stats` | 월간 통계 요약 | pension_id, year_month, total_views, total_videos, growth_rate | + +#### 5. Instagram 연동 + +| 테이블 | 설명 | 주요 컬럼 | +|--------|------|----------| +| `instagram_connections` | Instagram 계정 연결 | user_id, instagram_username, encrypted_password, encrypted_session | +| `instagram_settings` | Instagram 업로드 설정 | user_id, auto_upload, upload_as_reel, default_hashtags | +| `instagram_upload_history` | Instagram 업로드 기록 | user_id, pension_id, instagram_media_id, permalink, upload_type | + +#### 6. TikTok 연동 + +| 테이블 | 설명 | 주요 컬럼 | +|--------|------|----------| +| `tiktok_connections` | TikTok OAuth 연결 | user_id, open_id, display_name, access_token, refresh_token | +| `tiktok_settings` | TikTok 업로드 설정 | user_id, default_privacy, disable_duet, auto_upload | +| `tiktok_upload_history` | TikTok 업로드 기록 | user_id, pension_id, video_id, privacy_level, status | + +#### 7. 외부 데이터 (TourAPI 연동) + +| 테이블 | 설명 | 주요 컬럼 | +|--------|------|----------| +| `festivals` | 전국 축제 정보 | content_id, title, addr1, event_start_date, event_end_date, mapx, mapy | +| `public_pensions` | 전국 펜션 마스터 데이터 | name, address, sido, sigungu, mapx, mapy, facilities, pet_allowed | +| `pension_festival_matches` | 펜션-축제 매칭 | pension_id, festival_id, distance_km, match_score | +| `area_codes` | 지역 코드 (17개 시도) | code, name, name_short, name_en | + +#### 8. 시스템 관리 + +| 테이블 | 설명 | 주요 컬럼 | +|--------|------|----------| +| `system_settings` | 시스템 설정 (쿠키 등) | setting_key, setting_value, is_encrypted, updated_by | +| `system_stats_daily` | 일별 시스템 통계 | date, total_users, new_users, total_videos_generated | +| `api_usage_logs` | API 사용량 로그 | service, model, user_id, tokens_input, tokens_output, cost_estimate | +| `api_usage_daily` | API 일별 집계 | date, service, model, total_calls, total_cost_estimate | +| `platform_stats` | 플랫폼 통합 통계 | user_id, platform, date, views, likes, engagement_rate | + +### ERD (Entity Relationship Diagram) + +```mermaid +erDiagram + USERS ||--o{ PENSION_PROFILES : "owns" + USERS ||--o| YOUTUBE_CONNECTIONS : "has" + USERS ||--o| INSTAGRAM_CONNECTIONS : "has" + USERS ||--o| TIKTOK_CONNECTIONS : "has" + USERS ||--o{ HISTORY : "creates" + USERS ||--o{ USER_ASSETS : "owns" + USERS ||--o{ CREDIT_HISTORY : "has" + USERS ||--o{ API_USAGE_LOGS : "generates" + + PENSION_PROFILES ||--o{ PENSION_IMAGES : "contains" + PENSION_PROFILES ||--o{ DAILY_AUTO_GENERATION : "has" + PENSION_PROFILES ||--o{ YOUTUBE_PLAYLISTS : "has" + PENSION_PROFILES ||--o{ AUTO_GENERATION_LOGS : "logs" + PENSION_PROFILES ||--o{ PENSION_FESTIVAL_MATCHES : "matches" + + HISTORY ||--o{ UPLOAD_HISTORY : "youtube" + HISTORY ||--o{ INSTAGRAM_UPLOAD_HISTORY : "instagram" + HISTORY ||--o{ TIKTOK_UPLOAD_HISTORY : "tiktok" + + FESTIVALS ||--o{ PENSION_FESTIVAL_MATCHES : "matched_to" + + USERS { + int id PK + string username UK + string email UK + string password + string role + string plan_type + int credits + int max_pensions + string experience_level + } + + PENSION_PROFILES { + int id PK + int user_id FK + string brand_name + string region + string address + real mapx + real mapy + } + + PENSION_IMAGES { + int id PK + int pension_id FK + string filename + string source + int is_priority + } + + HISTORY { + int id PK + int user_id FK + int pension_id FK + string business_name + string final_video_path + } + + YOUTUBE_CONNECTIONS { + int id PK + int user_id FK UK + string youtube_channel_id + string access_token + string refresh_token + } + + INSTAGRAM_CONNECTIONS { + int id PK + int user_id FK UK + string instagram_username + string encrypted_session + } + + TIKTOK_CONNECTIONS { + int id PK + int user_id FK UK + string open_id + string access_token + } + + FESTIVALS { + int id PK + string content_id UK + string title + string event_start_date + string event_end_date + } +``` + +### 인덱스 목록 + +```sql +-- 축제 검색 최적화 +CREATE INDEX idx_festivals_area ON festivals(area_code, sigungu_code); +CREATE INDEX idx_festivals_date ON festivals(event_start_date, event_end_date); +CREATE INDEX idx_festivals_coords ON festivals(mapx, mapy); + +-- 펜션 검색 최적화 +CREATE INDEX idx_public_pensions_sido ON public_pensions(sido); +CREATE INDEX idx_public_pensions_sigungu ON public_pensions(sido, sigungu); +CREATE INDEX idx_public_pensions_coords ON public_pensions(mapx, mapy); + +-- API 사용량 조회 최적화 +CREATE INDEX idx_api_usage_service ON api_usage_logs(service, createdAt); +CREATE INDEX idx_api_usage_user ON api_usage_logs(user_id, createdAt); +CREATE INDEX idx_api_usage_date ON api_usage_logs(createdAt); + +-- 펜션-축제 매칭 최적화 +CREATE INDEX idx_matches_pension ON pension_festival_matches(pension_id, pension_type); +CREATE INDEX idx_matches_festival ON pension_festival_matches(festival_id); +``` + +--- + +## A5. API 명세 + +> **총 150+ API 엔드포인트** +> Auth: ✅ = JWT 필요, 🔐 = Admin 필요, - = 공개 + +--- + +### 1. 인증 API (`/api/auth/*`) + +| Method | Endpoint | 설명 | Auth | Body/Query | +|--------|----------|------|:----:|------------| +| POST | `/api/auth/register` | 회원가입 | - | `{username, email, password, name, phone}` | +| POST | `/api/auth/login` | JWT 로그인 | - | `{username, password}` | +| GET | `/api/auth/me` | 현재 사용자 정보 | ✅ | - | +| PUT | `/api/auth/profile` | 프로필 수정 | ✅ | `{name, phone}` | +| PUT | `/api/auth/change-password` | 비밀번호 변경 | ✅ | `{currentPassword, newPassword}` | +| GET | `/api/auth/verify-email` | 이메일 인증 | - | `?token=xxx` | +| POST | `/api/auth/resend-verification` | 인증메일 재발송 | - | `{email}` | +| POST | `/api/auth/forgot-password` | 비밀번호 재설정 요청 | - | `{email}` | +| POST | `/api/auth/reset-password` | 비밀번호 재설정 | - | `{token, newPassword}` | +| GET | `/api/auth/google` | Google OAuth 시작 | - | - | +| GET | `/api/auth/google/callback` | Google OAuth 콜백 | - | `?code=xxx` | +| GET | `/api/auth/naver` | Naver OAuth 시작 | - | - | +| GET | `/api/auth/naver/callback` | Naver OAuth 콜백 | - | `?code=xxx` | + +--- + +### 2. 사용자 설정 API (`/api/user/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/user/plan` | 현재 플랜 정보 | ✅ | +| GET | `/api/user/level` | 사용자 레벨 조회 | ✅ | +| PUT | `/api/user/level` | 사용자 레벨 변경 | ✅ | +| GET | `/api/user/auto-generation` | 자동 생성 설정 조회 | ✅ | +| PUT | `/api/user/auto-generation` | 자동 생성 설정 수정 | ✅ | +| GET | `/api/user/auto-upload-settings` | 자동 업로드 설정 조회 | ✅ | +| PUT | `/api/user/auto-upload-settings` | 자동 업로드 설정 수정 | ✅ | + +--- + +### 3. 펜션 프로필 API (`/api/profile/pension/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/profile/pensions` | 내 펜션 목록 | ✅ | +| GET | `/api/profile/pension` | 기본 펜션 조회 | ✅ | +| GET | `/api/profile/pension/:id` | 펜션 상세 조회 | ✅ | +| POST | `/api/profile/pension` | 펜션 생성 | ✅ | +| PUT | `/api/profile/pension/:id` | 펜션 수정 | ✅ | +| DELETE | `/api/profile/pension/:id` | 펜션 삭제 | ✅ | +| POST | `/api/profile/pension/:id/default` | 기본 펜션 설정 | ✅ | +| GET | `/api/profile/pension/:id/analytics` | 펜션 분석 데이터 | ✅ | +| GET | `/api/profile/pensions/analytics-summary` | 전체 펜션 분석 요약 | ✅ | + +--- + +### 4. 펜션 이미지 API (`/api/profile/pension/:id/images/*`) - v3.6.0 신규 + +| Method | Endpoint | 설명 | Auth | Query | +|--------|----------|------|:----:|-------| +| GET | `/api/profile/pension/:id/images` | 이미지 목록 | ✅ | `?source=naver\|google\|instagram\|upload` | +| POST | `/api/profile/pension/:id/images` | 이미지 업로드 | ✅ | - | +| DELETE | `/api/profile/pension/:id/images/:imageId` | 이미지 삭제 | ✅ | - | +| GET | `/api/profile/pension/:id/images/stats` | 소스별 이미지 통계 | ✅ | - | + +**이미지 소스 구분:** +- `naver` - 네이버 지도에서 크롤링 +- `google` - 구글 지도에서 크롤링 +- `instagram` - 인스타그램에서 크롤링 +- `upload` - 사용자 직접 업로드 + +--- + +### 5. 자동 생성/업로드 API + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/profile/pension/:id/auto-generation` | 자동 생성 설정 | ✅ | +| POST | `/api/profile/pension/:id/auto-generation` | 자동 생성 설정 저장 | ✅ | +| GET | `/api/profile/pension/:id/auto-generation/logs` | 자동 생성 로그 | ✅ | +| POST | `/api/profile/pension/:id/auto-generation/trigger` | 수동 트리거 | ✅ | +| PATCH | `/api/auto-generation/logs/:logId` | 로그 상태 업데이트 | ✅ | +| GET | `/api/auto-generation/scheduled` | 예약된 작업 목록 | - | +| POST | `/api/auto-upload` | 자동 업로드 실행 | ✅ | + +--- + +### 6. 렌더링 큐 API (`/api/render/*`) - v3.7.0 신규 + +> **백그라운드 렌더링 시스템**: 페이지 이동/로그아웃해도 작업 계속 진행 + +| Method | Endpoint | 설명 | Auth | Body/Response | +|--------|----------|------|:----:|---------------| +| POST | `/api/render/start` | 렌더링 작업 시작 | ✅ | `{posterBase64, audioBase64, imagesBase64, adCopy, textEffect, businessName, aspectRatio, pensionId}` → `{jobId, creditsCharged}` | +| GET | `/api/render/status/:jobId` | 작업 상태 조회 | ✅ | → `{status, progress, downloadUrl, error_message}` | +| GET | `/api/render/jobs` | 내 작업 목록 | ✅ | `?status=pending&limit=20` → `{jobs[], pendingCount}` | + +**렌더링 흐름:** +``` +1. POST /api/render/start + └─ 크레딧 체크 → 선차감 → render_jobs 저장 → jobId 반환 (즉시) + +2. Background Worker + └─ pending 작업 처리 (MAX 3개 동시) + └─ Puppeteer 녹화 + FFmpeg 합성 + └─ 진행률 DB 업데이트 + +3. GET /api/render/status/:jobId (폴링) + └─ progress: 0-100% + └─ status: completed → downloadUrl 제공 + └─ status: failed → credits_refunded (환불) +``` + +**제한 사항:** +| 제한 | 값 | 에러 코드 | +|------|-----|-----------| +| 계정당 동시 렌더링 | **1개** | `RENDER_IN_PROGRESS` (429) | +| 서버 전체 동시 렌더링 | **3개** | 큐 대기 | +| 크레딧 부족 | - | `INSUFFICIENT_CREDITS` (403) | + +--- + +### 7. 크롤링 API - v3.6.0 확장 + +#### 네이버 지도 크롤링 + +| Method | Endpoint | 설명 | Auth | Body | +|--------|----------|------|:----:|------| +| POST | `/api/naver/crawl` | 네이버 플레이스 크롤링 | - | `{url}` | + +#### 인스타그램 크롤링 - v3.6.0 신규 + +| Method | Endpoint | 설명 | Auth | Body | +|--------|----------|------|:----:|------| +| POST | `/api/instagram/crawl` | Instagram 프로필 크롤링 | - | `{url}` | + +#### Google Places API + +| Method | Endpoint | 설명 | Auth | Body | +|--------|----------|------|:----:|------| +| POST | `/api/google/places/search` | 장소 검색 | ✅ | `{query}` | +| POST | `/api/google/places/details` | 장소 상세 정보 | ✅ | `{placeId}` | +| POST | `/api/google/places/photo` | 사진 다운로드 | ✅ | `{photoReference}` | + +--- + +### 7. AI API (Gemini / Suno) + +#### Gemini AI 엔드포인트 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| POST | `/api/gemini/creative-content` | 광고 카피 생성 | ✅ | +| POST | `/api/gemini/speech` | TTS 음성 합성 | ✅ | +| POST | `/api/gemini/ad-poster` | 광고 포스터 생성 (Imagen 3) | ✅ | +| POST | `/api/gemini/image-gallery` | 갤러리 이미지 생성 | ✅ | +| POST | `/api/gemini/video-background` | 비디오 배경 생성 | ✅ | +| POST | `/api/gemini/text-effect` | 텍스트 이펙트 생성 | ✅ | +| POST | `/api/gemini/filter-images` | 이미지 필터링/분류 | ✅ | +| POST | `/api/gemini/enrich-description` | 설명 보강 | ✅ | +| POST | `/api/gemini/search-business` | 비즈니스 정보 검색 | ✅ | +| POST | `/api/gemini/analyze-dna` | 펜션 DNA 분석 | ✅ | + +#### Suno AI 음악 생성 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| POST | `/api/suno/generate` | AI 음악 생성 | ✅ | + +--- + +### 8. YouTube API (`/api/youtube/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/youtube/oauth/url` | OAuth URL 생성 | ✅ | +| GET | `/api/youtube/oauth/callback` | OAuth 콜백 처리 | - | +| GET | `/api/youtube/connection` | 연결 상태 조회 | ✅ | +| DELETE | `/api/youtube/connection` | 연결 해제 | ✅ | +| GET | `/api/youtube/settings` | 업로드 설정 조회 | ✅ | +| POST | `/api/youtube/settings` | 업로드 설정 저장 | ✅ | +| GET | `/api/youtube/my-playlists` | 내 플레이리스트 | ✅ | +| POST | `/api/youtube/my-playlists` | 플레이리스트 생성 | ✅ | +| GET | `/api/youtube/pension/:pensionId/playlists` | 펜션별 플레이리스트 | ✅ | +| POST | `/api/youtube/pension/:pensionId/playlists` | 펜션 플레이리스트 연결 | ✅ | +| DELETE | `/api/youtube/pension/:pensionId/playlists/:playlistId` | 연결 해제 | ✅ | +| POST | `/api/youtube/my-upload` | 영상 업로드 | ✅ | +| POST | `/api/youtube/upload` | 영상 업로드 (레거시) | ✅ | +| GET | `/api/youtube/upload-history` | 업로드 히스토리 | ✅ | +| POST | `/api/youtube/seo` | SEO 메타데이터 생성 | ✅ | +| GET | `/api/youtube/playlists` | 플레이리스트 목록 | ✅ | +| POST | `/api/youtube/playlists` | 플레이리스트 생성 | ✅ | +| GET | `/api/youtube/playlists/:playlistId/videos` | 플레이리스트 영상 | ✅ | +| POST | `/api/youtube/playlists/business` | 비즈니스 플레이리스트 | ✅ | +| POST | `/api/profile/pension/:id/youtube-playlist` | 펜션-플레이리스트 연결 | ✅ | +| DELETE | `/api/profile/pension/:id/youtube-playlist` | 연결 해제 | ✅ | + +--- + +### 9. Instagram API (`/api/instagram/*`) - v3.0+ 신규 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/instagram/health` | 서비스 상태 확인 | - | +| POST | `/api/instagram/connect` | 계정 연결 | ✅ | +| POST | `/api/instagram/disconnect` | 연결 해제 | ✅ | +| GET | `/api/instagram/status` | 연결 상태 조회 | ✅ | +| PUT | `/api/instagram/settings` | 업로드 설정 수정 | ✅ | +| POST | `/api/instagram/upload` | Reels 업로드 | ✅ | +| GET | `/api/instagram/weekly-stats` | 주간 통계 | ✅ | +| GET | `/api/instagram/history` | 업로드 히스토리 | ✅ | + +--- + +### 10. TikTok API (`/api/tiktok/*`) - v3.0 신규 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/tiktok/oauth/url` | OAuth URL 생성 | ✅ | +| GET | `/api/tiktok/oauth/callback` | OAuth 콜백 처리 | - | +| GET | `/api/tiktok/status` | 연결 상태 조회 | ✅ | +| POST | `/api/tiktok/disconnect` | 연결 해제 | ✅ | +| GET | `/api/tiktok/settings` | 업로드 설정 조회 | ✅ | +| PUT | `/api/tiktok/settings` | 업로드 설정 수정 | ✅ | +| POST | `/api/tiktok/upload` | Direct Post 업로드 | ✅ | +| POST | `/api/tiktok/upload-to-inbox` | Draft/Inbox 업로드 | ✅ | +| GET | `/api/tiktok/history` | 업로드 히스토리 | ✅ | +| GET | `/api/tiktok/stats` | 통계 조회 | ✅ | + +--- + +### 11. 에셋 API (`/api/user-assets/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/user-assets/stats` | 스토리지 통계 | ✅ | +| GET | `/api/user-assets` | 에셋 목록 | ✅ | +| POST | `/api/user-assets/upload` | 파일 업로드 (다중) | ✅ | +| DELETE | `/api/user-assets/:id` | 에셋 삭제 | ✅ | +| POST | `/api/assets/upload` | 에셋 업로드 (일반) | ✅ | + +--- + +### 12. 히스토리 API (`/api/history/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/history` | 생성 기록 목록 | ✅ | +| POST | `/api/history` | 기록 저장 | ✅ | +| DELETE | `/api/history/:id` | 기록 삭제 | ✅ | +| DELETE | `/api/history` | 일괄 삭제 | ✅ | + +--- + +### 13. 크레딧 API (`/api/credits/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/credits` | 크레딧 잔액 조회 | ✅ | +| GET | `/api/credits/history` | 크레딧 사용 내역 | ✅ | +| POST | `/api/credits/request` | 충전 요청 | ✅ | +| GET | `/api/credits/requests` | 내 충전 요청 목록 | ✅ | + +--- + +### 14. 축제/공개 펜션 API + +#### 축제 API (`/api/festivals/*`) + +| Method | Endpoint | 설명 | Auth | Query | +|--------|----------|------|:----:|-------| +| GET | `/api/festivals` | 축제 목록 | - | `?region=&month=&keyword=` | +| GET | `/api/festivals/grouped` | 지역별 그룹화 | - | - | +| GET | `/api/festivals/:id` | 축제 상세 | - | - | +| GET | `/api/festivals/:id/nearby-pensions` | 근처 펜션 | - | - | +| GET | `/api/festivals/stats/by-region` | 지역별 통계 | - | - | +| GET | `/api/festivals/stats/by-month` | 월별 통계 | - | - | + +#### 공개 펜션 API (`/api/pensions/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/pensions` | 공개 펜션 목록 | - | +| GET | `/api/pensions/:id` | 펜션 상세 | - | +| GET | `/api/pensions/:id/nearby-festivals` | 근처 축제 | - | +| GET | `/api/pensions/stats/by-region` | 지역별 통계 | - | + +--- + +### 15. 관리자 API (`/api/admin/*`) + +#### 사용자 관리 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/admin/users` | 사용자 목록 | 🔐 | +| GET | `/api/admin/users/:id/detail` | 사용자 상세 | 🔐 | +| POST | `/api/admin/users` | 사용자 생성 | 🔐 | +| DELETE | `/api/admin/users/:id` | 사용자 삭제 | 🔐 | +| POST | `/api/admin/approve` | 사용자 승인 | 🔐 | +| PUT | `/api/admin/users/:id/role` | 역할 변경 | 🔐 | +| PUT | `/api/admin/users/:id/plan` | 플랜 변경 | 🔐 | +| POST | `/api/admin/users/:id/reset-password` | 비밀번호 초기화 | 🔐 | +| POST | `/api/admin/users/:id/credits` | 크레딧 직접 조정 | 🔐 | + +#### 크레딧 관리 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/admin/credits/requests` | 충전 요청 목록 | 🔐 | +| POST | `/api/admin/credits/requests/:id/process` | 요청 처리 | 🔐 | +| GET | `/api/admin/credits/stats` | 크레딧 통계 | 🔐 | + +#### 콘텐츠 관리 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/admin/history` | 전체 생성 기록 | 🔐 | +| DELETE | `/api/admin/history/:id` | 기록 삭제 | 🔐 | +| DELETE | `/api/admin/history` | 기록 일괄 삭제 | 🔐 | +| GET | `/api/admin/uploads` | 업로드 현황 | 🔐 | + +#### 시스템 관리 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/admin/stats` | 통계 요약 | 🔐 | +| GET | `/api/admin/system-health` | 시스템 상태 | 🔐 | +| GET | `/api/admin/logs` | 시스템 로그 | 🔐 | +| GET | `/api/admin/settings` | 설정 조회 | 🔐 | +| PUT | `/api/admin/settings` | 설정 수정 | 🔐 | + +#### 쿠키 관리 - v3.6.0 신규 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/admin/cookies` | 쿠키 설정 조회 | 🔐 | +| PUT | `/api/admin/cookies` | 쿠키 설정 수정 | 🔐 | + +#### 데이터 동기화 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| POST | `/api/admin/sync/festivals` | 축제 데이터 동기화 | 🔐 | +| POST | `/api/admin/sync/pensions` | 펜션 데이터 동기화 | 🔐 | + +--- + +### 16. 고급 통계 API (`/api/admin/analytics/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/admin/analytics/summary` | 전체 요약 | 🔐 | +| GET | `/api/admin/analytics/user-growth` | 사용자 성장 추이 | 🔐 | +| GET | `/api/admin/analytics/video-trend` | 영상 생성 트렌드 | 🔐 | +| GET | `/api/admin/analytics/platform-uploads` | 플랫폼별 업로드 | 🔐 | +| GET | `/api/admin/analytics/credit-usage` | 크레딧 사용 분석 | 🔐 | +| GET | `/api/admin/analytics/plan-distribution` | 플랜 분포 | 🔐 | +| GET | `/api/admin/analytics/top-users` | Top 활동 사용자 | 🔐 | +| GET | `/api/admin/analytics/usage-pattern` | 사용 패턴 분석 | 🔐 | +| GET | `/api/admin/analytics/regional` | 지역별 분포 | 🔐 | +| GET | `/api/admin/analytics/revenue` | 수익 예측 | 🔐 | +| GET | `/api/admin/analytics/activity-logs` | 활동 로그 | 🔐 | +| GET | `/api/admin/analytics/system-health` | 시스템 헬스 | 🔐 | +| POST | `/api/admin/analytics/update-daily` | 일일 통계 갱신 | 🔐 | +| GET | `/api/admin/analytics/full-report` | 전체 리포트 | 🔐 | + +--- + +### 17. API 사용량/빌링 API + +#### API 사용량 (`/api/admin/api-usage/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/admin/api-usage/stats` | 사용량 통계 | 🔐 | +| GET | `/api/admin/api-usage/logs` | 사용 로그 | 🔐 | +| GET | `/api/admin/api-usage/models` | 모델별 사용량 | 🔐 | +| GET | `/api/admin/api-usage/by-user` | 사용자별 사용량 | 🔐 | +| GET | `/api/admin/api-usage/user/:userId` | 특정 사용자 상세 | 🔐 | +| GET | `/api/admin/api-usage/monthly-report` | 월간 리포트 | 🔐 | + +#### 빌링 (`/api/admin/billing/*`) + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/admin/billing/status` | 빌링 상태 | 🔐 | +| GET | `/api/admin/billing/dashboard` | 빌링 대시보드 | 🔐 | +| GET | `/api/admin/billing/by-service` | 서비스별 비용 | 🔐 | +| GET | `/api/admin/billing/daily` | 일별 비용 | 🔐 | +| GET | `/api/admin/billing/by-sku` | SKU별 비용 | 🔐 | +| GET | `/api/admin/billing/monthly` | 월별 비용 | 🔐 | +| GET | `/api/admin/billing/gemini` | Gemini API 비용 | 🔐 | + +--- + +### 18. 유틸리티 API + +#### AI 보조 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| POST | `/api/ai/auto-description` | 자동 설명 생성 | ✅ | + +#### 프록시 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| GET | `/api/proxy/image` | 이미지 프록시 | - | +| GET | `/api/proxy/audio` | 오디오 프록시 | - | + +#### 렌더링 + +| Method | Endpoint | 설명 | Auth | +|--------|----------|------|:----:| +| POST | `/render` | 영상 렌더링 시작 | ✅ | + +--- + +## A6. 인증 시스템 + +### JWT 토큰 구조 + +```javascript +// 토큰 페이로드 +{ + "id": 1, + "username": "user@example.com", + "role": "user", + "iat": 1733400000, + "exp": 1733486400 // 24시간 후 만료 +} +``` + +### 인증 미들웨어 + +```javascript +// server/index.js +const authenticateToken = (req, res, next) => { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (!token) return res.status(401).json({ error: '인증 필요' }); + + jwt.verify(token, process.env.JWT_SECRET, (err, user) => { + if (err) return res.status(403).json({ error: '토큰 무효' }); + req.user = user; + next(); + }); +}; + +const requireAdmin = (req, res, next) => { + if (req.user.role !== 'admin') { + return res.status(403).json({ error: '관리자 권한 필요' }); + } + next(); +}; +``` + +### OAuth 2.0 플로우 + +```mermaid +sequenceDiagram + participant C as Client + participant S as Server + participant G as Google/Naver + participant D as Database + + C->>S: Click 소셜 로그인 버튼 + S-->>C: Redirect to OAuth Provider + C->>G: User Login & Consent + G-->>C: Redirect with code + C->>S: /api/auth/{provider}/callback?code=xxx + S->>G: Exchange code for tokens + G-->>S: User profile (email, name, id) + + alt 기존 사용자 (이메일 매칭) + S->>D: Link OAuth to existing account + else 신규 사용자 + S->>D: Create new user with OAuth + end + + S->>S: Generate JWT token + S-->>C: Redirect to /oauth/callback?token=xxx +``` + +--- + +## A7. 영상 생성 파이프라인 + +### 전체 프로세스 + +```mermaid +flowchart TD + subgraph INPUT["1️⃣ INPUT"] + I1[펜션 정보] + I2[사진 업로드 - 최대 15장] + I3[지도 URL 크롤링] + end + + subgraph AI["2️⃣ AI GENERATION"] + A1[광고 카피 - Gemini] + A2[음악 - Suno AI] + A3[포스터 - Imagen 3] + end + + subgraph COMPOSE["3️⃣ COMPOSITION"] + C1[HTML/CSS 애니메이션] + C2[텍스트 이펙트] + C3[전환 효과] + end + + subgraph RENDER["4️⃣ RENDER"] + R1[Puppeteer 캡처] + R2[FFmpeg 병합] + end + + subgraph OUTPUT["5️⃣ OUTPUT"] + O1[MP4 다운로드] + O2[YouTube 업로드] + end + + INPUT --> AI --> COMPOSE --> RENDER --> OUTPUT +``` + +### 텍스트 이펙트 종류 + +| 이펙트 | 설명 | CSS 클래스 | +|--------|------|-----------| +| Neon | 네온 글로우 효과 | `text-neon` | +| Glitch | 글리치 효과 | `text-glitch` | +| Typewriter | 타자기 효과 | `text-typewriter` | +| Cinematic | 시네마틱 페이드 | `text-cinematic` | +| Bold | 볼드 팝업 | `text-bold-pop` | +| Motion | 모션 블러 | `text-motion` | +| LineReveal | 라인 공개 | `text-line-reveal` | +| Boxed | 박스 프레임 | `text-boxed` | +| Elegant | 엘레강트 | `text-elegant` | +| BlockReveal | 블록 공개 | `text-block-reveal` | + +### FFmpeg 명령어 + +```bash +# 이미지 시퀀스 → MP4 +ffmpeg -framerate 30 -i frame_%04d.png \ + -c:v libx264 -pix_fmt yuv420p \ + -r 30 output_video.mp4 + +# 오디오 병합 +ffmpeg -i output_video.mp4 -i audio.wav \ + -c:v copy -c:a aac -shortest \ + final_output.mp4 +``` + +--- + +## A8. 개발 환경 설정 + +### 1. 저장소 클론 + +```bash +git clone https://github.com/waabaa/19-claude-festival-castad.git +cd 19-claude-festival-castad +``` + +### 2. 의존성 설치 + +```bash +# Frontend +npm install + +# Backend +cd server && npm install && cd .. +``` + +### 3. 환경 변수 설정 + +```bash +cp .env.example .env +# .env 파일 편집 (API 키 입력) +``` + +### 4. 개발 서버 실행 + +```bash +# 방법 1: 통합 스크립트 +./start.sh + +# 방법 2: npm 스크립트 +npm run dev + +# 방법 3: 개별 실행 +npm run dev:frontend # Vite 프론트엔드 +cd server && node index.js # Express 백엔드 +``` + +### 5. 접속 + +- **Frontend**: http://localhost:3000 +- **Backend API**: http://localhost:3001 +- **기본 관리자**: admin / admin123 + +--- + +## A9. 코드 기여 가이드 + +### 브랜치 전략 + +``` +main # 프로덕션 브랜치 +├── develop # 개발 통합 브랜치 +│ ├── feature/xxx # 기능 개발 +│ ├── fix/xxx # 버그 수정 +│ └── refactor/xxx # 리팩토링 +``` + +### 커밋 메시지 컨벤션 + +``` +feat: 새로운 기능 추가 +fix: 버그 수정 +docs: 문서 수정 +style: 코드 포맷팅 +refactor: 코드 리팩토링 +test: 테스트 추가 +chore: 빌드, 설정 변경 +``` + +### 테스트 실행 + +```bash +# API 테스트 +./test_api.sh + +# 빌드 테스트 +npm run build +``` + +--- + +# Part B. 운용자 매뉴얼 (Operator Manual) + +> 이 섹션은 CastAD Pro를 설치, 배포, 운영하는 **시스템 관리자/운용자**를 위한 가이드입니다. + +--- + +## B1. 시스템 요구사항 + +### 하드웨어 최소 사양 + +| 항목 | 최소 | 권장 | +|------|------|------| +| **CPU** | 2 Core | 4 Core | +| **RAM** | 4GB | 8GB | +| **Storage** | 20GB SSD | 100GB SSD | +| **Network** | 100Mbps | 1Gbps | + +### 소프트웨어 요구사항 + +| 소프트웨어 | 최소 버전 | 확인 명령어 | +|------------|----------|-------------| +| Node.js | 18.x | `node -v` | +| npm | 9.x | `npm -v` | +| FFmpeg | 6.x | `ffmpeg -version` | +| Git | 2.x | `git --version` | +| Python 3 | 3.9+ | `python3 --version` | + +### 운영체제 지원 + +| OS | 지원 여부 | 비고 | +|----|----------|------| +| Ubuntu 20.04+ | ✅ 권장 | 프로덕션 권장 | +| Debian 11+ | ✅ 지원 | | +| CentOS 8+ | ✅ 지원 | | +| macOS 12+ | ✅ 지원 | 개발용 | +| Windows 10+ (WSL2) | ⚠️ 제한적 | WSL2 필수 | + +--- + +## B2. 설치 및 배포 + +### 방법 1: 직접 설치 + +```bash +# 1. 시스템 패키지 설치 (Ubuntu/Debian) +sudo apt update +sudo apt install -y nodejs npm ffmpeg git python3 python3-pip + +# 2. Chrome/Chromium 설치 (Puppeteer용) +sudo apt install -y chromium-browser + +# 3. 프로젝트 클론 +git clone https://github.com/waabaa/19-claude-festival-castad.git +cd 19-claude-festival-castad + +# 4. 의존성 설치 +npm install +cd server && npm install && cd .. + +# 5. 환경 변수 설정 +cp .env.example .env +nano .env # API 키 입력 + +# 6. 프로덕션 빌드 +npm run build + +# 7. 실행 +./start.sh +``` + +### 방법 2: PM2로 배포 + +```bash +# PM2 설치 +npm install -g pm2 + +# 백엔드 실행 +cd server +pm2 start index.js --name "castad-server" + +# 프론트엔드 빌드 및 서빙 +cd .. +npm run build +pm2 serve dist 3000 --name "castad-frontend" --spa + +# 자동 재시작 설정 +pm2 startup +pm2 save +``` + +### 방법 3: Nginx 리버스 프록시 + +```nginx +# /etc/nginx/sites-available/castad + +server { + listen 80; + server_name castad1.ktenterprise.net; + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name castad1.ktenterprise.net; + + ssl_certificate /etc/letsencrypt/live/castad1.ktenterprise.net/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/castad1.ktenterprise.net/privkey.pem; + + # Frontend (정적 파일) + location / { + root /var/www/castad/dist; + try_files $uri $uri/ /index.html; + } + + # Backend API + location /api { + proxy_pass http://localhost:3001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_cache_bypass $http_upgrade; + } + + # 렌더링 API + location /render { + proxy_pass http://localhost:3001; + proxy_read_timeout 300s; # 렌더링은 오래 걸릴 수 있음 + } + + # 다운로드 파일 + location /downloads { + alias /var/www/castad/server/downloads; + } +} +``` + +--- + +## B3. 환경 변수 설정 + +### .env 파일 전체 구조 + +```env +# ============================================ +# Google AI API (필수) +# ============================================ +VITE_GEMINI_API_KEY=AIzaSy... + +# ============================================ +# Suno AI Proxy (필수) +# ============================================ +SUNO_API_KEY=suno_... + +# ============================================ +# 인증 (필수) +# ============================================ +JWT_SECRET=your_super_secret_key_min_256_bits_random_string + +# ============================================ +# 서버 설정 +# ============================================ +PORT=3001 +FRONTEND_URL=https://castad1.ktenterprise.net + +# ============================================ +# 이메일 서비스 (선택 - 이메일 인증용) +# ============================================ +SMTP_HOST=smtp.gmail.com +SMTP_PORT=587 +SMTP_USER=your-email@gmail.com +SMTP_PASS=your-app-password +SMTP_FROM=CastAD + +# ============================================ +# Google OAuth (선택 - 소셜 로그인용) +# ============================================ +GOOGLE_CLIENT_ID=your_google_client_id.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=your_google_client_secret + +# ============================================ +# Naver OAuth (선택 - 소셜 로그인용) +# ============================================ +NAVER_CLIENT_ID=your_naver_client_id +NAVER_CLIENT_SECRET=your_naver_client_secret + +# ============================================ +# Instagram 서비스 (선택) +# ============================================ +INSTAGRAM_ENCRYPTION_KEY=base64_encoded_32_byte_key +``` + +### 환경 변수 설명 + +| 변수 | 필수 | 설명 | +|------|------|------| +| `VITE_GEMINI_API_KEY` | ✅ | Google AI Studio에서 발급 | +| `SUNO_API_KEY` | ✅ | Suno AI 프록시 API 키 | +| `JWT_SECRET` | ✅ | 최소 256비트 랜덤 문자열 | +| `PORT` | ❌ | 백엔드 포트 (기본: 3001) | +| `FRONTEND_URL` | ❌ | CORS 허용 도메인 | +| `SMTP_*` | ❌ | 이메일 인증 활성화 시 필요 | +| `GOOGLE_*` | ❌ | Google 로그인/YouTube 연동 시 필요 | +| `NAVER_*` | ❌ | Naver 로그인 시 필요 | + +--- + +## B4. 외부 서비스 연동 + +### Google Cloud Console 설정 + +#### 1. 프로젝트 생성 +1. https://console.cloud.google.com 접속 +2. 새 프로젝트 생성 또는 기존 프로젝트 선택 + +#### 2. API 활성화 +- **YouTube Data API v3** 활성화 +- **Gemini API** (AI Studio에서 별도 발급) + +#### 3. OAuth 2.0 설정 + +1. **API 및 서비스** → **OAuth 동의 화면** + - 사용자 유형: 외부 + - 앱 이름: CastAD + - 범위(Scopes): + - `email` + - `profile` + - `openid` + - `https://www.googleapis.com/auth/youtube.upload` + - `https://www.googleapis.com/auth/youtube` + +2. **사용자 인증 정보** → **OAuth 2.0 클라이언트 ID 생성** + - 애플리케이션 유형: 웹 애플리케이션 + - 승인된 JavaScript 원본: + - `http://localhost:3000` (개발) + - `https://castad1.ktenterprise.net` (프로덕션) + - 승인된 리디렉션 URI: + - `http://localhost:3001/api/auth/google/callback` + - `http://localhost:3001/api/youtube/oauth/callback` + - `https://api.castad1.ktenterprise.net/api/auth/google/callback` + - `https://api.castad1.ktenterprise.net/api/youtube/oauth/callback` + +3. **client_secret.json 다운로드** + - 다운로드한 파일을 `server/client_secret.json`으로 저장 + +### Naver Developers 설정 + +1. https://developers.naver.com 접속 +2. **애플리케이션 등록** +3. 사용 API: 네아로 (네이버 아이디로 로그인) + - 필수 권한: 이름, 이메일, 프로필 사진 +4. 환경 추가: PC웹 + - 서비스 URL: `https://castad1.ktenterprise.net` + - Callback URL: `https://api.castad1.ktenterprise.net/api/auth/naver/callback` +5. Client ID, Secret을 `.env`에 저장 + +### TikTok for Developers 설정 + +#### 1. 개발자 계정 등록 +1. https://developers.tiktok.com 접속 +2. 개발자 계정 등록 및 인증 + +#### 2. 앱 생성 +1. **My Apps** → **Create App** +2. 앱 유형: **Web** +3. 앱 이름: CaStAD + +#### 3. Product 추가 +- **Login Kit**: 사용자 인증 +- **Content Posting API**: 영상 업로드 + - Direct Post: 즉시 게시 + - Inbox Upload: 초안으로 저장 + +#### 4. OAuth 설정 +- **Scopes 설정**: + - `user.info.basic` + - `user.info.profile` + - `user.info.stats` + - `video.publish` + - `video.upload` +- **Redirect URI**: + - `http://localhost:3001/api/tiktok/oauth/callback` (개발) + - `https://api.castad1.ktenterprise.net/api/tiktok/oauth/callback` (프로덕션) + +#### 5. 환경 변수 설정 +```bash +TIKTOK_CLIENT_KEY=your_client_key +TIKTOK_CLIENT_SECRET=your_client_secret +``` + +#### 6. TikTok 업로드 제한사항 +| 항목 | 제한 | +|------|------| +| 영상 길이 | 3초 ~ 10분 | +| 파일 크기 | 최대 4GB | +| 해상도 | 720p 이상 권장 | +| 비율 | 9:16 (세로), 16:9 (가로), 1:1 (정사각형) | +| 일일 업로드 | 앱당 최대 100개 | + +### Instagram 연동 설정 + +#### 1. 아키텍처 개요 +Instagram은 공식 API 제약으로 인해 **Python 마이크로서비스** 방식을 사용합니다: + +```mermaid +flowchart LR + A[CaStAD Server] -->|HTTP| B[Python Instagram Service] + B -->|Instagrapi| C[Instagram] + B -->|암호화| D[(SQLite)] +``` + +#### 2. Python 서비스 설치 +```bash +cd server/instagram + +# 가상환경 생성 (권장) +python3 -m venv venv +source venv/bin/activate # Linux/Mac +# 또는 .\venv\Scripts\activate # Windows + +# 의존성 설치 +pip install instagrapi cryptography flask +``` + +#### 3. 환경 변수 설정 +```bash +# 암호화 키 생성 (최초 1회) +python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" + +# .env에 추가 +INSTAGRAM_SERVICE_URL=http://localhost:5001 +INSTAGRAM_ENCRYPTION_KEY=your_generated_key +``` + +#### 4. 서비스 시작 +```bash +cd server/instagram +python3 instagram_service.py +# → Flask 서버가 포트 5001에서 실행됨 +``` + +#### 5. Instagram 기능 +| 기능 | 설명 | +|------|------| +| 계정 연결 | ID/PW로 로그인, 세션 암호화 저장 | +| 2FA 지원 | 2단계 인증 코드 입력 지원 | +| 릴스 업로드 | 최대 90초 세로 영상 | +| 주간 제한 | 안전한 사용을 위해 주당 1회 제한 (설정 가능) | +| 자동 재로그인 | 세션 만료 시 자동 갱신 | + +#### 6. Instagram 업로드 제한사항 +| 항목 | 릴스 (Reels) | +|------|------------| +| 영상 길이 | 3초 ~ 90초 | +| 파일 크기 | 최대 650MB | +| 해상도 | 1080x1920 권장 | +| 비율 | 9:16 (세로) | +| 주간 권장 | 1~3회 (계정 보호) | + +#### 7. 주의사항 +> ⚠️ **중요**: Instagram은 비공식 API를 사용하므로: +> - 과도한 업로드는 계정 제재 위험이 있습니다 +> - 2FA 활성화를 권장합니다 +> - 주간 업로드 제한을 준수하세요 +> - 비즈니스 계정 사용을 권장합니다 + +### Suno AI 설정 + +#### 1. API 키 발급 +1. https://suno.ai 접속 +2. 계정 생성 및 구독 +3. API 키 발급 + +#### 2. 환경 변수 설정 +```bash +SUNO_API_KEY=your_suno_api_key +``` + +#### 3. 음악 생성 옵션 +| 옵션 | 설명 | +|------|------| +| 장르 | Pop, K-Pop, Jazz, Classical, EDM 등 | +| 분위기 | 밝은, 차분한, 신나는, 감성적인 등 | +| 길이 | 30초, 60초, 90초 | +| 보컬 | 포함/미포함 | + +--- + +## B5. 관리자 대시보드 + +### 접속 정보 + +- **URL**: `https://castad1.ktenterprise.net/admin` +- **초기 계정**: `admin` / `admin123` + +> ⚠️ **중요**: 첫 로그인 후 반드시 비밀번호를 변경하세요! + +### 대시보드 메뉴 구성 + +| 메뉴 | 아이콘 | 기능 | +|------|--------|------| +| **개요** | 📊 | 전체 통계, 최근 활동, 시스템 상태 | +| **회원 관리** | 👥 | 사용자 목록, 승인, 역할/플랜 변경 | +| **크레딧** | 💰 | 크레딧 요청 처리, 수동 조정 | +| **콘텐츠** | 🎬 | 생성된 영상 목록, 삭제 관리 | +| **업로드 현황** | 📤 | YouTube/TikTok/Instagram 업로드 통계 | +| **활동 로그** | 📋 | 사용자 활동 기록 | +| **API 사용량** | 📈 | Gemini/Suno API 사용량 및 비용 | +| **설정** | ⚙️ | 시스템 설정, 쿠키 관리 (v3.6.0) | + +### 개요 화면 정보 + +``` +┌─────────────────────────────────────────────────────────┐ +│ 전체 사용자: 150명 생성된 영상: 1,234개 │ +│ 승인 대기: 5명 오늘 생성: 23개 │ +│ 활성 사용자: 89명 YouTube 업로드: 567개 │ +├─────────────────────────────────────────────────────────┤ +│ 시스템 상태 │ +│ ● Server: 정상 ● DB: 연결됨 ● Gemini: OK │ +│ ● FFmpeg: 설치됨 ● Suno: OK ● YouTube: OK │ +└─────────────────────────────────────────────────────────┘ +``` + +--- + +## B6. 사용자 관리 + +### 사용자 목록 조회 + +회원 관리 메뉴에서 모든 사용자를 확인할 수 있습니다: + +| 컬럼 | 설명 | +|------|------| +| ID | 사용자 고유 ID | +| 아이디 | 로그인 아이디 | +| 이름 | 사용자 이름 | +| 플랜 | Free/Basic/Pro/Business | +| 크레딧 | 현재 보유 크레딧 | +| 역할 | user/admin | +| 상태 | 승인 대기/승인됨 | +| 가입일 | 가입 날짜 | + +### 사용자 승인 + +신규 가입한 사용자는 기본적으로 **승인 대기** 상태입니다. + +1. 회원 관리 메뉴 이동 +2. 상태가 "대기"인 사용자 확인 +3. **승인** 버튼 클릭 +4. 확인 대화상자에서 **확인** 클릭 + +### 역할 변경 + +사용자를 관리자로 승격하거나 강등할 수 있습니다. + +1. 회원 관리 메뉴에서 사용자 선택 +2. 역할 드롭다운에서 **admin** 또는 **user** 선택 +3. 변경사항 자동 저장 + +### 사용자 삭제 + +> ⚠️ **주의**: 사용자 삭제 시 모든 관련 데이터(펜션, 영상, 에셋)가 삭제됩니다. + +1. 회원 관리 메뉴에서 사용자 선택 +2. **삭제** 버튼 클릭 +3. 확인 메시지 입력 후 **삭제 확인** + +--- + +## B7. 플랜 및 크레딧 관리 + +### 플랜 종류 + +| 플랜 | 가격 (월) | 크레딧 | 펜션 수 | 스토리지 | +|------|----------|--------|---------|----------| +| **Free** | 무료 | 10 | 1개 | 500MB | +| **Basic** | ₩29,000 | 15 | 1개 | 2GB | +| **Pro** | ₩89,000 | 75 | 5개 | 10GB | +| **Business** | ₩249,000 | 무제한 | 무제한 | 50GB | + +### 사용자 플랜 변경 + +1. **회원 관리** 메뉴 이동 +2. 변경할 사용자의 **플랜 배지** 클릭 +3. 플랜 수정 모달에서: + - 새 플랜 선택 + - 크레딧 수량 조정 (선택) +4. **저장** 클릭 + +### 크레딧 수동 조정 + +1. **크레딧** 메뉴 → **사용자별 크레딧 현황** +2. 해당 사용자의 **조정** 버튼 클릭 +3. 조정량 입력: + - 양수: 크레딧 추가 (예: `+10`) + - 음수: 크레딧 차감 (예: `-5`) +4. 사유 입력 +5. **적용** 클릭 + +### 크레딧 충전 요청 처리 + +1. **크레딧** 메뉴 → **충전 요청** +2. 대기 중인 요청 확인 +3. 각 요청에 대해: + - **승인**: 요청된 크레딧 즉시 지급 + - **거절**: 요청 취소 (사유 입력 권장) + +--- + +## B8. 사진 관리 시스템 + +CaStAD는 다양한 소스에서 펜션 사진을 수집하고 관리할 수 있는 종합 사진 관리 시스템을 제공합니다. + +### 지원 소스 (Image Sources) + +| 소스 | 설명 | 설정 방법 | +|------|------|----------| +| **네이버 지도** | 네이버 플레이스에서 사진 크롤링 | 네이버 쿠키 설정 (선택) | +| **구글 지도** | 구글 플레이스에서 사진 크롤링 | Google API 키 설정 | +| **인스타그램** | 인스타그램 프로필에서 사진 크롤링 | 인스타그램 쿠키 설정 (필수) | +| **직접 업로드** | 사용자가 직접 업로드한 사진 | - | + +### 크롤링 사용법 + +1. **펜션 등록 시 자동 크롤링**: + - 네이버: `https://naver.me/...` 또는 `https://map.naver.com/...` + - 구글: `https://maps.google.com/...` 또는 `https://goo.gl/maps/...` + - 인스타: `https://instagram.com/username` + +2. **소스별 자동 분류**: + - 크롤링된 이미지는 자동으로 소스가 구분되어 저장됩니다 + - 파일명에 소스 접두사 포함: `naver_`, `google_`, `instagram_` + +### 쿠키 관리 (관리자) + +관리자 대시보드 → **설정** → **API 쿠키 관리** + +``` +┌─────────────────────────────────────────┐ +│ API 쿠키 관리 │ +├─────────────────────────────────────────┤ +│ 네이버 지도 쿠키 │ +│ [NNB=xxx; JSESSIONID=xxx; ...] [수정] │ +│ 마지막 수정: 2024-01-15 14:30 │ +│ │ +│ 인스타그램 쿠키 │ +│ [sessionid=xxx; csrftoken=xxx; ...][수정]│ +│ 마지막 수정: 2024-01-15 14:30 │ +└─────────────────────────────────────────┘ +``` + +**쿠키 획득 방법**: +1. 브라우저에서 해당 서비스에 로그인 +2. 개발자 도구 (F12) → Application → Cookies +3. 모든 쿠키를 `이름=값; 이름=값; ...` 형식으로 복사 +4. 관리자 대시보드에서 저장 + +### 이미지 소스별 API + +```bash +# 전체 이미지 조회 +GET /api/profile/pension/:id/images + +# 소스별 필터링 +GET /api/profile/pension/:id/images?source=naver +GET /api/profile/pension/:id/images?source=google +GET /api/profile/pension/:id/images?source=instagram +GET /api/profile/pension/:id/images?source=upload + +# 소스별 통계 +GET /api/profile/pension/:id/images/stats +# 응답: { total: 50, naver: 20, google: 15, instagram: 10, upload: 5 } +``` + +### 주의사항 + +- **쿠키 만료**: 로그인 세션 쿠키는 일정 기간 후 만료됩니다. 크롤링 실패 시 쿠키를 다시 설정하세요. +- **인스타그램 제한**: 인스타그램은 비로그인 상태에서 접근이 제한됩니다. 반드시 쿠키를 설정하세요. +- **API 호출 제한**: 과도한 크롤링은 차단될 수 있습니다. 적절한 간격을 두고 사용하세요. + +--- + +## B9. 시스템 모니터링 + +### 시스템 상태 확인 + +관리자 대시보드 → **시스템** 메뉴에서 확인: + +| 항목 | 설명 | +|------|------| +| **서버 상태** | Node.js 프로세스 상태 | +| **데이터베이스** | SQLite 연결 상태 | +| **메모리 사용량** | 현재 메모리 사용률 | +| **디스크 사용량** | 스토리지 사용률 | +| **외부 서비스** | Gemini, Suno, YouTube API 상태 | + +### 로그 확인 + +```bash +# PM2 로그 확인 +pm2 logs castad-server + +# 실시간 로그 모니터링 +pm2 monit + +# 로그 파일 직접 확인 +tail -f ~/.pm2/logs/castad-server-out.log +tail -f ~/.pm2/logs/castad-server-error.log +``` + +### 프로세스 모니터링 + +```bash +# PM2 상태 확인 +pm2 status + +# 상세 정보 +pm2 show castad-server + +# 재시작 +pm2 restart castad-server + +# 중지 +pm2 stop castad-server +``` + +--- + +## B10. 백업 및 복구 + +### 데이터베이스 백업 + +```bash +# SQLite 데이터베이스 백업 +cp server/database.sqlite backups/database_$(date +%Y%m%d).sqlite + +# 자동 백업 (cron 설정) +# 매일 새벽 3시 백업 +0 3 * * * cp /path/to/server/database.sqlite /path/to/backups/db_$(date +\%Y\%m\%d).sqlite +``` + +### 미디어 파일 백업 + +```bash +# 영상 파일 백업 +tar -czf backups/downloads_$(date +%Y%m%d).tar.gz server/downloads/ + +# 에셋 파일 백업 +tar -czf backups/assets_$(date +%Y%m%d).tar.gz server/assets/ +``` + +### 전체 백업 + +```bash +#!/bin/bash +# backup.sh + +BACKUP_DIR="/path/to/backups" +DATE=$(date +%Y%m%d_%H%M%S) + +# 데이터베이스 +cp server/database.sqlite $BACKUP_DIR/db_$DATE.sqlite + +# 미디어 파일 +tar -czf $BACKUP_DIR/media_$DATE.tar.gz server/downloads/ server/assets/ + +# 설정 파일 (환경 변수 제외) +cp .env.example $BACKUP_DIR/env_example_$DATE + +# 7일 이상 된 백업 삭제 +find $BACKUP_DIR -type f -mtime +7 -delete + +echo "Backup completed: $DATE" +``` + +### 복구 + +```bash +# 데이터베이스 복구 +cp backups/database_YYYYMMDD.sqlite server/database.sqlite + +# 미디어 파일 복구 +tar -xzf backups/downloads_YYYYMMDD.tar.gz -C server/ + +# 서버 재시작 +pm2 restart castad-server +``` + +--- + +## B11. 트러블슈팅 + +### 자주 발생하는 오류 + +#### 1. Puppeteer 실행 오류 + +``` +Error: Failed to launch the browser process +``` + +**해결 방법:** +```bash +# Chrome/Chromium 의존성 설치 (Ubuntu) +sudo apt install -y ca-certificates fonts-liberation libasound2 libatk-bridge2.0-0 \ + libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 \ + libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 \ + libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \ + libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 \ + libxss1 libxtst6 lsb-release wget xdg-utils +``` + +#### 2. SQLite 잠금 오류 + +``` +SQLITE_BUSY: database is locked +``` + +**해결 방법:** +```bash +# WAL 모드 활성화 (server/db.js에 추가) +db.run("PRAGMA journal_mode=WAL;"); + +# 또는 서버 재시작 +pm2 restart castad-server +``` + +#### 3. YouTube API 할당량 초과 + +``` +Error: quotaExceeded +``` + +**해결 방법:** +- 일일 할당량: 10,000 units +- 업로드 1건 = 1,600 units (하루 최대 6건) +- Google Cloud Console에서 할당량 증가 요청 + +#### 4. 메모리 부족 + +``` +FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory +``` + +**해결 방법:** +```bash +# Node.js 메모리 한도 증가 +export NODE_OPTIONS="--max-old-space-size=4096" + +# PM2 설정 +pm2 start index.js --node-args="--max-old-space-size=4096" +``` + +#### 5. FFmpeg 오류 + +``` +Error: ffmpeg exited with code 1 +``` + +**해결 방법:** +```bash +# FFmpeg 버전 확인 +ffmpeg -version + +# 재설치 +sudo apt install --reinstall ffmpeg + +# 코덱 확인 +ffmpeg -codecs | grep libx264 +``` + +### 로그 분석 + +```bash +# 오류만 필터링 +grep -i "error" ~/.pm2/logs/castad-server-error.log | tail -50 + +# 특정 날짜 로그 +grep "2024-12-06" ~/.pm2/logs/castad-server-out.log + +# 실시간 오류 모니터링 +tail -f ~/.pm2/logs/castad-server-error.log +``` + +--- + +# Part C. 사용자 매뉴얼 (User Manual) + +> 이 섹션은 CastAD Pro를 사용하여 마케팅 영상을 제작하는 **펜션 사업자/일반 사용자**를 위한 가이드입니다. + +--- + +## C1. 시작하기 + +### CastAD란? + +CastAD는 **AI가 자동으로 펜션 홍보 영상을 만들어주는 서비스**입니다. + +✅ 펜션 사진만 올리면 AI가 광고 문구, 음악, 영상을 만들어줍니다 +✅ 만든 영상을 버튼 하나로 YouTube에 올릴 수 있습니다 +✅ 외국어 자막도 자동 생성됩니다 (영어, 일본어, 중국어 등) + +### 필요한 것 + +- 인터넷 연결된 컴퓨터 또는 태블릿 +- 웹 브라우저 (Chrome 권장) +- 펜션 사진 (권장: 5~15장) + +### 간단 사용법 + +``` +1. 회원가입 → 로그인 +2. 펜션 정보 등록 +3. 사진 올리기 +4. "영상 생성" 클릭 +5. 잠시 기다리기 (약 2~3분) +6. 완성된 영상 다운로드 또는 YouTube 업로드 +``` + +--- + +## C2. 회원가입 및 로그인 + +### 이메일로 회원가입하기 + +1. CastAD 홈페이지 접속 +2. 우측 상단 **"회원가입"** 클릭 +3. 정보 입력: + - **아이디**: 영문/숫자 조합 (예: mypension123) + - **이메일**: 실제 사용하는 이메일 (인증 필요) + - **비밀번호**: 8자 이상, 영문+숫자+특수문자 + - **이름**: 본인 이름 + - **연락처**: 휴대폰 번호 +4. **"가입하기"** 버튼 클릭 +5. 입력한 이메일로 인증 메일 확인 +6. 메일의 **"이메일 인증하기"** 버튼 클릭 +7. 인증 완료! 이제 로그인 가능 + +### 소셜 계정으로 회원가입하기 + +더 간편하게 가입할 수 있습니다: + +1. 로그인 페이지에서 **"Google로 계속하기"** 또는 **"Naver로 계속하기"** 클릭 +2. 해당 계정으로 로그인 +3. 권한 동의 +4. 자동으로 회원가입 및 로그인 완료! + +### 로그인하기 + +1. 홈페이지에서 **"로그인"** 클릭 +2. 아이디와 비밀번호 입력 +3. **"로그인"** 버튼 클릭 + +### 비밀번호를 잊었어요 + +1. 로그인 페이지에서 **"비밀번호 찾기"** 클릭 +2. 가입할 때 사용한 이메일 입력 +3. **"비밀번호 재설정 링크 보내기"** 클릭 +4. 이메일 확인 후 링크 클릭 +5. 새 비밀번호 설정 + +--- + +## C3. 펜션 등록하기 + +### 왜 펜션을 등록해야 하나요? + +펜션 정보를 미리 등록해두면: +- 영상 생성할 때마다 정보를 다시 입력할 필요 없음 +- AI가 펜션 특성에 맞는 광고 문구를 더 잘 만듦 +- 여러 개의 펜션을 한 계정으로 관리 가능 + +### 펜션 등록 방법 + +#### 방법 1: 직접 입력 + +1. 로그인 후 왼쪽 메뉴에서 **"펜션 관리"** 클릭 +2. **"새 펜션 추가"** 버튼 클릭 +3. 펜션 정보 입력: + + | 항목 | 설명 | 예시 | + |------|------|------| + | **펜션 이름** | 한글 이름 | 하늘빛 풀빌라 | + | **영문 이름** | 영어 이름 | Hanulbit Pool Villa | + | **지역** | 소재 지역 | 가평 | + | **주소** | 전체 주소 | 경기도 가평군 청평면... | + | **펜션 유형** | 해당되는 것 모두 선택 | 풀빌라, 커플 펜션 | + | **주요 시설** | 보유 시설 선택 | 수영장, BBQ, 스파 | + | **예약 링크** | 예약 페이지 URL | https://... | + | **소개글** | 펜션 설명 | 청평호가 보이는... | + +4. **"저장"** 버튼 클릭 + +#### 방법 2: URL로 자동 입력 (v3.6.0 확장) + +네이버 지도, 구글 지도, 또는 인스타그램 URL을 입력하면 정보가 자동으로 채워집니다! + +**지원하는 URL 형식:** + +| 소스 | URL 예시 | +|------|----------| +| **네이버 지도** | `https://naver.me/...` 또는 `https://map.naver.com/...` | +| **구글 지도** | `https://maps.google.com/...` 또는 `https://goo.gl/maps/...` | +| **인스타그램** | `https://instagram.com/username` 또는 `https://www.instagram.com/username` | + +**사용 방법:** + +1. **"새 펜션 추가"** 클릭 +2. **"URL로 가져오기"** 탭 선택 +3. 원하는 URL 붙여넣기 +4. **"정보 가져오기"** 클릭 (URL이 자동으로 인식됩니다) +5. 자동으로 채워진 정보 확인 및 수정 +6. **"저장"** 클릭 + +> **인스타그램 크롤링 참고**: 인스타그램에서 사진을 가져오려면 관리자가 인스타그램 쿠키를 설정해야 합니다. 관리자에게 문의하세요. + +### 펜션 수정 및 삭제 + +- **수정**: 펜션 목록에서 해당 펜션의 **"수정"** 버튼 클릭 +- **삭제**: 펜션 목록에서 해당 펜션의 **"삭제"** 버튼 클릭 + +> ⚠️ 펜션을 삭제하면 해당 펜션으로 만든 영상 기록도 삭제될 수 있습니다. + +--- + +## C4. 영상 만들기 + +### 영상 생성 단계 + +``` +1단계: 펜션 선택 → 2단계: 옵션 설정 → 3단계: 생성 → 4단계: 결과 확인 +``` + +### 1단계: 펜션 선택 + +1. 왼쪽 메뉴에서 **"새 프로젝트"** 클릭 +2. 등록된 펜션 목록에서 **영상을 만들 펜션** 선택 +3. 펜션이 없으면 **"새 펜션 등록"** 클릭 + +### 2단계: 옵션 설정 + +#### 기본 옵션 + +| 옵션 | 설명 | 권장 | +|------|------|------| +| **화면 비율** | 16:9 (가로) / 9:16 (세로) | YouTube: 16:9, Shorts: 9:16 | +| **오디오 모드** | 음악 / 내레이션 / 인스트루멘탈 | 음악 권장 | +| **음악 장르** | K-Pop, 발라드, EDM 등 | 펜션 분위기에 맞게 | +| **텍스트 효과** | 글자 표시 스타일 | Cinematic 권장 | + +#### 사진 업로드 + +- **권장 장수**: 5~15장 +- **권장 해상도**: 1920x1080 이상 +- **형식**: JPG, PNG, WEBP +- **팁**: + - 외관, 객실, 수영장, 주변 풍경 등 다양하게 + - 밝고 선명한 사진이 좋음 + - 사람이 없는 사진 권장 + +### 3단계: 생성 시작 + +1. 모든 옵션 설정 확인 +2. **"영상 생성"** 버튼 클릭 +3. 생성 진행 화면 확인: + - 🔄 정보 분석 중... + - 🎵 음악 생성 중... + - 🎬 영상 렌더링 중... + - ✅ 완료! + +> 💡 **소요 시간**: 보통 2~3분, 이미지 수에 따라 다를 수 있음 + +### 4단계: 결과 확인 + +1. 생성된 영상 미리보기 +2. 마음에 들면: + - **"다운로드"**: 내 컴퓨터에 저장 + - **"YouTube 업로드"**: 바로 유튜브에 올리기 +3. 마음에 안 들면: + - **"다시 생성"**: 옵션 변경 후 재생성 + +--- + +## C5. 영상 옵션 상세 + +### 화면 비율 + +| 비율 | 용도 | 크기 | +|------|------|------| +| **16:9** | 일반 YouTube 영상 | 1920x1080 | +| **9:16** | YouTube Shorts, 인스타 릴스, 틱톡 | 1080x1920 | + +### 오디오 모드 + +| 모드 | 설명 | 용도 | +|------|------|------| +| **음악** | AI가 만든 로고송 | 브랜드 홍보 영상 | +| **내레이션** | AI 성우가 읽어주는 광고 | 정보 전달 위주 | +| **인스트루멘탈** | 가사 없는 배경음악 | 자막 집중형 영상 | + +### 음악 장르 + +한국어 콘텐츠: +- **K-Pop**: 신나는 분위기 +- **발라드**: 감성적인 분위기 +- **Hip-Hop**: 트렌디한 분위기 +- **EDM**: 역동적인 분위기 +- **트로트**: 친근한 분위기 +- **재즈**: 고급스러운 분위기 + +외국어 콘텐츠: +- **J-Pop**: 일본 대상 +- **C-Pop**: 중국 대상 +- **Pop**: 영미권 대상 + +### 텍스트 효과 + +| 효과 | 설명 | 분위기 | +|------|------|--------| +| **Neon** | 네온사인 효과 | 화려함 | +| **Cinematic** | 영화 자막 스타일 | 고급스러움 | +| **Typewriter** | 타자기 효과 | 감성적 | +| **Bold** | 크게 팝업 | 임팩트 | +| **Elegant** | 우아한 등장 | 세련됨 | +| **Glitch** | 글리치 효과 | 트렌디 | + +### 전환 효과 + +| 효과 | 설명 | +|------|------| +| **Mix** | 부드러운 디졸브 | +| **Zoom** | 확대/축소 전환 | +| **Slide** | 슬라이드 전환 | +| **Wipe** | 화면 닦아내기 | + +--- + +## C6. YouTube 연동 + +### YouTube 계정 연결하기 + +1. 왼쪽 메뉴에서 **"설정"** 클릭 +2. **"YouTube 연동"** 탭 선택 +3. **"YouTube 계정 연결하기"** 버튼 클릭 +4. Google 로그인 화면에서 **YouTube 채널 계정**으로 로그인 +5. 권한 요청 화면에서 **"허용"** 클릭 +6. 연결 완료! 채널 이름이 표시됩니다 + +### YouTube에 영상 올리기 + +1. 영상 생성 완료 후 **"YouTube 업로드"** 클릭 +2. 업로드 정보 입력: + + | 항목 | 설명 | 자동 생성 | + |------|------|----------| + | **제목** | 영상 제목 | ✅ AI 생성 | + | **설명** | 영상 설명란 | ✅ AI 생성 | + | **태그** | 검색용 키워드 | ✅ AI 생성 | + | **공개 설정** | 공개/비공개/미등록 | 수동 선택 | + | **플레이리스트** | 추가할 재생목록 | 수동 선택 | + +3. 내용 확인 후 **"업로드"** 클릭 +4. 업로드 완료 후 **"YouTube에서 보기"** 클릭 + +### 업로드 기본 설정 + +매번 같은 설정을 하지 않도록 기본값을 저장할 수 있습니다: + +1. **"설정"** → **"YouTube 연동"** +2. 기본 설정 변경: + - 기본 공개 설정 + - 기본 카테고리 + - 기본 태그 + - 기본 플레이리스트 +3. **"설정 저장"** 클릭 + +### 연결 해제 + +1. **"설정"** → **"YouTube 연동"** +2. **"연결 해제"** 버튼 클릭 +3. 확인 대화상자에서 **"확인"** + +--- + +## C7. TikTok 연동 + +### TikTok 계정 연결하기 + +1. 왼쪽 메뉴에서 **"설정"** 클릭 +2. **"TikTok 연동"** 탭 선택 +3. **"TikTok 계정 연결하기"** 버튼 클릭 +4. TikTok 로그인 화면에서 계정으로 로그인 +5. 권한 요청 화면에서 **"승인"** 클릭 +6. 연결 완료! 프로필 정보가 표시됩니다 + +### TikTok에 영상 올리기 + +1. 영상 생성 완료 후 **"TikTok 업로드"** 클릭 +2. 업로드 방식 선택: + + | 방식 | 설명 | 권장 | + |------|------|------| + | **Direct Post** | 즉시 TikTok에 게시 | 빠른 업로드 | + | **Inbox (초안)** | TikTok 앱에서 편집 후 게시 | 추가 편집 필요시 | + +3. 업로드 정보 입력: + + | 항목 | 설명 | + |------|------| + | **제목** | 영상 캡션 (150자 제한) | + | **공개 설정** | 나만 보기/팔로워만/전체 공개 | + | **듀엣 허용** | 다른 사람이 듀엣 가능 여부 | + | **댓글 허용** | 댓글 작성 허용 여부 | + | **스티치 허용** | 스티치 기능 허용 여부 | + +4. **"업로드"** 클릭 + +### TikTok 업로드 기본 설정 + +1. **"설정"** → **"TikTok 연동"** +2. 기본 설정 변경: + - 기본 공개 설정 + - 듀엣/댓글/스티치 허용 여부 + - 기본 해시태그 + - 업로드 방식 (Direct/Inbox) +3. **"설정 저장"** 클릭 + +### TikTok 영상 주의사항 + +> 💡 **팁**: TikTok은 세로 영상(9:16)에 최적화되어 있습니다. + +- 영상 길이: 3초 ~ 10분 +- 파일 크기: 최대 4GB +- 권장 해상도: 1080x1920 (Full HD 세로) + +--- + +## C8. Instagram 연동 + +### Instagram 계정 연결하기 + +> ⚠️ **참고**: Instagram은 보안상의 이유로 ID/비밀번호로 연결합니다. + +1. 왼쪽 메뉴에서 **"설정"** 클릭 +2. **"Instagram 연동"** 탭 선택 +3. **"Instagram 계정 연결하기"** 버튼 클릭 +4. Instagram 로그인 정보 입력: + - 사용자 이름 (또는 이메일) + - 비밀번호 +5. 2단계 인증 활성화된 경우: + - 인증 코드 입력 (SMS 또는 앱) +6. 연결 완료! 프로필 정보가 표시됩니다 + +### Instagram에 릴스 올리기 + +1. 영상 생성 완료 후 **"Instagram 업로드"** 클릭 +2. 업로드 정보 입력: + + | 항목 | 설명 | + |------|------| + | **캡션** | 게시물 설명 | + | **해시태그** | 검색용 해시태그 (기본 제공) | + +3. **"업로드"** 클릭 +4. 업로드 완료 후 Instagram에서 확인 + +### Instagram 업로드 제한 + +Instagram 계정 보호를 위해 다음 제한이 적용됩니다: + +| 항목 | 제한 | +|------|------| +| **주간 업로드** | 기본 1회 (설정 변경 가능) | +| **영상 길이** | 최대 90초 | +| **비율** | 9:16 (세로) 권장 | + +### Instagram 기본 설정 + +1. **"설정"** → **"Instagram 연동"** +2. 기본 설정 변경: + - 기본 캡션 템플릿 + - 기본 해시태그 + - 주간 업로드 제한 + - 업로드 알림 +3. **"설정 저장"** 클릭 + +### Instagram 연결 해제 + +1. **"설정"** → **"Instagram 연동"** +2. **"연결 해제"** 버튼 클릭 +3. 확인 대화상자에서 **"확인"** + +### Instagram 주의사항 + +> ⚠️ **중요**: Instagram 사용 시 주의하세요! + +- **과도한 업로드 금지**: 하루에 여러 번 업로드하면 계정이 제한될 수 있습니다 +- **비즈니스 계정 권장**: 일반 계정보다 제한이 적습니다 +- **2FA 활성화 권장**: 계정 보안을 위해 2단계 인증을 켜세요 +- **업로드 간격 유지**: 최소 24시간 간격 권장 + +--- + +## C9. 영상 관리 + +### 라이브러리 확인 + +왼쪽 메뉴의 **"라이브러리"**에서 생성한 모든 영상을 볼 수 있습니다. + +### 영상 목록 정보 + +| 항목 | 설명 | +|------|------| +| 썸네일 | 영상 미리보기 이미지 | +| 제목 | 펜션 이름 + 생성 날짜 | +| 생성일 | 영상을 만든 날짜 | +| 상태 | 업로드됨/미업로드 | +| 펜션 | 연결된 펜션 | + +### 영상 필터링 + +- **펜션별**: 특정 펜션의 영상만 보기 +- **상태별**: 업로드됨/미업로드 필터 +- **날짜순**: 최신순/오래된순 정렬 + +### 영상 관리 기능 + +| 기능 | 설명 | +|------|------| +| **다운로드** | MP4 파일로 저장 | +| **미리보기** | 영상 재생 | +| **YouTube 업로드** | 아직 안 올린 영상 업로드 | +| **삭제** | 영상 삭제 (복구 불가) | + +--- + +## C10. 에셋 관리 + +### 에셋이란? + +에셋은 영상 제작에 사용된 모든 파일입니다: +- **이미지**: 업로드한 사진, 크롤링한 사진, AI 생성 이미지 +- **오디오**: AI가 만든 음악 +- **비디오**: 완성된 영상 파일 + +### 에셋 확인하기 + +1. 왼쪽 메뉴에서 **"에셋"** 클릭 +2. 스토리지 사용량 확인 +3. 에셋 목록 확인 + +### 스토리지 관리 + +| 플랜 | 스토리지 한도 | +|------|--------------| +| Free | 500MB | +| Basic | 2GB | +| Pro | 10GB | +| Business | 50GB | + +스토리지가 부족하면: +- 불필요한 에셋 삭제 +- 플랜 업그레이드 + +### 에셋 삭제 + +1. 에셋 목록에서 삭제할 항목 선택 +2. **"삭제"** 버튼 클릭 +3. 확인 후 삭제 완료 + +> ⚠️ 삭제된 에셋은 복구할 수 없습니다. + +--- + +## C11. 계정 설정 + +### 프로필 수정 + +1. 왼쪽 메뉴에서 **"계정"** 클릭 +2. 수정할 정보 변경: + - 이름 + - 연락처 + - 이메일 +3. **"저장"** 클릭 + +### 비밀번호 변경 + +1. **"계정"** → **"비밀번호 변경"** +2. 현재 비밀번호 입력 +3. 새 비밀번호 입력 (8자 이상) +4. 새 비밀번호 확인 +5. **"변경"** 클릭 + +### 언어 설정 + +CastAD는 6개 언어를 지원합니다: + +| 언어 | 설정 | +|------|------| +| 한국어 | 기본값 | +| English | 영어 | +| 日本語 | 일본어 | +| 中文 | 중국어 | +| ไทย | 태국어 | +| Tiếng Việt | 베트남어 | + +변경 방법: +1. 상단 헤더의 **언어 버튼** 클릭 +2. 원하는 언어 선택 +3. 즉시 적용 + +### 테마 설정 + +- **라이트 모드**: 밝은 화면 +- **다크 모드**: 어두운 화면 +- **시스템**: 기기 설정 따름 + +변경 방법: +1. 상단 헤더의 **테마 버튼** 클릭 +2. 원하는 테마 선택 + +--- + +## C12. 구독 및 요금제 + +### 요금제 안내 + +| 플랜 | 가격 | 월간 영상 | 펜션 수 | 스토리지 | +|------|------|----------|---------|----------| +| **Free** | 무료 | 10개 | 1개 | 500MB | +| **Basic** | ₩29,000/월 | 15개 | 1개 | 2GB | +| **Pro** | ₩89,000/월 | 75개 | 5개 | 10GB | +| **Business** | ₩249,000/월 | 무제한 | 무제한 | 50GB | + +### 크레딧이란? + +- 영상 1개 생성 = 크레딧 1개 소모 +- 매월 플랜에 따라 크레딧 자동 충전 +- 미사용 크레딧은 이월되지 않음 + +### 남은 크레딧 확인 + +- 상단 헤더에 현재 크레딧 표시 +- **"계정"** 페이지에서 상세 확인 + +### 크레딧 충전 요청 + +무료 플랜에서 추가 크레딧이 필요하면: + +1. **"계정"** → **"크레딧 충전 요청"** +2. 요청 사유 입력 +3. **"요청하기"** 클릭 +4. 관리자 승인 후 크레딧 지급 + +### 플랜 업그레이드 + +더 많은 기능이 필요하면: + +1. **"계정"** → **"구독 관리"** +2. 원하는 플랜 선택 +3. 결제 진행 +4. 즉시 적용 + +--- + +## C13. 자주 묻는 질문 (FAQ) + +### 가입/로그인 관련 + +**Q: 회원가입 인증 메일이 안 와요.** +> A: 스팸 메일함을 확인해주세요. 그래도 없으면 **"인증 메일 재발송"**을 클릭하세요. + +**Q: 비밀번호를 잊었어요.** +> A: 로그인 페이지에서 **"비밀번호 찾기"**를 클릭하고 이메일을 입력하세요. + +**Q: 소셜 로그인을 다른 계정으로 바꾸고 싶어요.** +> A: 계정 설정에서 현재 소셜 연결을 해제한 후 다시 연결하세요. + +### 영상 생성 관련 + +**Q: 영상 생성이 오래 걸려요.** +> A: 보통 2~3분 소요됩니다. 이미지가 많거나 고화질이면 더 오래 걸릴 수 있습니다. + +**Q: 영상 생성 중 오류가 발생했어요.** +> A: 새로고침 후 다시 시도해주세요. 계속 오류가 나면 이미지 파일을 확인하거나 다른 이미지로 시도해보세요. + +**Q: 음악이 마음에 안 들어요.** +> A: 다른 장르를 선택하거나 **"다시 생성"**을 눌러 새로운 음악을 받아보세요. + +**Q: 영상에 내 로고를 넣을 수 있나요?** +> A: 현재 버전에서는 지원하지 않습니다. 향후 업데이트 예정입니다. + +### YouTube 관련 + +**Q: YouTube 연결이 안 돼요.** +> A: 팝업 차단을 해제하고, YouTube 채널이 있는 Google 계정으로 로그인했는지 확인하세요. + +**Q: 업로드한 영상이 YouTube에서 안 보여요.** +> A: 공개 설정이 **"비공개"**나 **"미등록"**이면 일반 검색에서 안 보입니다. YouTube Studio에서 확인하세요. + +**Q: YouTube 연결을 해제하면 업로드한 영상은 어떻게 되나요?** +> A: 이미 업로드된 영상은 그대로 YouTube에 남아있습니다. + +### 결제/플랜 관련 + +**Q: 무료 크레딧을 다 썼어요.** +> A: 다음 달까지 기다리거나, 플랜을 업그레이드하거나, 크레딧 충전을 요청하세요. + +**Q: 플랜 업그레이드하면 즉시 적용되나요?** +> A: 네, 결제 완료 즉시 적용됩니다. + +**Q: 환불 가능한가요?** +> A: 결제 후 7일 이내 미사용 시 환불 가능합니다. 고객센터로 문의하세요. + +### 기타 + +**Q: 모바일에서도 사용 가능한가요?** +> A: 네, 모바일 웹 브라우저에서 사용 가능합니다. 다만 PC에서 사용을 권장합니다. + +**Q: 영상 저작권은 누구에게 있나요?** +> A: 생성된 영상의 저작권은 사용자에게 있습니다. 자유롭게 사용하세요. + +**Q: 고객 지원은 어떻게 받나요?** +> A: 앱 내 **"문의하기"** 또는 이메일 support@castad.com으로 연락하세요. + +--- + +# Part D. 마케터 매뉴얼 (Marketer Manual) + +> 이 섹션은 CaStAD의 마케팅을 담당하는 **마케터**를 위한 가이드입니다. + +--- + +## D1. CaStAD 마케팅 전략 + +### 브랜드 포지셔닝 + +**CaStAD = 커스터드(Custard)** 🍮 + +커스터드 디저트처럼 **정밀하고, 부드럽고, 달콤한** 마케팅 솔루션 + +| 핵심 가치 | 의미 | 마케팅 메시지 | +|-----------|------|---------------| +| **정밀함 (달걀)** | 3-6도 온도 관리 | "AI가 정밀하게 분석합니다" | +| **부드러움 (우유)** | 크리미한 결과물 | "부드러운 사용자 경험" | +| **달콤함 (설탕)** | 만족스러운 성과 | "달콤한 마케팅 성과" | + +### 핵심 USP (Unique Selling Proposition) + +``` +1. 1분 만에 전문가급 영상 생성 +2. URL 하나로 모든 정보 자동 수집 +3. 6개 언어 자동 SEO 최적화 +4. YouTube/TikTok 원클릭 업로드 +5. 펜션 특화 AI (500+ 학습 데이터) +``` + +### 캠페인 슬로건 + +| 상황 | 슬로건 | +|------|--------| +| 메인 | "펜션 홍보, 1분이면 충분" | +| 시간 절약 | "7일 → 3분, 99% 시간 절약" | +| 비용 절감 | "100만원 → 1만원, 99% 비용 절감" | +| 성과 강조 | "평균 340% 예약 증가" | +| 신뢰 | "500+ 펜션이 선택한 이유" | + +--- + +## D2. 타겟 고객 분석 + +### 주요 타겟 페르소나 + +#### 페르소나 1: 김사장님 (50대 남성) +``` +- 직업: 펜션 단독 운영자 +- 고민: SNS 마케팅 방법을 모름 +- 니즈: 쉽고 빠르게 홍보하고 싶음 +- 결정 요인: 사용 편의성, 가격 +- 접점: 네이버 카페, 지역 상공회의소 +``` + +#### 페르소나 2: 이대표님 (40대 남성) +``` +- 직업: 펜션 프랜차이즈 대표 (5개 운영) +- 고민: 각 펜션별 마케팅 관리 어려움 +- 니즈: 통합 관리 + 성과 분석 +- 결정 요인: 다중 펜션 관리, 분석 기능 +- 접점: LinkedIn, 업계 세미나 +``` + +#### 페르소나 3: 박실장님 (30대 여성) +``` +- 직업: 숙박업 마케팅 대행사 실장 +- 고민: 클라이언트별 영상 제작 시간 +- 니즈: 대량 생산 + 고품질 유지 +- 결정 요인: API 연동, 화이트라벨 +- 접점: 마케팅 커뮤니티, 인스타그램 +``` + +### 고객 여정 맵 (Customer Journey) + +```mermaid +flowchart LR + A[인지] --> B[관심] + B --> C[고려] + C --> D[구매] + D --> E[충성] + + A -- "SNS 광고/검색" --> B + B -- "랜딩페이지 방문" --> C + C -- "무료 체험" --> D + D -- "유료 전환" --> E +``` + +### 채널별 타겟 전략 + +| 채널 | 타겟 | 메시지 | +|------|------|--------| +| **네이버** | 펜션 사장님 | "펜션 홍보 방법" 키워드 공략 | +| **인스타그램** | 젊은 운영자 | 비포/애프터 릴스 콘텐츠 | +| **YouTube** | 정보 탐색자 | 튜토리얼 + 성공 사례 | +| **카카오톡** | 기존 고객 | 신기능 안내, 리텐션 | +| **LinkedIn** | B2B 대행사 | 화이트라벨/API 제안 | + +--- + +## D3. 콘텐츠 마케팅 + +### 콘텐츠 유형별 가이드 + +#### 1. 블로그/SEO 콘텐츠 +``` +타겟 키워드: +- "펜션 홍보 방법" +- "숙박업 마케팅" +- "펜션 영상 제작" +- "인스타 릴스 만들기" +- "숙소 유튜브 마케팅" + +콘텐츠 구조: +1. 문제 제기 (펜션 마케팅 어려움) +2. 해결책 제시 (AI 영상 생성) +3. 사용법 안내 (단계별 가이드) +4. 성공 사례 (실제 고객 후기) +5. CTA (무료 체험 유도) +``` + +#### 2. 소셜 미디어 콘텐츠 + +**인스타그램 릴스 템플릿** +``` +[Hook - 3초] +"펜션 사장님들 영상 편집하느라 밤새세요?" + +[Problem - 5초] +"외주 맡기면 100만원, 직접 하면 7일..." + +[Solution - 7초] +"AI가 1분 만에 만들어드립니다" + +[Demo - 10초] +(실제 영상 생성 화면) + +[CTA - 5초] +"프로필 링크에서 무료 체험하세요" +``` + +**YouTube Shorts 스크립트** +``` +[썸네일] "100만원 vs 1만원 영상 비교" + +00:00 "왼쪽은 마케팅 대행사, 오른쪽은 AI" +00:05 "어떤 게 더 좋아 보여요?" +00:10 "정답은... 둘 다 AI입니다" +00:15 "CaStAD로 만들었어요" +00:20 "무료로 시작해보세요" +``` + +#### 3. 이메일 마케팅 + +**웰컴 시퀀스** +``` +Day 1: 가입 감사 + 첫 영상 만들기 가이드 +Day 3: 성공 사례 소개 + 팁 +Day 5: 미사용 시 리마인더 +Day 7: 무료 크레딧 추가 제안 +Day 14: 유료 플랜 할인 쿠폰 +``` + +--- + +## D4. SNS 마케팅 가이드 + +### 플랫폼별 전략 + +#### 인스타그램 +| 항목 | 전략 | +|------|------| +| **콘텐츠** | 릴스 80%, 피드 15%, 스토리 5% | +| **빈도** | 릴스 주 3회, 피드 주 1회 | +| **해시태그** | #펜션마케팅 #숙박업홍보 #AI영상 | +| **최적 시간** | 화/목 저녁 8-10시 | + +#### YouTube +| 항목 | 전략 | +|------|------| +| **콘텐츠** | Shorts 70%, 롱폼 30% | +| **Shorts** | 사용법, 비포/애프터, 꿀팁 | +| **롱폼** | 상세 튜토리얼, 인터뷰 | +| **SEO** | 제목에 "펜션 홍보" 키워드 | + +#### TikTok +| 항목 | 전략 | +|------|------| +| **콘텐츠** | 트렌드 활용 + 제품 소개 | +| **톤앤매너** | 캐주얼, 유머 가미 | +| **음악** | 트렌딩 사운드 활용 | +| **챌린지** | #1분영상챌린지 기획 | + +### 인플루언서 협업 + +| 티어 | 팔로워 | 협업 방식 | 예상 비용 | +|------|--------|----------|----------| +| **마이크로** | 1K-10K | 무료 체험 제공 | 무료~10만원 | +| **미드** | 10K-100K | 유료 리뷰 | 30-100만원 | +| **매크로** | 100K+ | 브랜드 앰버서더 | 협의 | + +--- + +## D5. 성과 측정 및 KPI + +### 핵심 KPI + +#### Acquisition (획득) +| 지표 | 목표 | 측정 방법 | +|------|------|----------| +| 웹사이트 방문자 | 월 10,000명 | Google Analytics | +| 회원가입 수 | 월 500명 | 내부 DB | +| 가입 전환율 | 5% | 방문자 대비 가입 | +| CAC | ₩10,000 이하 | 광고비 / 신규 가입 | + +#### Activation (활성화) +| 지표 | 목표 | 측정 방법 | +|------|------|----------| +| 첫 영상 생성률 | 70% | 가입 후 7일 내 | +| 활성 사용자(DAU) | 200명 | 일일 접속자 | +| 평균 세션 시간 | 5분+ | Analytics | + +#### Revenue (수익) +| 지표 | 목표 | 측정 방법 | +|------|------|----------| +| 유료 전환율 | 10% | 무료→유료 전환 | +| ARPU | ₩50,000 | 총 매출/활성 사용자 | +| MRR | ₩50,000,000 | 월 반복 매출 | +| LTV | ₩500,000 | 고객 생애 가치 | + +#### Retention (유지) +| 지표 | 목표 | 측정 방법 | +|------|------|----------| +| 월간 재방문율 | 60% | 30일 리텐션 | +| Churn Rate | 5% 이하 | 월간 이탈률 | +| NPS | 50+ | 분기별 설문 | + +--- + +## D6. 캠페인 사례 + +### 성공 캠페인 예시 + +#### 캠페인: "7일 → 3분" 챌린지 +``` +목표: 신규 가입 500명 +기간: 2주 +채널: 인스타그램 릴스 +예산: 200만원 + +결과: +- 노출: 150만 회 +- 가입: 623명 (+24.6%) +- CPA: 3,200원 +- ROAS: 420% +``` + +#### 캠페인: 펜션 사장님 인터뷰 +``` +목표: 신뢰도 향상 +형식: YouTube 롱폼 (5분) +내용: 실제 사용자 성공 스토리 + +결과: +- 조회수: 8.2만 +- 평균 시청 시간: 3분 42초 +- 유입 가입: 312명 +``` + +--- + +# Part E. 영업사원 매뉴얼 (Sales Manual) + +> 이 섹션은 CaStAD를 판매하는 **영업사원**을 위한 가이드입니다. + +--- + +## E1. 제품 이해하기 + +### 한 문장 정의 +> CaStAD는 **펜션/숙박업소를 위한 AI 마케팅 영상 자동 생성 및 멀티 플랫폼 관리 SaaS**입니다. + +### 엘리베이터 피치 (30초) + +``` +"펜션 사장님, 영상 마케팅 하려면 보통 100만원에 일주일 걸리잖아요? +CaStAD는 AI가 1분 만에 전문가급 홍보 영상을 만들어주고, +버튼 하나로 YouTube, TikTok에 자동 업로드까지 해줍니다. +월 2만9천원으로 시작할 수 있고, 지금 무료 체험도 가능합니다." +``` + +### 핵심 기능 요약 + +| 기능 | 고객 가치 | 경쟁 우위 | +|------|----------|----------| +| **AI 영상 생성** | 시간 99% 절약 | 펜션 특화 학습 | +| **URL 자동 크롤링** | 입력 작업 불필요 | 네이버/구글 지도 지원 | +| **AI 음악/로고송** | 저작권 걱정 없음 | Suno AI 연동 | +| **6개 언어 SEO** | 해외 고객 유치 | 한/영/일/중/태/베 | +| **다중 펜션 관리** | 효율적 운영 | 1계정 N펜션 | +| **YouTube/TikTok 연동** | 원클릭 업로드 | OAuth 자동화 | + +--- + +## E2. 타겟 시장 및 고객 + +### 시장 규모 + +``` +한국 펜션 시장: +- 전국 펜션 수: 약 20,000개 +- 연간 성장률: 5% +- 마케팅 예산: 평균 월 50만원 + +TAM (전체 시장): 20,000 × 50만원 × 12개월 = 1,200억원/년 +SAM (접근 가능): 5,000개 × 10만원 × 12개월 = 60억원/년 +SOM (초기 목표): 500개 × 5만원 × 12개월 = 3억원/년 +``` + +### 이상적인 고객 프로필 (ICP) + +| 특성 | 이상적 고객 | +|------|------------| +| **업종** | 펜션, 풀빌라, 글램핑, 리조트 | +| **규모** | 객실 5-30개 | +| **위치** | 관광지 (가평, 제주, 강원, 부산) | +| **운영 형태** | 직접 운영 or 소규모 법인 | +| **마케팅 현황** | SNS 하고 싶지만 방법 모름 | +| **예산** | 월 10-30만원 마케팅 예산 | + +### 고객 발굴 채널 + +| 채널 | 방법 | 예상 전환율 | +|------|------|------------| +| **인바운드 리드** | 웹사이트 문의 | 20-30% | +| **네이버 카페** | 펜션 운영자 커뮤니티 | 5-10% | +| **직접 연락** | 네이버 플레이스 검색 | 3-5% | +| **제휴** | 숙박 플랫폼, 상공회의소 | 15-20% | +| **추천** | 기존 고객 소개 | 30-40% | + +--- + +## E3. 세일즈 피치 + +### 상황별 오프닝 + +#### 콜드콜 오프닝 +``` +"안녕하세요, 사장님. 저는 CaStAD의 OOO입니다. +혹시 펜션 홍보 영상 만드시는 데 어려움 없으셨어요? +저희가 AI로 1분 만에 영상 만들어드리는 서비스를 하고 있는데, +잠깐 2분만 설명드려도 될까요?" +``` + +#### 인바운드 리드 오프닝 +``` +"안녕하세요, 사장님. CaStAD 문의 주셔서 감사합니다. +어떤 점이 궁금하셔서 문의 주셨나요? +(고객 니즈 파악 후) 아, 그 부분이라면 저희 서비스가 딱 맞을 것 같아요. +제가 화면 공유로 직접 보여드릴까요?" +``` + +### 니즈 파악 질문 (SPIN) + +| 유형 | 질문 예시 | +|------|----------| +| **Situation** | "지금 펜션 홍보는 어떻게 하고 계세요?" | +| **Problem** | "영상 만드실 때 가장 어려운 점이 뭐예요?" | +| **Implication** | "그러면 예약이 줄어들거나 하진 않으셨어요?" | +| **Need-payoff** | "만약 영상을 1분 만에 만들 수 있다면 어떠실 것 같아요?" | + +### 데모 스크립트 (5분) + +``` +[1분] 소개 +"지금부터 실제로 영상 만드는 걸 보여드릴게요." + +[2분] URL 입력 → 자동 정보 수집 +"이렇게 네이버 지도 URL만 붙여넣으면..." +"보세요, 펜션 이름, 사진, 위치가 자동으로 들어왔죠?" + +[1분] 옵션 설정 → 생성 +"음악 스타일 선택하시고... 생성 버튼 클릭!" +"AI가 지금 영상을 만들고 있어요." + +[1분] 결과 + CTA +"짠! 2분 만에 완성됐습니다. 어떠세요?" +"오늘 가입하시면 무료로 3개 영상 만들어보실 수 있어요." +``` + +--- + +## E4. 가격 정책 및 협상 + +### 공식 가격표 + +| 플랜 | 월 가격 | 영상/월 | 펜션 수 | 타겟 | +|------|--------|--------|--------|------| +| **Free** | 무료 | 10회 | 1개 | 체험 | +| **Basic** | ₩29,000 | 15회 | 1개 | 소규모 | +| **Pro** | ₩89,000 | 75회 | 5개 | 다중 운영 | +| **Business** | ₩249,000 | 무제한 | 무제한 | 대행사 | + +### 할인 정책 + +| 조건 | 할인 | 승인 | +|------|------|------| +| **연간 결제** | 20% | 자동 | +| **3개월 선결제** | 10% | 자동 | +| **단체 (5+)** | 15% | 팀장 승인 | +| **대행사 파트너** | 별도 협의 | 임원 승인 | + +### 가격 이의 대응 + +**"너무 비싸요"** +``` +"이해합니다. 그런데 사장님, 혹시 외주 맡기면 영상 하나에 얼마 하는지 아세요? +보통 50-100만원이에요. 저희는 월 2만9천원으로 15개 만드실 수 있어요. +영상당 2천원도 안 되는 거죠." +``` + +**"무료로 써볼게요"** +``` +"네, 물론이죠! 무료로 3개 먼저 만들어보시고, +마음에 드시면 그때 결정하세요. +다만 지금 가입하시면 첫 달 50% 할인 쿠폰 드려요." +``` + +--- + +## E5. 경쟁사 비교 + +### 직접 경쟁사 + +| 항목 | CaStAD | 경쟁사 A | 경쟁사 B | +|------|--------|---------|---------| +| **영상 생성 시간** | 2분 | 10분+ | 30분+ | +| **펜션 특화** | ✅ | ❌ | ❌ | +| **다국어 SEO** | 6개 언어 | 영어만 | ❌ | +| **AI 음악** | ✅ | ❌ | 유료 | +| **YouTube 연동** | OAuth | 수동 | 수동 | +| **TikTok 연동** | ✅ | ❌ | ❌ | +| **가격** | ₩29,000~ | ₩50,000~ | ₩100,000~ | + +### 간접 경쟁사 대응 + +**"그냥 직접 만들면 되잖아요"** +``` +"맞아요, 직접 만들 수도 있죠. 그런데 사장님 시간당 가치가 얼마예요? +영상 편집 배우고 만드는 데 10시간 쓰시면, 그 시간에 예약 10건 받으실 수 있잖아요. +저희한테 맡기시면 10분이면 끝나요." +``` + +**"마케팅 대행사 쓰면 되죠"** +``` +"대행사도 좋죠. 그런데 월 100만원 이상 들고, 수정하려면 또 기다려야 해요. +저희는 월 3만원에 직접 바로바로 수정할 수 있어요. +일단 무료로 비교해보시는 건 어떠세요?" +``` + +--- + +## E6. 이의 처리 + +### 자주 나오는 이의 + +#### "나중에 다시 연락주세요" +``` +반박: "네, 언제쯤 연락드릴까요? 혹시 다음 주 화요일 오후 2시 괜찮으세요?" +핵심: 구체적 일정 잡기 +``` + +#### "생각해볼게요" +``` +반박: "물론이죠. 혹시 어떤 부분이 고민되시는지 여쭤봐도 될까요? +제가 추가로 설명드릴 수 있으면 드리고 싶어서요." +핵심: 진짜 고민 포인트 파악 +``` + +#### "우리 펜션에는 안 맞을 것 같아요" +``` +반박: "어떤 점이 안 맞으실 것 같으세요? +저희가 이미 [비슷한 펜션 유형] 500개 이상 사용 중인데, +혹시 같은 지역 사례 보여드릴까요?" +핵심: 유사 사례로 신뢰 구축 +``` + +#### "효과가 있을지 모르겠어요" +``` +반박: "당연히 걱정되시죠. 저희 고객사 평균 예약 증가율이 340%예요. +무료로 먼저 테스트해보시고, 효과 없으면 안 쓰시면 되잖아요. +리스크가 없는 거죠." +핵심: 데이터 + 무료 체험으로 리스크 제거 +``` + +--- + +## E7. 클로징 전략 + +### 클로징 타이밍 신호 + +| 신호 | 대응 | +|------|------| +| "가격이 어떻게 되죠?" | 바로 결제 유도 | +| "다른 펜션도 쓰나요?" | 사례 → 클로징 | +| "기능이 더 있어요?" | 기능 설명 → 클로징 | +| "언제부터 쓸 수 있어요?" | 즉시 시작 가능 강조 | + +### 클로징 기법 + +#### 대안 제시 클로징 +``` +"사장님, Basic이랑 Pro 중에 어떤 게 더 맞으실 것 같으세요? +펜션 1개만 하시면 Basic, 나중에 확장 생각하시면 Pro 추천드려요." +``` + +#### 희소성 클로징 +``` +"지금 가입하시면 이번 달까지만 첫 달 50% 할인 적용해드려요. +내일부터는 정상가예요." +``` + +#### 시험 사용 클로징 +``` +"일단 무료로 3개 만들어보세요. 마음에 안 드시면 안 쓰시면 되고요. +지금 가입하시면 5분 뒤에 첫 영상 보실 수 있어요." +``` + +### 계약 후 체크리스트 + +``` +✅ 결제 완료 확인 +✅ 웰컴 이메일 발송 확인 +✅ 첫 영상 생성 가이드 안내 +✅ 3일 후 팔로업 전화 일정 등록 +✅ 1주일 후 만족도 체크 일정 등록 +✅ 고객 성공 팀에 핸드오프 +``` + +--- + +## 🏗️ 시스템 아키텍처 + +### 현재 아키텍처 (Single Server) + +

+ Single Server Architecture +

+ +**구성 요소:** +- **Frontend**: React 19 + Vite (Port 5173) +- **Backend**: Express.js (Port 3001) - Auth, Render, API Proxy Services +- **Database**: SQLite (File-based) +- **Storage**: Local Filesystem (downloads/, temp/, uploads/) +- **External APIs**: Gemini (AI), Suno (Music), YouTube (Upload) + +### 대규모 분산 아키텍처 (Scale-Out) + +

+ Distributed Architecture +

+ +**핵심 구성 요소:** +- **Load Balancer**: nginx / AWS ALB - 트래픽 분산 +- **API Servers**: 다중 Express.js 인스턴스 (수평 확장) +- **Redis**: 세션, 캐시, 작업 큐 (Bull Queue) +- **PostgreSQL**: Primary + Read Replica 구성 +- **S3/MinIO**: 영상, 이미지, 에셋 저장소 +- **Render Workers**: Puppeteer + FFmpeg (GPU 노드 권장) +- **Upload Workers**: YouTube, Instagram, TikTok 병렬 업로드 + +### Docker Compose 구성 (개발/소규모) + +```yaml +# docker-compose.yml +version: '3.8' + +services: + # Nginx Load Balancer + nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + depends_on: + - api1 + - api2 + + # API Servers + api1: + build: . + environment: + - NODE_ENV=production + - REDIS_URL=redis://redis:6379 + - DATABASE_URL=postgresql://user:pass@postgres:5432/castad + - S3_ENDPOINT=http://minio:9000 + depends_on: + - redis + - postgres + + api2: + build: . + environment: + - NODE_ENV=production + - REDIS_URL=redis://redis:6379 + - DATABASE_URL=postgresql://user:pass@postgres:5432/castad + - S3_ENDPOINT=http://minio:9000 + depends_on: + - redis + - postgres + + # Render Workers + render-worker: + build: + context: . + dockerfile: Dockerfile.render + deploy: + replicas: 3 + environment: + - REDIS_URL=redis://redis:6379 + - S3_ENDPOINT=http://minio:9000 + + # Upload Workers + upload-worker: + build: + context: . + dockerfile: Dockerfile.upload + deploy: + replicas: 2 + environment: + - REDIS_URL=redis://redis:6379 + + # Redis + redis: + image: redis:7-alpine + volumes: + - redis_data:/data + command: redis-server --appendonly yes + + # PostgreSQL + postgres: + image: postgres:15-alpine + environment: + - POSTGRES_USER=user + - POSTGRES_PASSWORD=pass + - POSTGRES_DB=castad + volumes: + - postgres_data:/var/lib/postgresql/data + + # MinIO (S3-compatible) + minio: + image: minio/minio + ports: + - "9000:9000" + - "9001:9001" + volumes: + - minio_data:/data + command: server /data --console-address ":9001" + +volumes: + redis_data: + postgres_data: + minio_data: +``` + +### Kubernetes 구조 (대규모) + +

+ Kubernetes Architecture +

+ +**Kubernetes 리소스:** +- **Ingress Controller**: nginx-ingress / Traefik (TLS 종료) +- **Deployments with HPA**: + - API Server: 2-10 pods (CPU 기반 auto-scaling) + - Render Worker: 3-20 pods (GPU 노드, 큐 길이 기반) + - Upload Worker: 2-5 pods +- **StatefulSets**: PostgreSQL (Primary + Replicas), Redis Cluster (3-node) +- **External Services**: AWS S3, Gemini API, Suno API, YouTube/Instagram/TikTok APIs + +### 마이그레이션 로드맵 + +| 단계 | 규모 | 주요 변경 | 예상 동시 사용자 | +|------|------|----------|-----------------| +| **1단계** | 현재 | Single Server + SQLite | ~50명 | +| **2단계** | 소규모 | Docker Compose + PostgreSQL + Redis | ~500명 | +| **3단계** | 중규모 | Kubernetes (3노드) + S3 | ~5,000명 | +| **4단계** | 대규모 | Multi-AZ K8s + Auto Scaling | ~50,000명+ | + +### 각 단계별 주요 작업 + +#### 2단계 (Docker Compose) +- [ ] SQLite → PostgreSQL 마이그레이션 +- [ ] Redis 도입 (세션, 캐시, 작업 큐) +- [ ] Bull Queue로 렌더링 작업 분리 +- [ ] MinIO로 파일 저장소 분리 +- [ ] Docker 이미지 빌드 및 compose 구성 + +#### 3단계 (Kubernetes) +- [ ] Helm Chart 작성 +- [ ] HPA(Horizontal Pod Autoscaler) 설정 +- [ ] PersistentVolume 구성 +- [ ] Ingress 설정 (TLS 포함) +- [ ] 모니터링 (Prometheus + Grafana) + +#### 4단계 (Multi-AZ) +- [ ] Multi-AZ PostgreSQL (RDS 또는 Cloud SQL) +- [ ] Redis Cluster (ElastiCache 또는 MemoryStore) +- [ ] CDN 설정 (CloudFront 또는 Cloud CDN) +- [ ] 글로벌 로드밸런싱 + +--- + +## 📅 버전 히스토리 + +### v3.7.0 - Background Render Queue & Platform Upload (Current) + +![Render Queue Architecture](public/images/render-queue-architecture.svg) + +- **🔄 백그라운드 렌더링 큐 시스템**: + - 페이지 이동/로그아웃해도 렌더링 작업 계속 진행 + - 작업 시작 시 크레딧 **선차감**, 실패 시 **자동 환불** + - 계정당 동시 렌더링 **1개** 제한 (서버 전체 **3개**) + - 실시간 진행률 폴링 및 완료 알림 + +| API | 설명 | +|-----|------| +| `POST /api/render/start` | 렌더링 작업 시작, jobId 즉시 반환 | +| `GET /api/render/status/:jobId` | 작업 상태/진행률 조회 | +| `GET /api/render/jobs` | 내 작업 목록 조회 | + +- **📱 Instagram 업로드 기능**: + - ResultPlayer에 Instagram 업로드 버튼 추가 + - LibraryView 드롭다운에 YouTube/Instagram 업로드 옵션 + - 계정 미연결 시 새 탭에서 설정 페이지 안내 + +- **💾 새 DB 테이블**: `render_jobs` + ```sql + render_jobs ( + id TEXT PRIMARY KEY, -- job_xxx_xxx + user_id, pension_id, + status TEXT, -- pending|processing|completed|failed + progress INTEGER, -- 0-100 + input_data TEXT, -- JSON + output_path TEXT, + credits_charged INTEGER, + credits_refunded INTEGER, + created_at, started_at, completed_at + ) + ``` + +- **🔧 프론트엔드 개선**: + - ResultPlayer: 큐 기반 렌더링, 폴링, "페이지를 나가도 렌더링 계속" 메시지 + - LibraryView: YouTube/Instagram 업로드 버튼 (DropdownMenu) + +### v3.6.0 - Instagram Crawling & Photo Management +- **📸 인스타그램 사진 크롤링**: + - 인스타그램 프로필 URL 입력 시 자동으로 사진 수집 + - 쿠키 기반 인증으로 공개/팔로우 계정 접근 + - 캐러셀 이미지 지원 (여러 장 게시물) +- **🔑 관리자 쿠키 관리**: + - 관리자 대시보드 > 설정에서 API 쿠키 관리 + - 네이버 지도 쿠키 / 인스타그램 쿠키 저장 + - DB 저장으로 서버 재시작 후에도 유지 +- **🗂️ 사진 소스별 분류**: + - 크롤링 시 소스 자동 구분: `naver` / `google` / `instagram` / `upload` + - 소스별 필터링 API 지원 (`?source=naver`) + - 소스별 통계 조회 API (`/images/stats`) +- **🔧 네이버 크롤링 안정화**: + - 네이버 GraphQL API 변경 대응 + - 지원되지 않는 필드 제거 (menuImages, bizImages 등) + - `images` + `cpImages`만 사용하도록 수정 + +### v3.5.0 - Enhanced Image Upload & Extended Capacity +- **📸 이미지 업로드 안정화**: + - `useRef` 기반 파일 입력으로 브라우저 호환성 개선 + - 파일 선택 후 업로드 안 되는 버그 수정 + - 호버 효과 추가로 UX 개선 +- **🖼️ 이미지 용량 대폭 확대** (15장 → 100장): + - 네이버 플레이스 크롤링: 최대 100장 + - 구글 지도 크롤링: 최대 100장 + - 수동 업로드: 최대 100장 +- **⭐ 업로드 이미지 우선순위**: + - 수동 업로드한 이미지가 목록 최상단에 배치 + - 업로드된 이미지 자동 선택 + - 랜덤 선택 시에도 업로드 이미지 우선 포함 +- **🌐 다국어 지원 업데이트**: + - 한국어, 영어, 일본어, 중국어, 태국어, 베트남어 번역 업데이트 + - "최대 100장" 텍스트 전 언어 반영 +- **🔧 Google OAuth 프로덕션 설정**: + - `BACKEND_URL` 환경변수 추가 필요 + - 프로덕션 리다이렉션 URI 가이드 문서화 + +### v3.4.0 - Business DNA & Smart Image Management +- **🧬 비즈니스 DNA 분석 시스템**: Google Search Grounding + Gemini 2.5 Flash + - 펜션 이름/URL 입력 시 AI가 자동으로 브랜드 DNA 분석 + - **톤앤매너 (Tone & Manner)**: 브랜드 커뮤니케이션 스타일 추출 + - **타겟 고객 (Target Customers)**: 주요/부차 타겟층, 연령대, 특성 분석 + - **브랜드 컬러 (Brand Colors)**: 이미지 기반 컬러 팔레트 자동 추출 + - **키워드 & 해시태그**: SEO/SNS 최적화 키워드 자동 생성 + - **시각적 스타일**: 인테리어, 외관, 분위기, 추천 사진 스타일 + - **차별화 포인트 (USP)**: 경쟁력 있는 고유 가치 제안 + - DNACard 컴포넌트로 시각적 결과 표시 +- **📸 스마트 이미지 크롤링 강화**: + - 네이버 플레이스 이미지 최대 100장 크롤링 (v3.5.0에서 확대) + - 다양한 이미지 소스 수집 (menuImages, bizImages, roomImages, fsasImages) + - 이미지 셔플 및 중복 제거 알고리즘 적용 +- **💾 펜션 이미지 영구 저장 시스템**: + - 크롤링/업로드된 이미지를 펜션 프로필에 자동 저장 + - `pension_images` 테이블로 이미지 메타데이터 관리 + - 펜션 선택 시 저장된 이미지 자동 로드 + - 이미지 추가/삭제 API 엔드포인트 +- **🎯 이미지 선택 UI 개선** (Beginner Mode): + - 전체 선택 / 개별 선택 / 랜덤 선택 모드 + - 체크박스 기반 직관적 이미지 선택 + - 선택된 이미지만으로 영상 생성 +- **📊 API 엔드포인트 추가**: + - `POST /api/profile/pension/:id/images` - 이미지 저장 + - `GET /api/profile/pension/:id/images` - 이미지 목록 조회 + - `DELETE /api/profile/pension/:id/images/:imageId` - 이미지 삭제 + - `POST /api/gemini/analyze-dna` - DNA 분석 + +### v3.3.0 - UX Level System +- **🎯 사용자 레벨 시스템**: 3단계 UX 레벨 도입 + - **쌩초보 (Beginner)**: "다 알아서 해줘" - 최소한의 옵션, 원클릭 생성 + - **중급 (Intermediate)**: "조금만 선택할게" - 장르/언어 선택, 기본 커스터마이징 + - **프로 (Pro)**: "내가 다 컨트롤" - 모든 옵션 제어, 축제 연동 +- **🤖 자동 업로드 시스템**: 레벨에 따른 자동 업로드 제어 + - Beginner/Intermediate: 강제 자동 업로드 (YouTube, Instagram, TikTok) + - Pro: 수동/자동 선택 가능 + - AI 기반 SEO 데이터 자동 생성 (제목, 설명, 태그, 해시태그) +- **⏰ 주간 자동 생성 스케줄러**: node-cron 기반 예약 생성 + - 요일/시간 선택 가능 + - 자동 큐 관리 및 순차 처리 + - 실패 시 자동 재시도 +- **🎨 조건부 UI 렌더링**: 레벨에 따른 메뉴 및 옵션 표시 + - Sidebar 메뉴 필터링 + - NewProjectView 단계 간소화 (Beginner: 2단계, Pro: 4단계) + - SettingsView에 레벨 변경 UI 추가 +- **📊 데이터베이스 확장**: + - `experience_level` 컬럼 추가 + - `auto_generation_settings` 테이블 (자동 생성 설정) + - `generation_queue` 테이블 (작업 큐) + - `upload_history` 테이블 (업로드 이력) + +### v3.2.1 - Instagram Service & Admin Dashboard Fixes +- **🔧 Instagram 서비스 안정화**: + - None 값 처리 개선 (`NoneType` has no attribute `strip` 에러 해결) + - 디버그 로깅 추가로 문제 진단 용이 + - instagrapi 라이브러리 2.1.2 → 2.2.1 업그레이드 +- **🚀 start.sh 개선**: + - Python 캐시 자동 삭제 (`.pyc`, `__pycache__`) + - 코드 변경 시 서비스 재시작 없이 즉시 반영 +- **🛡️ AdminDashboard 권한 체크 수정**: + - 사용자 정보 로딩 완료 후 권한 체크하도록 수정 + - 관리자 로그인 후 `/admin` 접근 시 리다이렉트 문제 해결 + +### v3.2.0 - Festival Integration + Billing & Analytics +- **🎪 축제 연동 시스템**: 한국관광공사 축제 데이터 통합 + - 실시간 축제 정보 조회 및 자동 동기화 + - 행정구역별 축제 그룹핑 (시/도 기준) + - 펜션 위치 기반 근처 축제 자동 추천 + - 선택한 축제 정보가 영상 콘텐츠에 자동 반영 +- **💰 Google Cloud Billing 연동**: BigQuery 기반 실시간 비용 추적 + - 서비스별/SKU별 비용 상세 분석 + - 일별/월별 비용 트렌드 차트 + - Gemini API 사용량 별도 집계 + - USD/KRW 환율 자동 적용 +- **📊 API 사용량 추적 시스템**: 서비스별 사용량 모니터링 + - Gemini, Suno, YouTube 등 서비스별 사용량 집계 + - 사용자별 API 호출 추적 (과금 대비) + - 모델별 성공/실패율 및 평균 지연시간 + - 월별 사용량 리포트 자동 생성 +- **🎯 AI 모델 우선순위 Fallback 시스템**: 지능적 모델 선택 + - Gemini 2.0 Flash → Gemini 2.5 Flash → Imagen 3 자동 전환 + - Rate Limit (429) 또는 서버 오류 (500) 시 자동 Fallback + - 모델별 비용 추정 및 최적화 +- **✨ AI 매직 라이트**: 펜션 컨셉 자동 생성 + - 펜션 정보 + 카테고리 + 축제 정보 기반 AI 작문 + - 2-3문장 매력적인 마케팅 컨셉 자동 생성 +- **🔐 개별 YouTube OAuth 2.0**: 사용자별 채널 연동 완성 + - 각 사용자가 자신의 YouTube 채널 연결 + - 연결된 채널로 직접 업로드 + - 연결 상태 관리 및 토큰 자동 갱신 +- **버그 수정**: + - Gemini API 500 오류 시 모델 자동 전환 + - YouTube 업로드 시 명확한 연결 상태 확인 + - 축제 데이터 날짜 포맷 정규화 + +### v3.1.0 - Custard Branding + Complete Manuals +- **커스터드(Custard) 브랜딩**: 🍮 테마로 전체 리브랜딩 + - CaStAD = Custard (카스타드) 철학 적용 + - SVG 로고: 라메킨에 담긴 커스터드 푸딩 + 스팀 애니메이션 + - 브랜드 스토리: 정밀함(달걀), 부드러움(우유), 달콤함(설탕) + - 컬러 팔레트: Custard Cream, Caramel Gold, Egg Yolk, Warm Amber +- **완전한 매뉴얼 시스템**: 5개 역할별 매뉴얼 완비 + - Part A: 개발자 매뉴얼 + - Part B: 운용자 매뉴얼 + - Part C: 사용자 매뉴얼 + - Part D: 마케터 매뉴얼 (신규) + - Part E: 영업사원 매뉴얼 (신규) +- **버그 수정**: AccountView 플랜 정보 동적 표시 + +### v3.0.0 - TikTok + Advanced Analytics +- **CaStAD 브랜딩**: 새로운 로고 및 전체 사이트 리브랜딩 +- **TikTok 연동**: OAuth 2.0 기반 TikTok Content Posting API 통합 + - Direct Post / Inbox(Draft) 업로드 지원 + - 업로드 히스토리 및 통계 +- **고급 통계 시스템**: 슈퍼어드민을 위한 종합 분석 대시보드 + - 사용자 성장, 영상 트렌드, 플랫폼 업로드 통계 + - 크레딧 사용 분석, 플랜 분포, 지역별 분포 + - 수익 예측, 시스템 헬스 모니터링 +- **영업용 매뉴얼**: 영업 담당자를 위한 상세 판매 가이드 +- **테스트 강화**: 39개 API 엔드포인트 종합 테스트 (92% 통과율) + +### v2.2.0 - Asset Management & Plan System +- **에셋 관리 시스템**: 사용자별 스토리지 할당 및 관리 +- **구독 플랜 시스템**: Free/Basic/Pro/Business 4단계 플랜 +- **관리자 플랜 수정**: 어드민에서 사용자별 플랜/크레딧 조정 +- **자동 에셋 저장**: 렌더링 시 생성된 모든 에셋 자동 저장 +- **향상된 테스트**: API 종합 테스트 스크립트 추가 + +### v2.1.0 - Enhanced Authentication & i18n +- **소셜 로그인**: Google/Naver OAuth 2.0 통합 +- **이메일 인증**: 회원가입 시 이메일 확인 필수 +- **비밀번호 재설정**: 이메일 기반 비밀번호 복구 +- **완전한 i18n**: UI 전체 6개 언어 지원 + +### v2.0.0 - Multi-Pension & YouTube Integration +- **다중 펜션 관리**: 1 계정 N 펜션 등록/관리 +- **YouTube OAuth 2.0**: 사용자 채널 직접 연동 +- **다국어 SEO**: 6개 언어 자동 메타데이터 생성 + +### v1.x - Foundation +- JWT 기반 인증 시스템 +- SQLite 데이터베이스 +- Puppeteer 서버사이드 렌더링 +- Gemini 2.5 Flash 통합 + +--- + +## 🔗 기술 참고 링크 + +### 공식 문서 +| 기술 | 링크 | +|------|------| +| React 19 | https://react.dev | +| Vite | https://vitejs.dev | +| Express.js | https://expressjs.com | +| Puppeteer | https://pptr.dev | +| FFmpeg | https://ffmpeg.org | + +### Google APIs +| API | 문서 링크 | +|-----|----------| +| Gemini API | https://ai.google.dev/docs | +| YouTube Data API | https://developers.google.com/youtube/v3 | +| OAuth 2.0 | https://developers.google.com/identity/protocols/oauth2 | + +--- + +## 📜 라이선스 + +MIT License - 자유롭게 사용, 수정, 배포 가능 + +--- + +## 👥 Contributors + +- **waabaa** - Lead Developer + +--- + +© 2025 CaStAD Team. All Rights Reserved. diff --git a/castad-data/SALES_MANUAL.md b/castad-data/SALES_MANUAL.md new file mode 100644 index 0000000..01aa5cf --- /dev/null +++ b/castad-data/SALES_MANUAL.md @@ -0,0 +1,360 @@ +# CaStAD (카스타드) 영업 매뉴얼 v3.0 + +## 솔루션 한 줄 소개 + +> **"네이버 플레이스 URL 하나로 15초만에 인스타그램 릴스, 틱톡, 유튜브 쇼츠 영상을 자동 생성하는 AI 마케팅 플랫폼"** + +--- + +## 목차 + +1. [솔루션 개요](#1-솔루션-개요) +2. [타겟 고객](#2-타겟-고객) +3. [핵심 가치 제안](#3-핵심-가치-제안) +4. [경쟁 우위](#4-경쟁-우위) +5. [요금제 안내](#5-요금제-안내) +6. [데모 시나리오](#6-데모-시나리오) +7. [예상 질문 및 답변 (FAQ)](#7-예상-질문-및-답변-faq) +8. [성공 사례](#8-성공-사례) +9. [계약 절차](#9-계약-절차) +10. [기술 지원 정책](#10-기술-지원-정책) + +--- + +## 1. 솔루션 개요 + +### CaStAD란? +CaStAD(카스타드)는 펜션, 풀빌라, 리조트 등 숙박업 사업자를 위한 **AI 기반 마케팅 영상 자동 생성 SaaS 플랫폼**입니다. + +### 핵심 기능 +| 기능 | 설명 | +|------|------| +| **AI 영상 자동 생성** | 네이버 플레이스 URL만 입력하면 사진, 리뷰 등을 분석하여 자동으로 홍보 영상 생성 | +| **AI 음악 생성** | 펜션 분위기에 맞는 배경음악 자동 생성 (Suno AI 연동) | +| **AI 카피라이팅** | Google Gemini AI가 감성적인 광고 문구 자동 생성 | +| **다국어 지원** | 한국어, 영어, 일본어, 중국어, 태국어, 베트남어 6개국어 | +| **3채널 동시 업로드** | YouTube, Instagram Reels, TikTok 원클릭 업로드 | +| **통합 분석 대시보드** | 모든 채널의 조회수, 참여율 등을 한눈에 확인 | + +### 주요 특징 +- **시간 절약**: 기존 2시간 → 15초로 영상 제작 시간 99% 단축 +- **비용 절감**: 영상 제작 외주비 월 50만원+ → 월 2.9만원부터 +- **전문가 수준**: AI가 최신 트렌드를 반영한 전문적인 영상 제작 +- **자동화**: 정기적인 업로드까지 완전 자동화 가능 + +--- + +## 2. 타겟 고객 + +### 주요 타겟 +| 업종 | 예시 | 니즈 | +|------|------|------| +| **펜션** | 전국 2만+ 펜션 | 저렴하고 쉬운 마케팅 | +| **풀빌라** | 프리미엄 풀빌라 | 고급스러운 영상 콘텐츠 | +| **리조트/호텔** | 중소형 리조트 | 일관된 브랜딩 영상 | +| **글램핑** | 글램핑장 | 감성적인 분위기 전달 | +| **게스트하우스** | 외국인 대상 숙소 | 다국어 마케팅 | + +### 이상적인 고객 프로필 +- 네이버 플레이스에 등록된 사업자 +- SNS 마케팅 필요성을 인지하지만 시간/역량 부족 +- 마케팅 예산이 제한적인 개인 사업자 +- 여러 펜션을 운영하는 법인 사업자 + +### 고객 Pain Point +1. "영상 만들 시간이 없어요" +2. "영상 편집할 줄 몰라요" +3. "외주 맡기기엔 너무 비싸요" +4. "SNS에 올리는 것도 귀찮아요" +5. "어떤 콘텐츠가 효과적인지 모르겠어요" + +--- + +## 3. 핵심 가치 제안 + +### Value Proposition Canvas + +**고객 문제 (Pains)** +- 영상 제작 시간 부족 +- 편집 기술 부족 +- 높은 외주 비용 +- 일관성 없는 마케팅 + +**솔루션 제공 (Gains)** +- 15초 만에 영상 완성 +- 기술 필요 없음 +- 월 2.9만원부터 +- AI가 일관된 품질 보장 + +### ROI 계산 예시 + +**현재 비용 (외주 의뢰 시)** +- 영상 제작: 30만원/건 +- 월 2개 제작: 60만원 +- 연간: 720만원 + +**CaStAD 사용 시 (Pro 플랜)** +- 월 요금: 9.9만원 +- 연간: 118.8만원 +- **절감 효과: 연 600만원 이상** + +--- + +## 4. 경쟁 우위 + +### 경쟁사 비교표 + +| 항목 | CaStAD | A사 (영상 편집 앱) | B사 (마케팅 대행) | +|------|--------|------------------|------------------| +| 자동화 수준 | ★★★★★ | ★★☆☆☆ | ★☆☆☆☆ | +| 가격 | ★★★★★ | ★★★☆☆ | ★☆☆☆☆ | +| 숙박업 특화 | ★★★★★ | ★☆☆☆☆ | ★★★☆☆ | +| AI 음악 생성 | ★★★★★ | ☆☆☆☆☆ | ☆☆☆☆☆ | +| 3채널 통합 | ★★★★★ | ☆☆☆☆☆ | ★★★☆☆ | +| 다국어 지원 | ★★★★★ | ★☆☆☆☆ | ★★☆☆☆ | + +### 우리만의 차별점 + +1. **숙박업 완전 특화** + - 네이버 플레이스 데이터 자동 추출 + - 숙박업에 최적화된 AI 프롬프트 + - 업종별 최적화된 템플릿 + +2. **완전 자동화** + - URL 입력 → 영상 생성 → SNS 업로드까지 원스톱 + - 예약 업로드 기능으로 정기 마케팅 자동화 + +3. **AI 음악 생성** + - 로열티 프리 음악 걱정 없음 + - 펜션 분위기에 맞춤 생성 + +4. **합리적인 가격** + - 영상 1개 비용 = 커피 한잔 가격 + +--- + +## 5. 요금제 안내 + +### 요금제 비교 + +| 항목 | Free | Basic | Pro | Business | +|------|------|-------|-----|----------| +| 월 요금 | 무료 | ₩29,000 | ₩99,000 | ₩299,000 | +| 월 크레딧 | 10회 | 15회 | 75회 | 무제한 | +| 관리 펜션 | 1개 | 1개 | 5개 | 무제한 | +| YouTube 업로드 | ✓ | ✓ | ✓ | ✓ | +| Instagram 업로드 | - | ✓ | ✓ | ✓ | +| TikTok 업로드 | - | ✓ | ✓ | ✓ | +| 다국어 콘텐츠 | - | - | ✓ | ✓ | +| 프리미엄 템플릿 | - | - | ✓ | ✓ | +| 전담 매니저 | - | - | - | ✓ | +| 분석 리포트 | - | - | 주간 | 일간 | + +### 타겟별 추천 플랜 + +| 고객 유형 | 추천 플랜 | 이유 | +|----------|----------|------| +| 개인 펜션 운영자 | Basic | 1개 펜션 관리에 충분한 기능 | +| 2-5개 펜션 운영 법인 | Pro | 다중 펜션 관리 + 다국어 | +| 리조트/체인 | Business | 무제한 + 전담 지원 | +| 시작 테스트 | Free | 무료로 기능 체험 | + +### 업셀링 포인트 + +**Free → Basic** +- "월 10회로 부족하시죠? 15회로 늘리면서 Instagram까지!" + +**Basic → Pro** +- "펜션 추가하셨나요? Pro면 5개까지 관리 가능!" +- "외국인 손님 많으시죠? 다국어로 글로벌 마케팅!" + +**Pro → Business** +- "월 75회 이상 필요하시면 무제한이 경제적!" +- "전담 매니저가 성과 분석까지 도와드립니다!" + +--- + +## 6. 데모 시나리오 + +### 5분 데모 시나리오 + +**1단계: 문제 제기 (30초)** +> "사장님, 요즘 인스타 릴스나 틱톡 마케팅 하고 계신가요? 하시려면 영상을 만들어야 하는데 시간도 없고 어렵죠?" + +**2단계: 솔루션 소개 (30초)** +> "저희 CaStAD는 네이버 플레이스 URL만 넣으면 자동으로 영상이 만들어집니다. 한번 보시겠어요?" + +**3단계: 실시간 데모 (2분)** +1. 고객 펜션의 네이버 플레이스 URL 복사 +2. CaStAD에 붙여넣기 +3. 15초 후 영상 생성 완료 +4. 영상 미리보기 및 다운로드 + +**4단계: 기능 설명 (1분)** +> "이 영상이 자동으로 만들어졌습니다. 음악도 AI가 만든 거예요. +> 여기서 바로 유튜브, 인스타, 틱톡에 업로드할 수 있어요." + +**5단계: 클로징 (30초)** +> "지금 무료 체험 가입하시면 10회까지 무료로 사용 가능합니다. +> 한번 사용해보시고 효과 있으시면 Basic 플랜 추천드릴게요." + +### 핵심 데모 포인트 + +1. **고객의 실제 펜션으로 데모** → 즉각적인 공감 +2. **15초 생성 시간 강조** → "이게 15초 만에요?" +3. **음악도 AI 생성** → "음악도 따로 안 구해도 돼요?" +4. **원클릭 업로드** → "바로 올릴 수 있네요!" + +--- + +## 7. 예상 질문 및 답변 (FAQ) + +### 제품 관련 + +**Q: 영상 품질이 어느 정도예요?** +> A: 1080p Full HD로 제작되며, 인스타 릴스나 틱톡에 올리기에 전문가 수준입니다. 실제 데모 영상을 보여드릴까요? + +**Q: 네이버 플레이스가 없으면 못 써요?** +> A: 직접 사진 업로드도 가능합니다. 다만 네이버 플레이스가 있으면 리뷰, 정보가 자동 추출되어 더 풍부한 영상이 만들어져요. + +**Q: 음악 저작권 문제는 없나요?** +> A: 모든 음악이 Suno AI로 자동 생성되어 저작권 문제가 전혀 없습니다. 상업적 사용도 100% 가능해요. + +**Q: 영상 수정할 수 있어요?** +> A: 마음에 안 드시면 다른 스타일로 다시 생성하실 수 있어요. 생성 횟수 내에서 무제한 재생성 가능합니다. + +### 가격 관련 + +**Q: 왜 월 구독제예요?** +> A: AI 서버 운영 비용이 들기 때문에 구독제로 운영됩니다. 대신 영상 1개당 비용으로 계산하면 건당 2,000원도 안 돼요. + +**Q: 계약 기간은요?** +> A: 월 단위 구독이라 언제든 해지 가능합니다. 연 결제 시 20% 할인 혜택도 있어요. + +**Q: 크레딧이 남으면 이월되나요?** +> A: 죄송하지만 미사용 크레딧은 이월되지 않습니다. 대신 크레딧 추가 구매가 가능해요. + +### 기술 관련 + +**Q: 설치해야 해요?** +> A: 웹 기반이라 설치 필요 없습니다. 크롬 브라우저만 있으면 PC, 모바일 어디서든 사용 가능해요. + +**Q: 인스타 연동이 안전한가요?** +> A: 비밀번호는 암호화되어 저장되고, 원하시면 언제든 연결 해제 가능합니다. 인스타 공식 연동 방식도 지원해요. + +**Q: 여러 명이 함께 쓸 수 있어요?** +> A: Business 플랜에서 팀 계정 기능을 지원합니다. 직원분들과 함께 사용 가능해요. + +--- + +## 8. 성공 사례 + +### 사례 1: 제주 오션뷰 펜션 +> "인스타 팔로워가 한 달 만에 3배 늘었어요. +> 매주 릴스 올리는데 10분도 안 걸려요." +> +> - 월 영상 제작: 15개 +> - 예약률 증가: 25% +> - 팔로워 증가: 850명 → 2,400명 + +### 사례 2: 강원도 풀빌라 체인 (5개 지점) +> "각 지점 개별로 마케팅하는 게 힘들었는데, +> 이제 한 곳에서 5개 다 관리해요." +> +> - 관리 시간: 주 10시간 → 1시간 +> - 월 콘텐츠: 5개 → 50개 +> - 마케팅 비용: 200만원 → 10만원 + +### 사례 3: 경기도 글램핑장 +> "틱톡에서 영상 하나가 10만뷰 나왔어요. +> 외국인 예약이 갑자기 늘어났어요." +> +> - 틱톡 조회수: 평균 1,000 → 15,000 +> - 외국인 예약: 10% → 35% +> - 네이버 플레이스 순위: 8위 → 2위 + +--- + +## 9. 계약 절차 + +### 신규 계약 프로세스 + +``` +1. 무료 체험 + ↓ +2. 데모 및 상담 + ↓ +3. 플랜 선택 + ↓ +4. 결제 및 계정 생성 + ↓ +5. 초기 설정 지원 + ↓ +6. 정기 영상 생성 시작 +``` + +### 필요 서류 +- 사업자등록증 (세금계산서 발행 시) +- 결제 카드 정보 (자동결제) + +### 결제 방법 +- 신용카드 (자동결제) +- 계좌이체 (연 결제 시) +- 세금계산서 발행 가능 + +--- + +## 10. 기술 지원 정책 + +### 지원 채널 + +| 플랜 | 이메일 | 채팅 | 전화 | 전담 매니저 | +|------|--------|------|------|------------| +| Free | ✓ (48h) | - | - | - | +| Basic | ✓ (24h) | ✓ | - | - | +| Pro | ✓ (12h) | ✓ | ✓ | - | +| Business | ✓ (4h) | ✓ | ✓ | ✓ | + +### 지원 범위 +- 계정 및 결제 문의 +- 기능 사용 방법 +- SNS 연동 문제 +- 영상 생성 오류 +- 비즈니스 플랜: 마케팅 전략 컨설팅 포함 + +### 응답 시간 보장 +- Business 플랜: 4시간 내 응답 보장 +- 긴급 이슈: 1시간 내 처리 + +--- + +## 영업 자료 체크리스트 + +### 필수 준비물 +- [ ] 데모 계정 (관리자 권한) +- [ ] 노트북 + 인터넷 (데모용) +- [ ] 명함 및 회사 소개서 +- [ ] 요금표 출력물 +- [ ] 계약서 양식 + +### 현장 체크 +- [ ] 고객 네이버 플레이스 URL 확인 +- [ ] 현재 마케팅 방법 파악 +- [ ] 예산 및 결정권자 확인 +- [ ] 경쟁사 사용 여부 확인 +- [ ] 다음 미팅 일정 잡기 + +--- + +## 연락처 + +- **영업 문의**: sales@castad.io +- **기술 지원**: support@castad.io +- **파트너십**: partners@castad.io + +--- + +*이 매뉴얼은 영업팀 내부용입니다. 외부 유출을 금합니다.* + +**마지막 업데이트: 2025년 12월** +**버전: 3.0.0** diff --git a/castad-data/SERVER_DEPLOY_GUIDE.md b/castad-data/SERVER_DEPLOY_GUIDE.md new file mode 100644 index 0000000..d4ddcc0 --- /dev/null +++ b/castad-data/SERVER_DEPLOY_GUIDE.md @@ -0,0 +1,21 @@ +#!/bin/bash + +# 배포 가이드 (서버에서 실행하세요) + +# 1. 압축 해제 +# tar -xzvf bizvibe_deploy.tar.gz + +# 2. 필수 패키지 설치 (Ubuntu 기준) +# sudo apt-get update && sudo apt-get install -y ffmpeg fonts-noto-cjk + +# 3. 의존성 설치 및 빌드 +# ./deploy.sh + +# 4. 환경 변수 설정 +# .env 파일을 열어 API 키들을 확인하고 채워주세요. +# 특히 SUNO_API_KEY, VITE_GEMINI_API_KEY 확인 필수! + +# 5. 서버 실행 +# ./start.sh + +echo "배포 준비 완료! bizvibe_deploy.tar.gz 파일을 서버로 전송하세요." diff --git a/castad-data/USER_MANUAL.md b/castad-data/USER_MANUAL.md new file mode 100644 index 0000000..7859a61 --- /dev/null +++ b/castad-data/USER_MANUAL.md @@ -0,0 +1,80 @@ +# ADo4 사용자 매뉴얼 (User Manual) + +**ADo4**는 AI 기술을 활용하여 누구나 쉽게 전문가 수준의 마케팅 비디오를 만들 수 있는 도구입니다. 이 매뉴얼을 따라 단계별로 영상을 만들어보세요. + +--- + +## 1. 시작하기 (메인 화면) + +서비스에 접속하면 가장 먼저 보게 되는 메인 대시보드입니다. 왼쪽은 **데이터 입력 영역**, 오른쪽은 **설정 영역**으로 구성되어 있습니다. + +### 1-1. 업체 정보 입력 (자동 검색 기능) + +가장 쉬운 방법은 **[정보 자동 검색]** 기능을 활용하는 것입니다. + +* **Google Maps 검색:** '지도 검색 (Global)' 입력창에 `지역명 + 상호명` (예: "강남 스타벅스", "군산 이성당")을 입력하고 **[검색]** 버튼을 누르세요. +* **Naver 플레이스:** 네이버 지도 URL이 있다면 붙여넣고 **[가져오기]**를 누르면 더욱 정확한 한국형 데이터를 가져올 수 있습니다. + +> 💡 **Tip:** 자동 검색을 사용하면 업체명, 설명, 대표 사진(AI가 선별한 베스트 컷)이 자동으로 채워집니다. + +--- + +## 2. 세부 설정 (Customization) + +자동으로 입력된 정보를 확인하고, 원하는 스타일로 다듬어 보세요. + +### 2-1. 프로젝트 설정 +* **브랜드 이름 & 설명:** 자동 입력된 내용을 수정하거나 직접 입력합니다. 이곳에 적힌 내용(분위기, 특징)을 바탕으로 AI가 가사를 씁니다. +* **화면 비율:** + * `16:9`: 유튜브, TV, 모니터용 (가로형) + * `9:16`: 인스타그램 릴스, 틱톡, 유튜브 쇼츠용 (세로형) +* **비주얼 스타일:** + * `슬라이드`: 사진들이 부드럽게 전환되는 슬라이드쇼 영상 (추천) + * `AI 비디오`: 사진을 바탕으로 AI가 움직이는 영상을 생성 (생성 시간 김) + +### 2-2. 오디오 및 자막 설정 +* **오디오 모드:** + * `노래 (Song)`: 나만의 오리지널 CM송을 작곡합니다. + * `성우 (Narration)`: 차분하고 신뢰감 있는 성우 목소리로 읽어줍니다. +* **장르 선택:** 팝, 발라드, 재즈, 힙합 등 원하는 분위기를 고르세요. +* **자막 스타일:** 영상 위에 입혀질 자막의 디자인(네온, 타자기 등)을 선택하세요. + +--- + +## 3. 영상 생성 시작 + +모든 설정이 끝났다면, 우측 하단의 **[영상 생성 시작]** 버튼을 클릭하세요. + +> ⏳ **소요 시간:** 약 1~2분 정도 소요됩니다. +> AI가 시나리오 작성 -> 작곡 -> 이미지 검수 -> 영상 합성을 진행하는 동안 잠시만 기다려주세요. + +--- + +## 4. 결과 확인 및 저장 (플레이어 화면) + +영상이 완성되면 자동으로 **결과 플레이어** 화면이 나타납니다. + +### 4-1. 영상 재생 +* 가운데 **[재생 ▶]** 버튼을 눌러 완성된 영상을 감상해 보세요. +* 가사나 광고 문구가 음악에 맞춰 화면에 나타나는 것을 볼 수 있습니다. + +### 4-2. 영상 저장 및 활용 +* **[영상 저장 (텍스트 효과 포함)]:** 완성된 고화질 MP4 파일을 내 컴퓨터로 다운로드합니다. 동시에 서버에서 **YouTube 자동 업로드** 준비가 진행됩니다. +* **[YouTube 업로드]:** (영상 저장 후 활성화됨) 클릭 한 번으로 내 유튜브 채널에 영상을 업로드합니다. 업로드가 완료되면 **[보러가기]** 버튼이 나타납니다. +* **[빠른 저장 (효과X)]:** 자막 효과 없이 원본 영상과 음악만 빠르게 합쳐서 저장하고 싶을 때 사용합니다. + +--- + +## ❓ 자주 묻는 질문 (FAQ) + +**Q. 영상 생성이 실패했어요.** +A. 일시적인 네트워크 문제일 수 있습니다. 잠시 후 다시 시도해 주세요. 계속 실패한다면 입력한 이미지의 용량이 너무 크거나(개당 10MB 이상), 인터넷 연결이 불안정할 수 있습니다. + +**Q. 유튜브 업로드가 안 돼요.** +A. 최초 1회 인증이 필요합니다. 관리자에게 문의하거나 서버 설정 가이드를 참고하여 구글 계정 인증을 완료해주세요. + +**Q. 세로 영상(9:16)인데 사진이 잘려요.** +A. AI가 자동으로 최적의 구도를 찾지만, 가로로 긴 사진은 양옆이 잘릴 수밖에 없습니다. 되도록 세로로 찍은 사진을 업로드하거나, AI에게 맡겨주세요. + +--- +© 2025 ADo4. All Rights Reserved. diff --git a/castad-data/components.json b/castad-data/components.json new file mode 100644 index 0000000..c08a2b3 --- /dev/null +++ b/castad-data/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/styles/globals.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/src/components", + "utils": "@/src/lib/utils", + "ui": "@/src/components/ui", + "lib": "@/src/lib", + "hooks": "@/src/hooks" + } +} diff --git a/castad-data/components/ApiKeySelector.tsx b/castad-data/components/ApiKeySelector.tsx new file mode 100644 index 0000000..9b4e73a --- /dev/null +++ b/castad-data/components/ApiKeySelector.tsx @@ -0,0 +1,150 @@ +import React, { useState, useEffect } from 'react'; +import { Key } from 'lucide-react'; + +/** + * ApiKeySelector 컴포넌트의 props 정의 + * @interface ApiKeySelectorProps + * @property {(key?: string) => void} onKeySelected - API 키가 성공적으로 선택되거나 입력되었을 때 호출될 콜백 함수. (선택된 키를 인자로 받을 수 있음) + * @property {string | null} [initialError] - 초기 에러 메시지 (선택 사항) + */ +interface ApiKeySelectorProps { + onKeySelected: (key?: string) => void; + initialError?: string | null; +} + +/** + * API 키 선택 및 입력 컴포넌트 + * Gemini 및 Veo 모델 사용을 위해 Google Cloud Project의 API 키를 설정하도록 안내하고, + * AI Studio 환경 또는 수동 입력 방식을 제공합니다. + */ +const ApiKeySelector: React.FC = ({ onKeySelected, initialError }) => { + const [loading, setLoading] = useState(false); // 로딩 상태 관리 + const [error, setError] = useState(initialError || null); // 에러 메시지 관리 + const [manualKey, setManualKey] = useState(''); // 수동으로 입력된 API 키 + const [isAiStudio, setIsAiStudio] = useState(false); // 현재 환경이 Google AI Studio인지 여부 + + /** + * 환경 체크 및 초기 API 키 확인 + * 컴포넌트 마운트 시 Google AI Studio 환경인지 확인하고, 이미 API 키가 선택되어 있는지 검사합니다. + */ + const checkKey = async () => { + try { + // window 객체에 aistudio 속성이 있는지 확인하여 AI Studio 환경을 감지합니다. + if ((window as any).aistudio) { + setIsAiStudio(true); + // AI Studio에서 이미 API 키가 선택되어 있는지 확인합니다. + const hasKey = await (window as any).aistudio.hasSelectedApiKey(); + if (hasKey && !initialError) { + // 키가 이미 선택되어 있고 초기 에러가 없으면 onKeySelected 콜백을 호출하여 앱 시작 + onKeySelected(); + } + } + } catch (e) { + console.error("API 키 확인 중 오류 발생:", e); + // 오류가 발생해도 isAiStudio 상태는 유지 + } + }; + + // 컴포넌트가 처음 렌더링될 때 한 번만 checkKey 함수 실행 + useEffect(() => { + checkKey(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + /** + * AI Studio에서 API 키 선택 핸들러 + * AI Studio의 `openSelectKey()` 함수를 호출하여 API 키 선택 UI를 띄웁니다. + */ + const handleSelectKey = async () => { + setLoading(true); // 로딩 상태 시작 + setError(null); // 기존 에러 메시지 초기화 + try { + if((window as any).aistudio) { + await (window as any).aistudio.openSelectKey(); // AI Studio 키 선택 대화 상자 열기 + onKeySelected(); // 키 선택 성공 시 콜백 호출 + } + } catch (e: any) { + console.error("API 키 선택 중 오류 발생:", e); + // 특정 에러 메시지에 따라 사용자에게 더 명확한 안내 제공 + if (e.message && e.message.includes("Requested entity was not found")) { + setError("유효하지 않은 프로젝트/키가 선택되었습니다. 다시 시도해주세요."); + } else { + setError("API 키 선택에 실패했습니다. 다시 시도해주세요."); + } + } finally { + setLoading(false); // 로딩 상태 종료 + } + }; + + /** + * 수동 API 키 입력 폼 제출 핸들러 + * 사용자가 직접 입력한 API 키를 검증하고 콜백 함수를 호출합니다. + */ + const handleManualSubmit = (e: React.FormEvent) => { + e.preventDefault(); // 폼 기본 제출 동작 방지 + if(!manualKey.trim()) { + setError("유효한 API 키를 입력해주세요."); // 빈 값일 경우 에러 메시지 + return; + } + onKeySelected(manualKey.trim()); // 유효한 키가 입력되면 콜백 호출 + }; + + return ( +
+
+
+
+ +
+
+ +

API 키 필요

+

+ Veo 및 Gemini로 고품질 뮤직 비디오를 생성하려면, 결제 가능한 Google Cloud 프로젝트의 API 키를 선택해주세요. +

+ + {error && ( +
+ {error} +
+ )} + + {isAiStudio ? ( + // AI Studio 환경일 경우 API 키 선택 버튼 제공 + + ) : ( + // 일반 브라우저 환경일 경우 수동 API 키 입력 폼 제공 +
+ setManualKey(e.target.value)} + placeholder="Gemini API 키 입력" + className="w-full p-3 rounded-xl bg-white/10 border border-white/20 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-purple-500" + /> + +
+ )} + + +
+
+ ); +}; + +export default ApiKeySelector; \ No newline at end of file diff --git a/castad-data/components/CaStADLogo.tsx b/castad-data/components/CaStADLogo.tsx new file mode 100644 index 0000000..a241056 --- /dev/null +++ b/castad-data/components/CaStADLogo.tsx @@ -0,0 +1,100 @@ +import React from 'react'; + +interface CaStADLogoProps { + size?: 'sm' | 'md' | 'lg' | 'xl'; + showText?: boolean; + className?: string; +} + +const sizeClasses = { + sm: { icon: 'w-8 h-8', text: 'text-lg' }, + md: { icon: 'w-12 h-12', text: 'text-xl' }, + lg: { icon: 'w-16 h-16', text: 'text-2xl' }, + xl: { icon: 'w-24 h-24', text: 'text-4xl' }, +}; + +// Main CaStAD Logo with custard pudding icon +export const CaStADLogo: React.FC = ({ + size = 'md', + showText = true, + className +}) => { + const { icon, text } = sizeClasses[size]; + + return ( +
+ {/* Custard Pudding SVG Icon */} +
+ CaStAD +
+ {showText && ( +
+ + CaStAD + + + 카스타드 + +
+ )} +
+ ); +}; + +// Inline version with custard cream colors +export const CaStADLogoInline: React.FC = ({ + size = 'md', + className +}) => { + const { icon, text } = sizeClasses[size]; + + return ( +
+ {/* Custard emoji as simple icon */} + + 🍮 + + + CaStAD + +
+ ); +}; + +// Icon only version +export const CaStADIcon: React.FC<{ size?: 'sm' | 'md' | 'lg' | 'xl'; className?: string }> = ({ + size = 'md', + className +}) => { + const { icon } = sizeClasses[size]; + + return ( +
+ CaStAD +
+ ); +}; + +// Text only gradient version +export const CaStADText: React.FC<{ size?: 'sm' | 'md' | 'lg' | 'xl'; className?: string }> = ({ + size = 'md', + className +}) => { + const { text } = sizeClasses[size]; + + return ( + + CaStAD + + ); +}; + +export default CaStADLogo; diff --git a/castad-data/components/InputForm.tsx b/castad-data/components/InputForm.tsx new file mode 100644 index 0000000..0fc5dc5 --- /dev/null +++ b/castad-data/components/InputForm.tsx @@ -0,0 +1,1169 @@ +/// + +import React, { useState, ChangeEvent, useEffect } from 'react'; +import { Upload, Music, Sparkles, X, Volume2, Search, MapPin, Loader2, Type, Globe, Video, Monitor, Wand2, Layers, FileText, Home, Waves, Mountain, Heart, Users, Dog, Tent, Building, CheckCircle, Lock, ChevronDown, Image as ImageIcon, ExternalLink } from 'lucide-react'; +import { BusinessInfo, MusicGenre, MusicDuration, AudioMode, TTSConfig, TTSGender, TTSAge, TTSTone, TextEffect, AspectRatio, Language, TransitionEffect, PensionCategory } from '../types'; +import { filterBestImages, enrichDescriptionWithReviews, extractTextEffectFromImage } from '../services/geminiService'; +import { crawlNaverPlace } from '../services/naverService'; +import { searchPlaceDetails, fetchPlacePhoto } from '../services/googlePlacesService'; +import { useLanguage } from '../src/contexts/LanguageContext'; +import OnboardingTour from './OnboardingTour'; + +// shadcn/ui components +import { cn } from '../src/lib/utils'; +import { Button } from '../src/components/ui/button'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../src/components/ui/card'; +import { Input } from '../src/components/ui/input'; +import { Label } from '../src/components/ui/label'; +import { Textarea } from '../src/components/ui/textarea'; +import { Badge } from '../src/components/ui/badge'; +import { Separator } from '../src/components/ui/separator'; +import { Switch } from '../src/components/ui/switch'; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../src/components/ui/collapsible'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../src/components/ui/tooltip'; + +interface InputFormProps { + onSubmit: (info: BusinessInfo) => void; + isSubmitting: boolean; +} + +// 국가별 장르 매핑 +const GENRE_BY_LANG: Record = { + 'KO': ['Auto', 'K-Pop', 'Trot', 'Ballad', 'Hip-Hop', 'R&B', 'EDM', 'Jazz', 'Rock'], + 'EN': ['Auto', 'Pop', 'Country', 'Hip-Hop', 'R&B', 'EDM', 'Jazz', 'Rock', 'Ballad'], + 'JP': ['Auto', 'J-Pop', 'Enka', 'Anime', 'Rock', 'Jazz', 'Ballad', 'EDM', 'Hip-Hop'], + 'CN': ['Auto', 'C-Pop', 'Mandopop', 'Traditional CN', 'Ballad', 'Hip-Hop', 'EDM', 'Jazz', 'Rock'], + 'TH': ['Auto', 'T-Pop', 'Luk Thung', 'Pop', 'Hip-Hop', 'Rock', 'EDM', 'Jazz', 'Ballad'], + 'VN': ['Auto', 'V-Pop', 'Bolero', 'Pop', 'Hip-Hop', 'Rock', 'EDM', 'Jazz', 'Ballad'] +}; + +// 펜션 카테고리 옵션 +const PENSION_CATEGORIES: { id: PensionCategory; icon: any; labelKey: string }[] = [ + { id: 'PoolVilla', icon: Waves, labelKey: 'catPoolVilla' }, + { id: 'OceanView', icon: Waves, labelKey: 'catOceanView' }, + { id: 'Mountain', icon: Mountain, labelKey: 'catMountain' }, + { id: 'Private', icon: Home, labelKey: 'catPrivate' }, + { id: 'Couple', icon: Heart, labelKey: 'catCouple' }, + { id: 'Family', icon: Users, labelKey: 'catFamily' }, + { id: 'Pet', icon: Dog, labelKey: 'catPet' }, + { id: 'Glamping', icon: Tent, labelKey: 'catGlamping' }, + { id: 'Traditional', icon: Building, labelKey: 'catTraditional' }, +]; + +const FLAG_CLASSES: Record = { + 'KO': 'fi-kr', + 'EN': 'fi-us', + 'JP': 'fi-jp', + 'CN': 'fi-cn', + 'TH': 'fi-th', + 'VN': 'fi-vn' +}; + +const InputForm: React.FC = ({ onSubmit, isSubmitting }) => { + const { language: uiLanguage, t } = useLanguage(); + + // 온보딩 투어 상태 + const [showTour, setShowTour] = useState(false); + + useEffect(() => { + const hasSeenTour = localStorage.getItem('castadTourCompleted'); + if (!hasSeenTour) { + setTimeout(() => setShowTour(true), 1000); + } + }, []); + + // 콘텐츠 생성 언어 - UI 언어와 동기화 + const [contentLanguage, setContentLanguage] = useState(uiLanguage); + + // Sync content language with UI language + useEffect(() => { + setContentLanguage(uiLanguage); + }, [uiLanguage]); + + // 펜션 카테고리 (복수 선택) + const [pensionCategories, setPensionCategories] = useState([]); + + // 상태 관리 + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const [address, setAddress] = useState(''); + const [category, setCategory] = useState(''); + + const [images, setImages] = useState([]); + const [previews, setPreviews] = useState([]); + + const [searchQuery, setSearchQuery] = useState(''); + const [isSearching, setIsSearching] = useState(false); + const [searchStatus, setSearchStatus] = useState(''); + const [mapLink, setMapLink] = useState(null); + + const [naverUrl, setNaverUrl] = useState(''); + const [isCrawling, setIsCrawling] = useState(false); + const [crawlStatus, setCrawlStatus] = useState(''); + + const [audioMode, setAudioMode] = useState('Song'); + const [musicGenre, setMusicGenre] = useState('Auto'); + const [musicDuration, setMusicDuration] = useState('Short'); + const [activeGenres, setActiveGenres] = useState(GENRE_BY_LANG['KO']); + + const [ttsConfig, setTTSConfig] = useState({ + gender: 'Female', + age: 'Young', + tone: 'Bright' + }); + + const [visualStyle, setVisualStyle] = useState<'Video' | 'Slideshow'>('Slideshow'); + const [transitionEffect, setTransitionEffect] = useState('Mix'); + const [aspectRatio, setAspectRatio] = useState('9:16'); + const [textEffect, setTextEffect] = useState('Cinematic'); + const [customStyle, setCustomStyle] = useState(''); + const [isAnalyzingStyle, setIsAnalyzingStyle] = useState(false); + const [useAiImages, setUseAiImages] = useState(false); + + // Collapsible states + const [openSections, setOpenSections] = useState({ + category: true, + images: true, + autoSearch: false, + basicInfo: true, + settings: true, + textStyle: true, + audio: true + }); + + useEffect(() => { + setActiveGenres(GENRE_BY_LANG[contentLanguage]); + setMusicGenre('Auto'); + }, [contentLanguage]); + + const textEffects: { id: TextEffect; label: string; desc: string; previewClass?: string }[] = [ + { id: 'Cinematic', label: t('textStyle') === '자막 스타일' ? '영화 같은' : 'Cinematic', desc: 'Fade', previewClass: 'font-serif tracking-widest' }, + { id: 'Neon', label: t('textStyle') === '자막 스타일' ? '네온 사인' : 'Neon', desc: 'Glow', previewClass: 'effect-neon font-bold text-pink-500' }, + { id: 'Glitch', label: t('textStyle') === '자막 스타일' ? '글리치' : 'Glitch', desc: 'Noise', previewClass: 'effect-glitch font-mono font-bold' }, + { id: 'Typewriter', label: t('textStyle') === '자막 스타일' ? '타자기' : 'Typewriter', desc: 'Cursor', previewClass: 'font-mono border-r-2 border-white pr-1' }, + { id: 'Bold', label: t('textStyle') === '자막 스타일' ? '임팩트' : 'Bold', desc: 'Strong', previewClass: 'font-black uppercase italic' }, + { id: 'Motion', label: t('textStyle') === '자막 스타일' ? '모션' : 'Motion', desc: 'Dynamic', previewClass: 'effect-motion font-bold italic' }, + { id: 'LineReveal', label: t('textStyle') === '자막 스타일' ? '라인 등장' : 'Line Reveal', desc: 'Minimal', previewClass: 'border-t border-b border-white py-1' }, + { id: 'Boxed', label: t('textStyle') === '자막 스타일' ? '박스' : 'Boxed', desc: 'Frame', previewClass: 'border border-white p-1' }, + { id: 'Elegant', label: t('textStyle') === '자막 스타일' ? '우아함' : 'Elegant', desc: 'Classic', previewClass: 'font-serif italic' }, + { id: 'BlockReveal', label: t('textStyle') === '자막 스타일' ? '블록 등장' : 'Block Reveal', desc: 'Pop', previewClass: 'bg-white text-black px-1' }, + { id: 'Custom', label: t('textStyle') === '자막 스타일' ? '나만의 스타일' : 'Custom', desc: 'AI', previewClass: '' }, + ]; + + const transitionOptions: { id: TransitionEffect; labelKey: any }[] = [ + { id: 'Mix', labelKey: 'transMix' }, + { id: 'Zoom', labelKey: 'transZoom' }, + { id: 'Slide', labelKey: 'transSlide' }, + { id: 'Wipe', labelKey: 'transWipe' }, + ]; + + // Helper Functions + const updateImages = (newFiles: File[]) => { + const combinedFiles = [...images, ...newFiles]; + const limitedFiles = combinedFiles.slice(0, 10); + setImages(limitedFiles); + + const newPreviews: string[] = []; + let processed = 0; + if (limitedFiles.length === 0) { + setPreviews([]); + return; + } + + limitedFiles.forEach(file => { + const reader = new FileReader(); + reader.onloadend = () => { + newPreviews.push(reader.result as string); + processed++; + if (processed === limitedFiles.length) { + setPreviews([...newPreviews]); + } + }; + reader.readAsDataURL(file); + }); + }; + + const handleImageChange = (e: ChangeEvent) => { + if (e.target.files) { + updateImages(Array.from(e.target.files)); + } + }; + + const removeImage = (index: number) => { + const newImages = [...images]; + newImages.splice(index, 1); + setImages(newImages); + + if (newImages.length === 0) { + setPreviews([]); + } else { + const newPreviews: string[] = []; + let processed = 0; + newImages.forEach(file => { + const reader = new FileReader(); + reader.onloadend = () => { + newPreviews.push(reader.result as string); + processed++; + if (processed === newImages.length) setPreviews(newPreviews); + }; + reader.readAsDataURL(file); + }); + } + }; + + const handleSearch = async () => { + if (!searchQuery) return; + setIsSearching(true); + setSearchStatus('Processing...'); + setMapLink(null); + + let finalQuery = searchQuery; + if (searchQuery.includes('google') && searchQuery.includes('/maps/place/')) { + try { + const match = searchQuery.match(/\/maps\/place\/([^\/]+)/); + if (match && match[1]) { + finalQuery = decodeURIComponent(match[1]).replace(/\+/g, ' '); + } + } catch (e) { console.warn("URL Error"); } + } + + try { + const apiKey = import.meta.env.VITE_GEMINI_API_KEY; + if (!apiKey) throw new Error("API Key Missing"); + + const details = await searchPlaceDetails(finalQuery); + if (!details) { + alert("Place not found"); + setIsSearching(false); + return; + } + + setName(details.displayName.text); + setAddress(details.formattedAddress); + + if (details.photos && details.photos.length > 0) { + setSearchStatus(`Fetching ${details.photos.length} photos...`); + const photoPromises = details.photos.slice(0, 10).map(p => fetchPlacePhoto(p.name)); + const fetchedPhotos = (await Promise.all(photoPromises)).filter((f): f is File => f !== null); + + updateImages(fetchedPhotos); + } + + setSearchStatus('Analyzing reviews...'); + let generatedDesc = details.formattedAddress; + const reviewsText = details.reviews?.map(r => r.text.text) || []; + if (reviewsText.length > 0 || details.rating) { + generatedDesc = await enrichDescriptionWithReviews( + details.displayName.text, + details.formattedAddress, + reviewsText, + details.rating || 0, + apiKey + ); + } + + setDescription(generatedDesc); + setSearchStatus('Done!'); + setTimeout(() => setSearchStatus(''), 2000); + + } catch (error) { + console.error("Search failed:", error); + alert("Failed to fetch info."); + setSearchStatus(''); + } finally { + setIsSearching(false); + } + }; + + const handleNaverCrawl = async () => { + if (!naverUrl) return; + const apiKey = import.meta.env.VITE_GEMINI_API_KEY; + if (!apiKey) { + alert("API Key Required"); + return; + } + setIsCrawling(true); + setCrawlStatus('Connecting...'); + + try { + const result: any = await crawlNaverPlace(naverUrl, (msg) => setCrawlStatus(msg)); + if (result.name) setName(result.name); + if (result.description) setDescription(result.description); + if (result.address) setAddress(result.address); + if (result.category) setCategory(result.category); + + if (result.images && result.images.length > 0) { + setCrawlStatus(`Fetched ${result.images.length} photos.`); + updateImages(result.images); + } else { + setCrawlStatus('No photos found.'); + } + setTimeout(() => setCrawlStatus(''), 2000); + } catch (e: any) { + console.error("Crawl failed:", e); + alert(e.message || "Failed to crawl Naver Place."); + setCrawlStatus(''); + } finally { + setIsCrawling(false); + } + }; + + const handleStyleImageUpload = async (e: ChangeEvent) => { + if (!e.target.files || e.target.files.length === 0) return; + const file = e.target.files[0]; + const apiKey = import.meta.env.VITE_GEMINI_API_KEY; + if (!apiKey) { alert("API Key Required"); return; } + + setIsAnalyzingStyle(true); + try { + const cssCode = await extractTextEffectFromImage(file, apiKey); + let styleTag = document.getElementById('custom-text-style'); + if (!styleTag) { + styleTag = document.createElement('style'); + styleTag.id = 'custom-text-style'; + document.head.appendChild(styleTag); + } + styleTag.textContent = cssCode; + setCustomStyle(cssCode); + setTextEffect('Custom'); + alert("Style analysis complete!"); + } catch (e) { + console.error("Style analysis failed:", e); + alert("Failed to analyze style."); + } finally { + setIsAnalyzingStyle(false); + } + }; + + const handleSubmit = (e: React.FormEvent | null, mode: 'Full' | 'AudioOnly' = 'Full') => { + if (e) e.preventDefault(); + if (name && description) { + onSubmit({ + name, description, images, audioMode, musicGenre, musicDuration, ttsConfig, + textEffect, transitionEffect, visualStyle, aspectRatio, sourceUrl: naverUrl || mapLink || undefined, + customStyleCSS: customStyle, creationMode: mode, address, category, + language: contentLanguage, + useAiImages, + pensionCategories + }); + } else { + alert(t('brandName') + " & " + t('brandDesc') + " required."); + } + }; + + // 단계별 완료 조건 + const step1Complete = pensionCategories.length > 0; + const step2Complete = step1Complete; + const step3Complete = step2Complete && name.length > 0 && description.length > 0; + const step4Complete = step3Complete && contentLanguage !== undefined; + const step5Complete = step4Complete && textEffect !== undefined; + const step6Complete = step5Complete; + const canSubmit = step6Complete; + + // Progress calculation + const completedSteps = [step1Complete, step2Complete, step3Complete, step4Complete, step5Complete, step6Complete].filter(Boolean).length; + const progressPercent = Math.round((completedSteps / 6) * 100); + + // 온보딩 투어 스텝 + const tourSteps = [ + { + target: '[data-tour="step1"]', + title: '1단계: 펜션 유형 선택', + description: '운영하시는 펜션의 유형을 선택해주세요. 복수 선택 가능하며, AI가 이 정보를 바탕으로 맞춤형 콘텐츠를 생성합니다.', + placement: 'bottom' as const, + }, + { + target: '[data-tour="step2"]', + title: '2단계: 사진 업로드', + description: '펜션 사진을 업로드하세요. 사진이 부족하면 AI가 자동으로 생성할 수도 있습니다. (선택사항)', + placement: 'bottom' as const, + }, + { + target: '[data-tour="step3"]', + title: '3단계: 정보 자동 입력', + description: '네이버 플레이스 URL 또는 Google Maps에서 검색하여 펜션 정보를 자동으로 가져올 수 있습니다.', + placement: 'bottom' as const, + }, + { + target: '[data-tour="step4"]', + title: '4단계: 기본 정보 입력', + description: '펜션 이름과 설명을 입력해주세요. 자동 입력을 사용했다면 이미 채워져 있을 거예요!', + placement: 'bottom' as const, + }, + { + target: '[data-tour="step5"]', + title: '5단계: 언어 및 영상 설정', + description: '영상 생성 언어, 비율(인스타/유튜브), 비주얼 스타일을 선택하세요.', + placement: 'left' as const, + }, + { + target: '[data-tour="step6"]', + title: '6단계: 자막 스타일', + description: '영상에 표시될 자막의 스타일을 선택하세요. 다양한 효과를 미리 볼 수 있습니다.', + placement: 'top' as const, + }, + { + target: '[data-tour="step7"]', + title: '7단계: 오디오 설정', + description: '노래, BGM, 성우 중 선택하고, 장르나 음성 설정을 커스터마이징하세요.', + placement: 'top' as const, + }, + { + target: '[data-tour="submit"]', + title: '마지막: 영상 생성!', + description: '모든 설정이 완료되면 이 버튼을 눌러 AI 영상 생성을 시작하세요!', + placement: 'top' as const, + }, + ]; + + const handleTourComplete = () => { + localStorage.setItem('castadTourCompleted', 'true'); + setShowTour(false); + }; + + const handleTourSkip = () => { + localStorage.setItem('castadTourCompleted', 'true'); + setShowTour(false); + }; + + // Step indicator component + const StepIndicator = ({ step, completed, active, locked }: { step: number; completed: boolean; active: boolean; locked: boolean }) => ( +
+ {locked ? : completed ? : step} +
+ ); + + return ( + <> + {showTour && ( + + )} + +
+ + {/* Header */} +
+

+ {t('appTitle')} +

+

{t('appSubtitle')}

+ + {/* Progress indicator */} +
+
+ {t('textStyle') === '자막 스타일' ? '진행률' : 'Progress'} + {progressPercent}% +
+
+
+
+
+
+ +
handleSubmit(e, 'Full')} className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-start"> + + {/* [LEFT COLUMN] */} +
+ + {/* STEP 1: Pension Category */} + + +
+ +
+ {t('pensionCategory')} + + {t('textStyle') === '자막 스타일' ? '복수 선택 가능' : 'Multiple selection allowed'} + +
+ {step1Complete && {t('textStyle') === '자막 스타일' ? '완료' : 'Done'}} +
+
+ +
+ {PENSION_CATEGORIES.map((cat) => { + const isSelected = pensionCategories.includes(cat.id); + return ( + + ); + })} +
+
+
+ + {/* STEP 2: Image Upload */} + + +
+ +
+ {t('uploadTitle')} + + {t('textStyle') === '자막 스타일' ? '최대 10장 (선택사항)' : 'Up to 10 images (optional)'} + +
+ {previews.length > 0 && ( + {previews.length}/10 + )} +
+
+ +
+ + +
+ + {previews.length > 0 && ( +
+ {previews.map((src, idx) => ( +
+ + +
+ ))} +
+ )} + + {/* AI Image Option */} +
+
+ +
+

{t('aiImageOption')}

+

{t('aiImageDesc')}

+
+
+ +
+
+
+ + {/* STEP 3: Auto Data Search */} + setOpenSections({...openSections, autoSearch: open})} + > + + + +
+ +
+ {t('autoInfoTitle')} + + {t('textStyle') === '자막 스타일' ? '네이버/구글에서 자동 가져오기' : 'Auto-fetch from Naver/Google'} + +
+ +
+
+
+ + + {/* Naver */} +
+ +
+ setNaverUrl(e.target.value)} + placeholder="https://naver.me/..." + disabled={!step2Complete} + className="flex-1 h-9 text-sm" + /> + +
+ {crawlStatus &&

{crawlStatus}

} +
+ +
+ + + {t('textStyle') === '자막 스타일' ? '또는' : 'OR'} + +
+ + {/* Google */} +
+ +
+ setSearchQuery(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && (e.preventDefault(), handleSearch())} + placeholder="Search place name..." + disabled={!step2Complete} + className="flex-1 h-9 text-sm" + /> + +
+ {searchStatus &&

{searchStatus}

} +
+
+
+
+
+ + {/* STEP 4: Basic Info */} + + +
+ +
+ + {t('textStyle') === '자막 스타일' ? '기본 정보' : 'Basic Info'} + + + {t('textStyle') === '자막 스타일' ? '펜션 이름과 설명을 입력하세요' : 'Enter pension name and description'} + +
+ {step3Complete && {t('textStyle') === '자막 스타일' ? '완료' : 'Done'}} +
+
+ +
+ + setName(e.target.value)} + placeholder="ex: Galaxy Pension" + disabled={!step2Complete} + className="h-11 text-base font-medium" + /> +
+
+ +