# FastAPI 프로젝트 구조 원칙

**작성일**: 2025-09-17
**수정일**: 2025-10-02 (모범 사례 반영)

## 1. 계층 분리 원칙

### 필수 계층
```
요청 계층 (router/)
    ↓
비즈니스 계층 (services/, llm/, brain/)
    ↓
데이터 계층 (state/, repositories/)
```

### 계층별 책임

| 계층 | 역할 | 금지 사항 |
|------|------|----------|
| **router/** | HTTP 요청/응답 처리만 | DB 직접 접근, 비즈니스 로직 |
| **services/llm/** | 비즈니스 로직 구현 | DB 직접 연결 (state를 통해서만) |
| **state/** | DB CRUD만 | 비즈니스 로직 포함 |

## 2. 폴더 구조 규칙

### 표준 구조
```
{service_name}/
├── main.py              # 앱 실행, 라우터 등록만
├── app/
│   ├── router/         # HTTP 엔드포인트
│   │   └── v1/         # API 버전 관리 (선택)
│   ├── services/       # 비즈니스 로직
│   ├── state/          # DB 접근 (Repository 패턴)
│   ├── models/         # ORM 모델 (DB 테이블 정의)
│   ├── schemas/        # Pydantic 모델 (API 요청/응답)
│   ├── core/           # 설정, 공통 기능
│   ├── db/             # DB 세션 관리 (선택)
│   └── utils/          # 유틸리티
└── tests/
```

### 폴더 명명 규칙
- `router/` 또는 `api/`: HTTP 처리
- `services/`: 도메인 로직
- `state/` 또는 `repositories/`: Repository 패턴으로 CRUD 캡슐화
- `models/`: SQLAlchemy 등 ORM 모델
- `schemas/`: Pydantic 요청/응답 스키마 (models와 분리)
- `db/`: DB 엔진/세션 중앙 관리 (선택)
- 복수형 사용 권장

## 3. 파일 명명 규칙

### router/
- `{기능}_handler.py`: 이벤트 처리 (slack_handler.py)
- `{기능}_endpoint.py`: REST API (emotion_endpoint.py)

### services/
- `{도메인}_{기능}.py`: coldmail_filter.py, ir_analyzer.py
- 한 파일 최대 300줄

### state/ (Repository 패턴)
- `database.py`: 통합 DB 접근
- `{도메인}_repository.py`: 도메인별 CRUD 캡슐화 (예: user_repository.py는 User 모델 CRUD만)

### models/
- `{도메인}_model.py`: ORM 모델 (예: user_model.py, emotion_model.py)

### schemas/
- `{도메인}_schema.py`: API 입출력 스키마 (예: user_schema.py, emotion_schema.py)

## 4. 의존성 방향 규칙

### 단방향 흐름
```
router → services → state/repositories
  ↓         ↓         ↓
schemas   core     models
  ↓
utils
```

### 계층 간 데이터 흐름
- **router**: schemas로 요청/응답 검증
- **services**: schemas + models 사용 가능
- **state/repositories**: models만 사용 (DB 접근)

### 금지 사항
- ❌ 순환 참조: A imports B, B imports A
- ❌ 하위가 상위 호출: state가 services 호출
- ❌ 계층 건너뛰기: router가 직접 state 호출 (긴급 상황 제외)

## 5. 코드 작성 원칙

### LangGraph 워크플로우
- **복잡한 다단계 처리**: LangGraph 적극 활용
- **프로덕션 핵심 워크플로우**: PostgresSaver로 체크포인트를 두어 부분 재시도 가능하게 구현 (권장)
- **실험/경량 플로우**: stateless LangGraph도 허용하되, 추후 stateful 전환 여부를 문서나 주석으로 명시

### router 계층
```python
# ✅ 올바름
async def handle_request(data: dict):
    result = await some_service.process(data)  # 서비스 호출
    return {"result": result}

# ❌ 금지
async def handle_request(data: dict):
    conn = await asyncpg.connect(...)  # DB 직접 접근
    result = complex_logic(data)       # 비즈니스 로직
```

### services 계층
```python
# ✅ 올바름
async def process_data(data: dict):
    validated = validate(data)         # 비즈니스 로직
    await save_to_db(validated)        # state 호출
    return validated

# ❌ 금지
async def process_data(data: dict):
    conn = await asyncpg.connect(...)  # 직접 DB 연결
```

### state 계층
```python
# ✅ 올바름
async def save_emotion(data: dict):
    conn = await asyncpg.connect(METRICS_DB_URL)
    await conn.execute("INSERT INTO ...")  # DB만
    await conn.close()

# ❌ 금지
async def save_emotion(data: dict):
    if data['emotion'] == 'anger':     # 비즈니스 로직
        data = transform(data)
    await conn.execute(...)
```

## 6. DB 접근 규칙

### 환경변수 사용
- `DATABASE_URL`: 메인 DB
- `METRICS_DATABASE_URL`: 메트릭 전용 DB
- `TEST_DATABASE_URL`: 테스트 DB

### 연결 방식
```python
# db/database.py: DB 세션 중앙 관리 (권장)
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession

async def get_session() -> AsyncSession:
    # 의존성 주입으로 세션 제공
    pass

# state/database.py: 직접 연결 (간단한 경우)
async def get_connection():
    return await asyncpg.connect(os.getenv("DATABASE_URL"))
```

### 금지 사항
- ❌ 프로덕션 router/services에서 직접 asyncpg.connect()
- ❌ 하드코딩된 DB URL
- ❌ JSONB 저장 시 dict 직접 전달 (json.dumps() 필수)
  - 마이그레이션/원샷 스크립트 등 서비스 외부 유틸에서는 직접 연결 허용하되, 프로덕션 요청 경로에서 재사용 금지

## 7. 파일 크기 제한

- **한 파일 최대 300줄 권장** (핵심 모듈은 가급적 유지)
- 초과 시 기능별 분리
- 예: `services/email_integration.py` (500줄) → `email_send.py` + `email_fetch.py`

## 8. Import 규칙

### 금지
```python
from app.state.database import *       # ❌ wildcard
from ..router.slack_handler import x   # ❌ 순환 가능성
```

### 권장
```python
from app.state.database import save_emotion_reading  # ✅ 명시적
from app.services import coldmail_filter             # ✅ 모듈 import
```

## 9. 체크리스트

코드 작성 전:
- [ ] 이 코드는 어느 계층인가?
- [ ] DB 접근은 state를 통하는가?
- [ ] 비즈니스 로직이 router에 있지 않은가?
- [ ] 순환 import 가능성은 없는가?
- [ ] 핵심 파일은 300줄 이하로 유지할 수 있는가?

## 10. 예외 상황

### 허용되는 예외
1. **긴급 핫픽스**: 임시로 계층 건너뛰기 가능 (문서화 필수)
2. **레거시 코드**: 점진적 리팩토링
3. **성능 최적화**: 충분한 근거 필요

### 예외 처리 시
```python
# TODO: 계층 위반 - 리팩토링 필요 (issue #123)
# 긴급 수정: 2025-10-02, 사유: DB 장애 복구
```

## 11. 모범 사례 참고

본 문서는 FastAPI 커뮤니티의 다음 모범 사례를 반영하였습니다:

1. **models/schemas 분리**: DB 스키마와 API 스펙 독립 관리
2. **Repository 패턴**: state/repositories에서 CRUD 캡슐화
3. **DB 세션 중앙화**: db/database.py에서 의존성 주입
4. **API 버전 관리**: router/v1/, router/v2/ 구조
5. **관심사 분리**: 요청/비즈니스/데이터 계층 명확한 역할 분담
