O2Sound_ver2_final/backend/docs/Developer_Guide.md

425 lines
9.8 KiB
Markdown

# O2Sound Backend 개발자 가이드
## 프로젝트 구조
```
backend/
├── app/
│ ├── core/ # 핵심 설정 및 유틸리티
│ │ ├── celery_app.py # Celery 설정
│ │ ├── database.py # 데이터베이스 연결
│ │ ├── env_setting.py # 환경 변수 설정
│ │ └── redis/ # Redis 관련 설정
│ ├── domain/ # 도메인 모델
│ │ └── models/ # SQLAlchemy 모델
│ ├── infra/ # 인프라 레이어
│ │ └── google/ # Google OAuth 구현
│ ├── presentation/ # 프레젠테이션 레이어
│ │ ├── api/ # API 라우터
│ │ │ └── v1/ # v1 API 엔드포인트
│ │ └── schemas/ # Pydantic 스키마
│ ├── services/ # 비즈니스 로직
│ ├── shared/ # 공통 유틸리티
│ │ ├── decorator/ # 데코레이터
│ │ ├── logger.py # 로깅 설정
│ │ └── progress.py # 진행률 추적
│ └── workers/ # Celery 작업
│ └── tasks.py # 비동기 작업 정의
├── docs/ # API 문서
├── uploads/ # 업로드된 파일
├── main.py # FastAPI 앱 엔트리포인트
├── dependencies.py # 의존성 주입
├── pyproject.toml # Poetry 설정
└── Dockerfile # Docker 설정
```
## 핵심 개념
### 1. DDD (Domain-Driven Design) 구조
프로젝트는 DDD 원칙을 따라 구성되어 있습니다:
- **Domain Layer**: 비즈니스 엔티티와 도메인 로직
- **Application Layer**: 서비스와 비즈니스 유스케이스
- **Infrastructure Layer**: 외부 시스템과의 통합
- **Presentation Layer**: API 엔드포인트와 스키마
### 2. 의존성 주입
FastAPI의 의존성 주입 시스템을 활용:
```python
from app.dependencies import get_user_service
@router.post("/items")
async def get_items(
request: GetItemsRequest,
user_service: UserService = Depends(get_user_service)
):
return user_service.get_items(request)
```
### 3. Response Wrapper
모든 API 응답은 일관된 형식으로 래핑됩니다:
```python
from app.shared.decorator.response_wrapper import response_wrapper
@router.post("/login")
@response_wrapper
async def login(request: LoginRequest):
# 자동으로 성공/실패 형식으로 래핑됨
return auth_service.login(request)
```
## 주요 기능 구현
### 1. 인증 시스템
#### 세션 기반 인증
```python
# main.py
app.add_middleware(SessionMiddleware, secret_key=settings.SESSION_SECRET_KEY)
```
#### OAuth 2.0 (Google)
```python
# infra/google/service.py
class GoogleService:
async def get_login_url(self, return_url: Optional[str], request: Request):
# OAuth 플로우 시작
async def handle_callback(self, request: Request):
# 콜백 처리 및 토큰 교환
```
### 2. 비동기 작업 처리 (Celery)
#### 작업 정의
```python
# workers/tasks.py
@celery_app.task(bind=True)
def task1_crawl(self, url: str, root_task_id: str):
# 웹 크롤링 작업
process = Process(redis_manager)
process.update_progress(root_task_id, "metadata", True)
```
#### 워크플로우 체인
```python
# presentation/api/v1/moviemakers.py
workflow = chain(
crawl,
chord(
[lyrics_to_music, image_to_video],
task5_merge_results.s(root_task_id)
)
)
```
### 3. 진행률 추적
```python
# shared/progress.py
class Process:
def __init__(self, redis_manager: RedisManager):
self.redis = redis_manager
async def init_task_status(self, task_id: str):
# 초기 상태 설정
async def update_progress(self, task_id: str, step: str, completed: bool):
# 진행률 업데이트
```
## 개발 환경 설정
### 1. 초기 설정
```bash
# Poetry 설치
pip install poetry
# 의존성 설치
poetry install
# 환경 변수 설정
cp .env.local .env
```
### 2. 개발 서버 실행
```bash
# Redis 실행 (Docker)
docker run -d -p 6379:6379 redis
# PostgreSQL 실행 (Docker)
docker run -d -p 5432:5432 \
-e POSTGRES_USER=o2sound \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=o2sound \
postgres:14
# Celery Worker 실행
poetry run celery -A app.core.celery_app worker --loglevel=info
# FastAPI 개발 서버
poetry run uvicorn main:app --reload --port 8000
```
### 3. 환경 변수 설정 (.env)
```env
# 기본 설정
PROJECT_NAME=O2Sound
API_V1_STR=/api/v1
DEBUG=True
# 데이터베이스
DATABASE_URL=postgresql://o2sound:password@localhost:5432/o2sound
# Redis
REDIS_URL=redis://localhost:6379
# 세션
SESSION_SECRET_KEY=your-secret-key-here
# Google OAuth
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=http://localhost:8000/social/google/callback
# Celery
CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_RESULT_BACKEND=redis://localhost:6379/0
```
## 새로운 기능 추가하기
### 1. 새 엔드포인트 추가
#### Step 1: 스키마 정의
```python
# presentation/schemas/new_feature_schema.py
from pydantic import BaseModel
from uuid import UUID
class NewFeatureRequest(BaseModel):
user_id: UUID
feature_data: str
class NewFeatureResponse(BaseModel):
success: bool
result: str
```
#### Step 2: 서비스 구현
```python
# services/new_feature_service.py
class NewFeatureService:
def __init__(self, db_session):
self.db = db_session
def process_feature(self, request: NewFeatureRequest):
# 비즈니스 로직 구현
return NewFeatureResponse(success=True, result="processed")
```
#### Step 3: 라우터 추가
```python
# presentation/api/v1/new_feature.py
from fastapi import APIRouter, Depends
router = APIRouter(prefix="/new-feature", tags=["new-feature"])
@router.post("/process")
@response_wrapper
async def process_feature(
request: NewFeatureRequest,
service: NewFeatureService = Depends(get_new_feature_service)
):
return service.process_feature(request)
```
#### Step 4: 메인 앱에 등록
```python
# main.py
from app.presentation.api.v1.new_feature import router as new_feature_router
app.include_router(new_feature_router)
```
### 2. 새 Celery 작업 추가
```python
# workers/tasks.py
@celery_app.task(bind=True)
def new_async_task(self, data: dict, task_id: str):
try:
# 작업 시작
update_progress(task_id, "started", 0)
# 처리 로직
result = process_data(data)
# 완료
update_progress(task_id, "completed", 100)
return result
except Exception as e:
update_progress(task_id, "failed", -1)
raise
```
## 테스트
### 1. 단위 테스트
```python
# tests/test_auth.py
import pytest
from app.services.auth_service import AuthService
def test_user_registration():
service = AuthService(mock_db_session)
result = service.join(JoinRequest(
user_id="test_user",
name="Test User",
password="password123"
))
assert result.name == "Test User"
```
### 2. API 테스트
```python
# tests/test_api.py
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_health_check():
response = client.get("/health")
assert response.status_code == 200
assert response.json()["status"] == "healthy"
```
### 3. 통합 테스트
```bash
# Postman 컬렉션 실행
newman run tests/postman/O2Sound.postman_collection.json
```
## 배포
### 1. Docker 빌드
```bash
docker build -t o2sound-backend .
```
### 2. Docker Compose
```yaml
# docker-compose.yml
version: '3.8'
services:
backend:
image: o2sound-backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/o2sound
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
worker:
image: o2sound-backend
command: celery -A app.core.celery_app worker
environment:
- CELERY_BROKER_URL=redis://redis:6379/0
depends_on:
- redis
db:
image: postgres:14
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=o2sound
redis:
image: redis:7
```
### 3. 프로덕션 체크리스트
- [ ] 환경 변수 확인
- [ ] 데이터베이스 마이그레이션
- [ ] Redis 연결 확인
- [ ] Celery Worker 실행
- [ ] 로그 설정
- [ ] 모니터링 설정
- [ ] 백업 전략 수립
## 모니터링
### 1. 로그 확인
```bash
# FastAPI 로그
tail -f logs/app.log
# Celery 로그
tail -f logs/celery.log
```
### 2. Celery Flower (웹 UI)
```bash
celery -A app.core.celery_app flower --port=5555
```
### 3. 성능 모니터링
- APM 도구 (예: New Relic, DataDog)
- Prometheus + Grafana
- ELK Stack (Elasticsearch, Logstash, Kibana)
## 문제 해결
### 일반적인 문제
1. **Redis 연결 실패**
```
❌ Redis 연결 실패: Connection refused
```
해결: Redis 서버가 실행 중인지 확인
2. **데이터베이스 연결 오류**
```
sqlalchemy.exc.OperationalError
```
해결: DATABASE_URL 및 PostgreSQL 서버 상태 확인
3. **Celery 작업 실패**
```
Task task1_crawl[...] raised unexpected
```
해결: Celery Worker 로그 확인 및 재시작
### 디버깅 팁
1. **FastAPI 자동 문서**
- Swagger UI: `http://localhost:8000/docs`
- ReDoc: `http://localhost:8000/redoc`
2. **대화형 디버깅**
```python
import pdb; pdb.set_trace()
```
3. **로그 레벨 조정**
```python
# 개발 환경에서 상세 로그
logger.setLevel(logging.DEBUG)
```
## 추가 리소스
- [FastAPI 공식 문서](https://fastapi.tiangolo.com/)
- [Celery 공식 문서](https://docs.celeryproject.org/)
- [SQLAlchemy 공식 문서](https://www.sqlalchemy.org/)
- [Redis 공식 문서](https://redis.io/documentation)