Merge branch 'main' of https://git.ro-being.com/ivada_Ro-being/DOCS
This commit is contained in:
commit
aba03cd32c
@ -19,7 +19,7 @@
|
|||||||
| 계층 | 역할 | 금지 사항 |
|
| 계층 | 역할 | 금지 사항 |
|
||||||
|------|------|----------|
|
|------|------|----------|
|
||||||
| **router/** | HTTP 요청/응답 처리만 | DB 직접 접근, 비즈니스 로직 |
|
| **router/** | HTTP 요청/응답 처리만 | DB 직접 접근, 비즈니스 로직 |
|
||||||
| **services/llm/** | 비즈니스 로직 구현 | DB 직접 연결 (state를 통해서만) |
|
| **services/** | 비즈니스 로직 구현 | DB 직접 연결 (state를 통해서만) |
|
||||||
| **state/** | DB CRUD만 | 비즈니스 로직 포함 |
|
| **state/** | DB CRUD만 | 비즈니스 로직 포함 |
|
||||||
|
|
||||||
## 2. 폴더 구조 규칙
|
## 2. 폴더 구조 규칙
|
||||||
@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
### 폴더 명명 규칙
|
### 폴더 명명 규칙
|
||||||
- `router/` 또는 `api/`: HTTP 처리
|
- `router/` 또는 `api/`: HTTP 처리
|
||||||
- `services/`: 도메인 로직
|
- `services/`: 비즈니스 로직
|
||||||
- `state/` 또는 `repositories/`: Repository 패턴으로 CRUD 캡슐화
|
- `state/` 또는 `repositories/`: Repository 패턴으로 CRUD 캡슐화
|
||||||
- `models/`: SQLAlchemy 등 ORM 모델
|
- `models/`: SQLAlchemy 등 ORM 모델
|
||||||
- `schemas/`: Pydantic 요청/응답 스키마 (models와 분리)
|
- `schemas/`: Pydantic 요청/응답 스키마 (models와 분리)
|
||||||
@ -62,7 +62,7 @@
|
|||||||
|
|
||||||
### state/ (Repository 패턴)
|
### state/ (Repository 패턴)
|
||||||
- `database.py`: 통합 DB 접근
|
- `database.py`: 통합 DB 접근
|
||||||
- `{도메인}_repository.py`: 도메인별 CRUD 캡슐화 (예: user_repository.py는 User 모델 CRUD만)
|
- `{도메인}_repository.py`: 도메인별 CRUD 캡슐화
|
||||||
|
|
||||||
### models/
|
### models/
|
||||||
- `{도메인}_model.py`: ORM 모델 (예: user_model.py, emotion_model.py)
|
- `{도메인}_model.py`: ORM 모델 (예: user_model.py, emotion_model.py)
|
||||||
@ -95,49 +95,13 @@ utils
|
|||||||
|
|
||||||
### LangGraph 워크플로우
|
### LangGraph 워크플로우
|
||||||
- **복잡한 다단계 처리**: LangGraph 적극 활용
|
- **복잡한 다단계 처리**: LangGraph 적극 활용
|
||||||
- **프로덕션 핵심 워크플로우**: PostgresSaver로 체크포인트를 두어 부분 재시도 가능하게 구현 (권장)
|
- **프로덕션 핵심 워크플로우**: PostgresSaver로 체크포인트 구현 (권장)
|
||||||
- **실험/경량 플로우**: stateless LangGraph도 허용하되, 추후 stateful 전환 여부를 문서나 주석으로 명시
|
- **실험/경량 플로우**: stateless LangGraph 허용
|
||||||
|
|
||||||
### router 계층
|
### 계층별 원칙
|
||||||
```python
|
- **router**: 서비스 호출만, DB/비즈니스 로직 금지
|
||||||
# ✅ 올바름
|
- **services**: 비즈니스 로직 구현, state를 통한 DB 접근
|
||||||
async def handle_request(data: dict):
|
- **state**: DB CRUD만, 비즈니스 로직 금지
|
||||||
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 접근 규칙
|
## 6. DB 접근 규칙
|
||||||
|
|
||||||
@ -147,44 +111,29 @@ async def save_emotion(data: dict):
|
|||||||
- `TEST_DATABASE_URL`: 테스트 DB
|
- `TEST_DATABASE_URL`: 테스트 DB
|
||||||
|
|
||||||
### 연결 방식
|
### 연결 방식
|
||||||
```python
|
- **권장**: `db/database.py`에서 DB 세션 중앙 관리 (의존성 주입)
|
||||||
# db/database.py: DB 세션 중앙 관리 (권장)
|
- **간단한 경우**: `state/database.py`에서 직접 연결
|
||||||
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()
|
- ❌ 프로덕션 router/services에서 직접 asyncpg.connect()
|
||||||
- ❌ 하드코딩된 DB URL
|
- ❌ 하드코딩된 DB URL
|
||||||
- ❌ JSONB 저장 시 dict 직접 전달 (json.dumps() 필수)
|
- ❌ JSONB 저장 시 dict 직접 전달 (json.dumps() 필수)
|
||||||
- 마이그레이션/원샷 스크립트 등 서비스 외부 유틸에서는 직접 연결 허용하되, 프로덕션 요청 경로에서 재사용 금지
|
- ❌ 프로덕션 요청 경로에서 직접 DB 연결 재사용
|
||||||
|
|
||||||
## 7. 파일 크기 제한
|
## 7. 파일 크기 제한
|
||||||
|
|
||||||
- **한 파일 최대 300줄 권장** (핵심 모듈은 가급적 유지)
|
- **한 파일 최대 300줄 권장**
|
||||||
- 초과 시 기능별 분리
|
- 초과 시 기능별 분리
|
||||||
- 예: `services/email_integration.py` (500줄) → `email_send.py` + `email_fetch.py`
|
|
||||||
|
|
||||||
## 8. Import 규칙
|
## 8. Import 규칙
|
||||||
|
|
||||||
### 금지
|
### 금지
|
||||||
```python
|
- ❌ wildcard import (`from module import *`)
|
||||||
from app.state.database import * # ❌ wildcard
|
- ❌ 상대 import로 순환 참조 가능성 (`from ..router import x`)
|
||||||
from ..router.slack_handler import x # ❌ 순환 가능성
|
|
||||||
```
|
|
||||||
|
|
||||||
### 권장
|
### 권장
|
||||||
```python
|
- ✅ 명시적 import (`from app.state.database import save_emotion_reading`)
|
||||||
from app.state.database import save_emotion_reading # ✅ 명시적
|
- ✅ 모듈 import (`from app.services import coldmail_filter`)
|
||||||
from app.services import coldmail_filter # ✅ 모듈 import
|
|
||||||
```
|
|
||||||
|
|
||||||
## 9. 체크리스트
|
## 9. 체크리스트
|
||||||
|
|
||||||
@ -203,12 +152,23 @@ from app.services import coldmail_filter # ✅ 모듈 import
|
|||||||
3. **성능 최적화**: 충분한 근거 필요
|
3. **성능 최적화**: 충분한 근거 필요
|
||||||
|
|
||||||
### 예외 처리 시
|
### 예외 처리 시
|
||||||
```python
|
- TODO 주석으로 계층 위반 표시
|
||||||
# TODO: 계층 위반 - 리팩토링 필요 (issue #123)
|
- 긴급 수정 사유 명시
|
||||||
# 긴급 수정: 2025-10-02, 사유: DB 장애 복구
|
|
||||||
```
|
|
||||||
|
|
||||||
## 11. 모범 사례 참고
|
## 11. 로깅 원칙
|
||||||
|
|
||||||
|
**로그 레벨 사용 기준** (Python logging 공식 문서):
|
||||||
|
- **DEBUG**: 개발/디버깅용 상세 정보 (중간 과정, 내부 상태)
|
||||||
|
- **INFO**: 정상 동작 및 주요 이벤트 (시작/종료, 주요 단계)
|
||||||
|
- **WARNING**: 잠재적 문제 (예상치 못한 상황, 성능 저하 가능성)
|
||||||
|
- **ERROR**: 오류 발생 (기능 실패, 예외 처리)
|
||||||
|
|
||||||
|
**규칙**:
|
||||||
|
- 시작/종료는 반드시 INFO 레벨
|
||||||
|
- 중간 과정은 DEBUG 레벨
|
||||||
|
- 프로덕션에서는 INFO 기본, DEBUG는 필요 시에만 활성화
|
||||||
|
|
||||||
|
## 12. 모범 사례 참고
|
||||||
|
|
||||||
본 문서는 FastAPI 커뮤니티의 다음 모범 사례를 반영하였습니다:
|
본 문서는 FastAPI 커뮤니티의 다음 모범 사례를 반영하였습니다:
|
||||||
|
|
||||||
|
|||||||
@ -155,6 +155,21 @@ CREATE TABLE ir_deck_feedback (
|
|||||||
- 등급 분류를 위한 베이지안 분류기 구현 (향후)
|
- 등급 분류를 위한 베이지안 분류기 구현 (향후)
|
||||||
- 피드백 데이터가 충분히 쌓이면 학습 시작
|
- 피드백 데이터가 충분히 쌓이면 학습 시작
|
||||||
|
|
||||||
|
## 전체 플로우
|
||||||
|
|
||||||
|
1. **업로드**: `POST /rb8001/api/ir-deck/upload` → `document_id` 반환
|
||||||
|
2. **평가**: `POST /rb8001/api/ir-deck/evaluate` → 동기식으로 완료까지 대기 → `evaluation_id`, `total_score`, `grade`, `page_evaluations` 반환
|
||||||
|
3. **조회**: `GET /rb8001/api/ir-deck/evaluation/{id}` → DB 조회 (폴링용)
|
||||||
|
|
||||||
|
**참고**: `/rb8001`는 Nginx 프록시 경로 (51123 → 51124:8001)
|
||||||
|
|
||||||
|
## 중요한 점
|
||||||
|
|
||||||
|
- **동기식 평가**: `/evaluate` API는 평가 완료까지 대기 (페이지별 순차 실행, 타임아웃 주의)
|
||||||
|
- **중복 방지**: `force_reevaluate=False` 시 기존 평가 반환
|
||||||
|
- **의존 서비스**: skill-rag-file (포트 8508), 내장 LLM
|
||||||
|
- **DB 테이블**: `ir_deck_evaluations`, `ir_deck_page_evaluations`, `ir_deck_feedback`
|
||||||
|
|
||||||
## 교훈
|
## 교훈
|
||||||
|
|
||||||
- **기존 인프라 활용**: skill-rag-file의 업로드/검색 기능 재사용으로 개발 시간 단축
|
- **기존 인프라 활용**: skill-rag-file의 업로드/검색 기능 재사용으로 개발 시간 단축
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user