From c5d2f0880088671828ca1749a43a9c3471045053 Mon Sep 17 00:00:00 2001 From: happybell80 Date: Thu, 2 Oct 2025 15:15:40 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20FastAPI=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=9B=90=EC=B9=99=20=EB=AC=B8=EC=84=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 311_FastAPI_구조_원칙.md: 현재 구조가 아닌 지켜야 할 원칙으로 재작성 - 계층 분리, 의존성 방향, DB 접근 규칙 명확화 - 체크리스트 및 예외 상황 추가 --- 300_architecture/311_FastAPI_구조_원칙.md | 176 ++++++++++++++++++ ...PI_구조와_객체지향_개념_정리.md | 156 ---------------- 2 files changed, 176 insertions(+), 156 deletions(-) create mode 100644 300_architecture/311_FastAPI_구조_원칙.md delete mode 100644 ideas/250917_FastAPI_구조와_객체지향_개념_정리.md diff --git a/300_architecture/311_FastAPI_구조_원칙.md b/300_architecture/311_FastAPI_구조_원칙.md new file mode 100644 index 0000000..b8fa2a1 --- /dev/null +++ b/300_architecture/311_FastAPI_구조_원칙.md @@ -0,0 +1,176 @@ +# 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 엔드포인트 +│ ├── services/ # 비즈니스 로직 +│ ├── state/ # DB 접근 +│ ├── core/ # 설정, 공통 기능 +│ └── utils/ # 유틸리티 +└── tests/ +``` + +### 폴더 명명 규칙 +- `router/` 또는 `api/`: HTTP 처리 +- `services/`: 도메인 로직 +- `state/` 또는 `repositories/`: 데이터 접근 +- 복수형 사용 권장 + +## 3. 파일 명명 규칙 + +### router/ +- `{기능}_handler.py`: 이벤트 처리 (slack_handler.py) +- `{기능}_endpoint.py`: REST API (emotion_endpoint.py) + +### services/ +- `{도메인}_{기능}.py`: coldmail_filter.py, ir_analyzer.py +- 한 파일 최대 500줄 + +### state/ +- `database.py`: 통합 DB 접근 +- `{도메인}_repository.py`: 도메인별 분리 시 + +## 4. 의존성 방향 규칙 + +### 단방향 흐름 +``` +router → services → state + ↓ ↓ ↓ +utils core models +``` + +### 금지 사항 +- ❌ 순환 참조: A imports B, B imports A +- ❌ 하위가 상위 호출: state가 services 호출 +- ❌ 계층 건너뛰기: router가 직접 state 호출 (긴급 상황 제외) + +## 5. 코드 작성 원칙 + +### 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 +# state/database.py만 DB 연결 가능 +async def get_connection(): + return await asyncpg.connect(os.getenv("DATABASE_URL")) +``` + +### 금지 사항 +- ❌ router/services에서 직접 asyncpg.connect() +- ❌ 하드코딩된 DB URL +- ❌ JSONB 저장 시 dict 직접 전달 (json.dumps() 필수) + +## 7. 파일 크기 제한 + +- **한 파일 최대 500줄** +- 초과 시 기능별 분리 +- 예: `services/email_integration.py` (800줄) → `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 가능성은 없는가? +- [ ] 파일 크기가 500줄 이하인가? + +## 10. 예외 상황 + +### 허용되는 예외 +1. **긴급 핫픽스**: 임시로 계층 건너뛰기 가능 (문서화 필수) +2. **레거시 코드**: 점진적 리팩토링 +3. **성능 최적화**: 충분한 근거 필요 + +### 예외 처리 시 +```python +# TODO: 계층 위반 - 리팩토링 필요 (issue #123) +# 긴급 수정: 2025-10-02, 사유: DB 장애 복구 +``` diff --git a/ideas/250917_FastAPI_구조와_객체지향_개념_정리.md b/ideas/250917_FastAPI_구조와_객체지향_개념_정리.md deleted file mode 100644 index ff40a0e..0000000 --- a/ideas/250917_FastAPI_구조와_객체지향_개념_정리.md +++ /dev/null @@ -1,156 +0,0 @@ ---- -tags: [FastAPI, Architecture, OOP, Python, Analogy, Roving] -date: 2025-09-17 -modified: 2025-09-19 ---- - -# FastAPI 프로젝트 구조와 객체지향 개념 정리 (식당 비유) - -이 문서는 FastAPI의 계층형 아키텍처와 객체지향 프로그래밍의 핵심 용어들을 '식당/푸드코트' 비유를 통해 정리한 내용입니다. 추가로 로빙 프로젝트의 특수한 아키텍처를 인체 비유로 설명합니다. - -## 1. FastAPI 프로젝트 구조: 푸드코트와 개별 식당 - -마이크로서비스 아키텍처는 하나의 큰 식당이 아닌, 여러 전문 식당이 모인 '푸드코트'와 같습니다. - -- **API 게이트웨이**: 푸드코트 정문 안내 데스크. 모든 손님은 여기로만 들어오며, 공통 인증, 경로 안내 등을 담당. -- **개별 서비스**: 푸드코트 내의 각 식당 (한식 코너, 중식 코너 등). 각자 독립된 주방과 창고를 가짐. -- **메시지 버스**: 푸드코트 전체에 울리는 호출 벨. 가게 간 직접 통신이 아닌, 이벤트(주문 완료 등)를 알리는 역할. - -### 1.1. 개별 식당(서비스)의 내부 구조 - -| 파일/폴더 | 역할 (간단 설명) | 식당 비유 | -|---|---|---| -| `main.py` | 앱 실행, 라우터 등록 | 가게 정문 스위치, 메뉴판 거는 곳 | -| `api/routers/*.py` | HTTP 요청 처리, 서비스 호출 | 홀 서빙 직원 | -| `services/*.py` | 비즈니스 로직 구현 | 주방장 | -| `repositories/*.py` | 데이터베이스 접근 로직 | 창고 관리 직원 | -| `db/session.py` | DB 연결 및 세션 관리 | 창고 관리인, 창고 출입문 | -| `models/*.py` | DB 테이블 구조 정의 (ORM) | 창고 선반 설계도 (내부용) | -| `schemas/*.py` | 데이터 입출력 형식 정의 (Pydantic) | 손님 주문서 양식 (외부용) | -| `core/*.py` | 설정, 보안, 로깅 등 공통 규칙 | 가게 사장, 운영 규칙 책자 | -| `tests/*.py` | 코드 기능 자동 검증 | 시식 코너, 품질 검사 | -| `.env` | 민감 정보 보관 (비밀번호 등) | 금고 속 비밀 문서 | - -### 1.2. 올바른 작업 흐름 (주문 프로세스) - -> **원칙: 작업 지시서(API 요청) 없이 작업하지 않는다.** - -1. **손님**, 메뉴판을 보고 **주문(API 요청)**을 한다. -2. **홀 서빙 직원(라우터)**이 **주문서 양식(스키마)**에 맞는지 확인하고 주방에 전달한다. -3. **주방장(서비스)**은 레시피(비즈니스 로직)에 따라 요리를 시작한다. -4. **창고 직원(리포지토리)**에게 **창고 설계도(모델)**를 보고 재료를 가져오라고 시킨다. -5. 요리가 완성되면 다시 홀 서빙 직원을 통해 손님에게 전달된다. - -> **핵심**: 홀 서빙 직원이 직접 요리하거나, 주방장이 창고를 뒤지는 등 역할을 섞으면 가게가 엉망이 된다. 각자의 역할 분리가 중요하다. - -## 2. 핵심 용어: 스키마, 모델, 객체, 인스턴스 - -이 용어들은 헷갈리기 쉽지만, 본질과 표현의 관계로 이해할 수 있습니다. - -### 2.1. 스키마 (Schema) - 본질, 실체의 구조 - -- **의미**: 데이터가 어떤 모양과 규칙을 가져야 하는지에 대한 본질적인 틀, 설계. -- **비유**: - - **DB 스키마**: 아마존 창고의 실제 선반 구조, 구역 구분, 물리적 규칙. (실체) - - **API 스키마**: 테이블 오더 시스템의 전자 주문서 양식. (규칙) -- **역할**: 데이터가 제멋대로 존재하지 못하도록 형식을 강제한다. - -### 2.2. 모델 (Model) - 사영(投影), 껍데기 - -- **의미**: 스키마라는 본질을 코드 세계에서 다루기 쉽게 표현한 것. -- **비유**: - - **DB 모델(ORM 모델)**: 아마존 창고 구조를 원격으로 보여주는 대시보드 화면. - - **API 모델(Pydantic 모델)**: 전자 주문서 양식을 코드로 표현한 클래스(틀). -- **역할**: 개발자가 코드를 통해 실체(스키마)를 다룰 수 있게 해주는 인터페이스. - -> **결론**: 스키마는 본질, 모델은 그 본질을 다루기 위한 코드 상의 표현(껍데기)이다. 현업에서는 둘을 섞어 쓰기도 하지만, 이 개념을 구분하면 구조를 더 깊이 이해할 수 있다. - -### 2.3. 객체(Object)와 인스턴스(Instance) - -- **의미**: 클래스(설계도)를 바탕으로 메모리에 실제로 만들어진 개별 실체. -- **어원**: `Instance`는 '무리 속에 함께 서 있는 하나(one of them)', 즉 '구체적 사례'라는 뜻. -- **비유**: - - **클래스**: 김밥 레시피. - - **객체/인스턴스**: 그 레시피로 실제로 만든 김밥 한 줄. -- **차이**: 거의 동의어지만, **객체**는 존재 자체를, **인스턴스**는 '어떤 클래스의 사례'라는 관계를 강조한다. - -## 3. 파이썬 클래스와 객체지향의 핵심 - -### 3.1. `self` - "나 자신(객체)" - -- **역할**: 클래스 메서드 안에서, "현재 이 코드를 실행하고 있는 객체 자신"을 가리킨다. -- **필요성**: `self`가 없다면, 같은 클래스로 만든 여러 객체(김밥 100줄) 중 어느 객체(어떤 김밥)의 속성을 바꿔야 할지 구분할 수 없다. 객체의 '개별성'을 보장하기 위해 필수적이다. -- **동작**: `u.greet()`처럼 호출하면, 파이썬이 자동으로 `User.greet(u)`처럼 `u`를 `self` 자리에 넣어준다. - -### 3.2. `cls` - "우리 가게(클래스)" - -- **역할**: `@classmethod` 데코레이터가 붙은 메서드 안에서, "클래스 자신"을 가리킨다. -- **비유**: 개별 김밥(`self`)이 아닌, 김밥 가게(`cls`) 전체의 정보(e.g., 오늘 판 김밥 총개수)를 다룰 때 사용한다. - -### 3.3. 클래스와 메타클래스 - -- **클래스도 객체다**: 파이썬에서는 `class User:` 자체도 하나의 객체(`type` 클래스의 인스턴스)이다. -- **메타클래스(Metaclass)**: '클래스를 만드는 클래스'. 파이썬의 기본 메타클래스는 `type`이다. -- **비유**: - - **인스턴스**: 김밥 한 줄 - - **클래스**: 김밥 레시피 - - **메타클래스**: 그 레시피를 찍어내는 공장 - -## 4. 로빙 프로젝트 특수 아키텍처: 인체 비유 - -로빙 프로젝트는 일반적인 마이크로서비스와 달리 중앙 집중식 판단 구조를 가집니다. 이는 인체의 신경계와 유사합니다. - -### 4.1 로빙 = 디지털 생명체 - -**감각기관 (입력층)**: -- **Gateway**: 모든 감각 수용체 - 외부 자극(Slack 이벤트, HTTP 요청 등) 통합 수신 - -**뇌 (중앙 처리)**: -- **rb8001 (대뇌)**: 의식, 판단, 오케스트레이션 -- **PostgreSQL (해마)**: 장기기억 저장소 -- **ChromaDB (연합야)**: 연상 기억, 의미 검색 -- **Redis (작업기억)**: 단기 캐시, 즉각 반응 -- **auth-server (시상하부)**: 신원 확인, 접근 제어 - -**운동기관 (출력층)**: -- **skill-slack**: Slack 메시지 전송 (입/성대) -- **skill-email**: 이메일 발송 (편지 쓰는 손) -- **skill-calendar**: 일정 관리 (계획하는 손) -- **skill-file**: 파일 조작 (물건 다루는 손) - -### 4.2 감각-운동 피드백 루프 - -인체처럼 로빙도 행동하면서 동시에 감각 피드백을 받습니다: - -**1차 루프 (외부 자극-반응)**: -``` -외부 → Gateway(감각) → rb8001(뇌) → Skills(운동) → 외부 -``` - -**2차 루프 (고유감각 피드백)**: -``` -Skills → 실행 결과 → rb8001 → 다음 행동 조정 -``` - -**구체적 피드백 예시**: -- **skill-slack**: 메시지 전송 → message_ts 반환 (위치 감각) -- **skill-email**: 메일 발송 → 발송 성공/실패 (촉각) -- **skill-file**: 파일 생성 → 파일 크기/권한 (압력 감각) - -### 4.3 일반 푸드코트 vs 로빙 푸드코트 - -**일반 푸드코트 (마이크로서비스)**: -- 각 가게가 독립적으로 요리+서빙 -- 가게 간 최소한의 통신 - -**로빙 푸드코트 (중앙 집중식)**: -- **rb8001**: 중앙 통합 조리대 - 모든 요리 결정과 조리 -- **skill-email**: 재료 공급점 - 메일 데이터만 제공 -- **skill-slack**: 배달 전문점 - 완성 요리를 Slack 도시락으로 포장 -- **skill-rag**: 재료 창고 - 문서 보관/검색만 - -**핵심 차이**: -- 일반: 각 서비스가 판단+실행 -- 로빙: rb8001만 판단, 스킬은 단순 실행 - -이러한 구조는 로빙의 베이즈 철학과 일치합니다 - 모든 증거(Evidence)를 중앙에서 종합하여 일관된 사후확률(Posterior)을 도출하고, 이를 각 기관(스킬)이 충실히 실행합니다.