- FastAPI 백엔드 (audio-studio-api) - Next.js 프론트엔드 (audio-studio-ui) - Qwen3-TTS 엔진 (audio-studio-tts) - MusicGen 서비스 (audio-studio-musicgen) - Docker Compose 개발/운영 환경 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7.9 KiB
7.9 KiB
Project Knowledge
이 문서는 프로젝트의 개발 표준 및 패턴을 정의합니다. Claude.ai Projects 또는 Claude Code에서 참조하여 일관된 코드를 작성하세요.
1. 프로젝트 개요
기술 스택
- Frontend: Next.js 16 + React 19 + TypeScript + Tailwind CSS 4 + shadcn/ui
- Backend: FastAPI + Python 3.11 + Pydantic v2
- Database: MongoDB 7.0 (motor async driver) + Redis 7
- AI: Claude API (Anthropic) + OpenAI API
- Containerization: Docker + Docker Compose
- Repository: Gitea (http://gitea.yakenator.io/yakenator/)
2. Docker 배포 규칙
Dockerfile 패턴 (Python Worker)
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY *.py .
CMD ["python", "worker.py"]
docker-compose 서비스 패턴
services:
{서비스명}:
build:
context: ./repos/{서비스명}
dockerfile: Dockerfile
container_name: {프로젝트}-{서비스명}
restart: unless-stopped
environment:
- REDIS_URL=redis://redis:6379
- MONGODB_URL=mongodb://${MONGO_USER}:${MONGO_PASSWORD}@mongodb:27017/
- DB_NAME={데이터베이스명}
depends_on:
redis:
condition: service_healthy
mongodb:
condition: service_healthy
networks:
- {프로젝트}-network
네이밍 규칙
- 컨테이너:
{프로젝트}-{서비스명} - 볼륨:
{프로젝트}_{데이터유형}_data - 네트워크:
{프로젝트}-network
3. 한국어 개발 컨벤션
Docstring
async def get_data(self, name: str, context: List[str] = None) -> DataInfo:
"""
데이터 조회 (context 기반 후보 선택)
Args:
name: 데이터 이름
context: 컨텍스트 키워드
Returns:
DataInfo 객체
"""
로깅 메시지 (한글 + 영문 혼용)
logger.info(f"Found {len(items)} item(s) for '{name}'")
logger.warning(f"Operation failed (non-critical): {e}")
커밋 메시지
feat: 기능 설명
- 변경사항 1
- 변경사항 2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
4. FastAPI 패턴
앱 초기화
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(
title="{서비스명} API",
description="서비스 설명",
version="1.0.0"
)
Pydantic 모델
class CreateRequest(BaseModel):
name: str
description: Optional[str] = ""
data_type: Optional[str] = "default"
엔드포인트
@app.get("/health")
async def health_check():
return {"status": "healthy", "timestamp": datetime.now().isoformat()}
@app.post("/create")
async def create_item(request: CreateRequest):
try:
# 처리 로직
return {"success": True, "data": result}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
5. React/Next.js 패턴
디렉토리 구조
src/
├── app/ # Next.js App Router
├── components/
│ ├── ui/ # shadcn/ui 컴포넌트
│ └── providers.tsx # Context Providers
├── hooks/ # 커스텀 훅
├── lib/utils.ts # cn() 등 유틸리티
└── types/ # TypeScript 타입
cn() 유틸리티
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
폼 패턴 (react-hook-form + zod)
const formSchema = z.object({
title: z.string().min(1, "제목을 입력하세요"),
content: z.string().min(10, "내용은 10자 이상"),
})
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
})
6. MongoDB 패턴
연결 (motor async)
from motor.motor_asyncio import AsyncIOMotorClient
client = AsyncIOMotorClient(os.getenv("MONGODB_URL"))
db = client[os.getenv("DB_NAME")]
인덱스 생성
await collection.create_index("unique_field", unique=True, sparse=True)
await collection.create_index("name")
await collection.create_index("updated_at")
Upsert 패턴
result = await collection.update_one(
{"unique_field": data["unique_field"]},
{
"$set": update_doc,
"$setOnInsert": {"created_at": datetime.now()}
},
upsert=True
)
TTL 캐시
CACHE_TTL_DAYS = 7
def _is_cache_fresh(self, cached_data: Dict) -> bool:
updated_at = cached_data.get("updated_at")
expiry_date = updated_at + timedelta(days=CACHE_TTL_DAYS)
return datetime.now() < expiry_date
7. AI API 통합
Claude API
from anthropic import AsyncAnthropic
client = AsyncAnthropic(api_key=os.getenv("CLAUDE_API_KEY"))
response = await client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=8192,
messages=[{"role": "user", "content": prompt}]
)
프롬프트 템플릿 (MongoDB 저장)
async def _get_prompt_template(self) -> str:
# 캐시 확인
if self._cached_prompt and time.time() - self._prompt_cache_time < 300:
return self._cached_prompt
# DB에서 조회
custom_prompt = await self.db.prompts.find_one({"service": "{서비스명}"})
return custom_prompt.get("content") if custom_prompt else self._default_prompt
8. 환경 변수
# .env
MONGO_USER=admin
MONGO_PASSWORD={비밀번호}
REDIS_URL=redis://redis:6379
CLAUDE_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
JWT_SECRET_KEY={시크릿키}
DB_NAME={데이터베이스명}
9. 헬스체크
FastAPI
@app.get("/health")
async def health():
return {"status": "healthy"}
Docker Compose
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
10. 데이터베이스 백업 정책
백업 규칙
- 주기: 하루에 한 번 (daily)
- 보관 기간: 최소 7일
- 백업 위치: 프로젝트 루트의
./backups/디렉토리
MongoDB 백업 명령어
BACKUP_NAME="mongodb_backup_$(date +%Y%m%d_%H%M%S)"
docker exec {프로젝트}-mongodb mongodump \
--uri="mongodb://{user}:{password}@localhost:27017" \
--authenticationDatabase=admin \
--out="/tmp/$BACKUP_NAME"
docker cp {프로젝트}-mongodb:/tmp/$BACKUP_NAME ./backups/
복원 명령어
docker cp ./backups/$BACKUP_NAME {프로젝트}-mongodb:/tmp/
docker exec {프로젝트}-mongodb mongorestore \
--uri="mongodb://{user}:{password}@localhost:27017" \
--authenticationDatabase=admin \
"/tmp/$BACKUP_NAME"
자동화 (cron)
# crontab -e
0 2 * * * /path/to/backup-script.sh # 매일 새벽 2시
주의사항
- 새 프로젝트 생성 시 반드시 백업 스크립트 설정
- 백업 디렉토리는 .gitignore에 추가
- 중요 데이터는 외부 스토리지에 추가 백업 권장
11. Gitea 리포지토리 관리
서버 정보
- URL: http://gitea.yakenator.io
- 사용자: yakenator
- 비밀번호: asdfg23we
새 리포지토리 생성 (API)
curl -X POST "http://gitea.yakenator.io/api/v1/user/repos" \
-H "Content-Type: application/json" \
-u "yakenator:asdfg23we" \
-d '{"name": "{repo-name}", "private": false}'
새 프로젝트 초기화 및 푸시
# 1. Git 초기화
git init
# 2. 첫 커밋
git add -A
git commit -m "Initial commit"
# 3. Gitea에 리포지토리 생성 (위 API 사용)
# 4. Remote 추가 및 푸시
git remote add origin http://yakenator:asdfg23we@gitea.yakenator.io/yakenator/{repo-name}.git
git branch -M main
git push -u origin main
이 문서를 Claude.ai Projects의 "Project Knowledge"에 추가하면 프로젝트 컨텍스트를 공유할 수 있습니다.