o2o-negosium-original/negodata/backend/README.md
Mina Choi 43ac308a5e [docs] negodata: README 갱신 — 테스트 실행법·API 도메인·프론트 포트
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:06:55 +09:00

94 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Negodata Backend
DerbyMasters_Server 아키텍처를 이식한 FastAPI 백엔드. 인증(JWT) 위에 **견적·협력사·상품·견적설정·대시보드·알림·협상카드·회사유저관리** 도메인과 **견적 마감 스케줄러**를 구현.
[negosium-backend](../../backend/README.md) 와 동일 구조이며, 실행·테스트·벤치마크 종합은 [레포 최상위 README](../../README.md) 참고.
## 디렉토리 구조
```
negodata/backend/
├── web_main.py # 엔트리포인트 (uvicorn)
├── config/ # 환경설정 (APP_ENV 별 toml 로드)
├── conftest.py, tests/ # pytest (test DB 자동 create/drop) — 아래 '테스트'
├── common/
│ ├── enums.py # ErrorType / 코드값 enum / EXCEPTION_*
│ ├── models/gmodel.py # 프로토콜 베이스 (WebPacketProtocol 등)
│ └── database/
│ ├── db_session_manager.py# ★ DB Read/Write + 람다 실행 핵심
│ └── model/models.py # ORM 모델 (companies·users·quotations·sessions·items·suppliers·notifications·cards …)
├── crud/ # 도메인별 DB 접근(I*CRUD 인터페이스+구현): quotation·supplier·item·dashboard·notification·card·user …
├── services/ # 비즈니스 로직: quotation·supplier·item·dashboard·notification·company_user·auth·email …
├── scheduler/ # 견적 마감 크론 잡(만료 마감 · 협상종결 마감)
└── router/
├── router.py # FastAPI app (CORS 등)
└── v1/ # 도메인별 라우터: auth·quotation·quotation_setting·supplier·item·card·dashboard·notification·company
└── validator/dependencies.py # ★ JWT 발급·검증, 해시(bcrypt), RemoveNoneResponse, RequireOwner
```
## 핵심 패턴
- **MVC**: router(컨트롤러) → service(로직) → crud(쿼리). crud 는 인터페이스/구현 분리.
- **Depends 주입**: service 가 `IUserCRUD = Depends(UserCRUD)`, router 가 `Depends(AuthService)`, 인증은 `Depends(IsValidAccessToken)`.
- **DB Read/Write 분리**: `DBType` × `DBWRType` 조합마다 별도 async 엔진(조회=Read, 변경=Write).
- **람다 DB 실행**: service 는 세션을 직접 열지 않고 람다를 매니저에 넘긴다. 세션/트랜잭션은 매니저가 책임.
```python
err, acc = await DB_SESSION_MNG.execute_lambda( # 단일 조회
tbl_account.DBType(), DBWRType.DB_READ.value, lambda s: crud.get_account_by_id(s, id))
err = await DB_SESSION_MNG.execute_lambda_run( # 여러 변경 = 1 트랜잭션
[tbl_account.DBType()], [lambda s: crud.update_last_login(s, uid)])
```
- **비동기**: 전 계층 async/await, SQLAlchemy async + asyncpg, task 단위 scoped session.
- **Protocol 규약**: 모든 패킷 `WebPacketProtocol` 상속, `Req_*`/`Res_*`(응답엔 `result: ErrorInfo`), 라우터별 `protocol.py`.
- **ResponseNone**: 응답의 `None` 필드 재귀 제거(`RemoveNoneResponse`).
- **bcrypt 비차단**: `GetHashedPW`/`VerifyPW` 를 `asyncio.to_thread` 로 오프로드(이벤트 루프 비차단). → [벤치마크](../../README.md#성능--벤치마크)
## API 도메인 (`/v1/*`)
전체 스펙은 실행 후 **http://localhost:9400/docs** (Swagger). 주요 도메인:
| prefix | 요약 |
|---|---|
| `/v1/auth` | 로그인 · access 토큰 재발급 · 내 정보(`me`). ※ 무인증 계정 생성은 제거됨 |
| `/v1/company/user` | 최고관리자(OWNER) 전용 — 자기 회사 직원 계정 생성·관리 |
| `/v1/quotation` | 견적 생성·목록·단건·마감·재생성 + 세션·채팅·낙찰결과·카드·초청메일 |
| `/v1/quotation-setting` | 견적 설정(마진율 등) — **유저별** 소유 |
| `/v1/supplier`, `/v1/item` | 협력사 / 상품 CRUD (회사 스코프) |
| `/v1/card` | 협상 카드 |
| `/v1/dashboard` | 요약(회사 전체 + 내 견적) |
| `/v1/notification` | 알림함(목록 · 읽음 처리) |
> 인증 헤더: `Authorization: Bearer <access_token>`. 회사 소유 자원은 토큰의 회사로 스코프되고, 계정 관리는 OWNER 만 가능.
## 실행
```bash
cd negodata/backend
pip install -r requirements.txt
python web_main.py # APP_ENV 기본 local → http://localhost:9400/docs
```
환경: `config.{local,test,prod}.toml` (`APP_ENV` 로 선택).
## 테스트
**테스트는 도커가 아니라 호스트(venv)에서 돌린다** — DB(PostgreSQL)만 도커(`negosium-db`, `127.0.0.1:5432`)면 되고, 앱 컨테이너 안엔 pytest 가 없다. test DB(`negosium_test_db`)는 알아서 만들어졌다 지워지므로 **수동 세팅이 필요 없다.**
```bash
cd negodata/backend
python3 -m venv .venv && source .venv/bin/activate # 최초 1회 (venv 없을 때)
pip install -r requirements.txt # httpx 포함
pip install pytest pytest-asyncio # 테스트 도구(requirements 에 없음)
python -m pytest # 전체 (venv 활성화 상태)
python -m pytest -v # 테스트별 PASS/FAIL
python -m pytest tests/test_company_scope.py # 파일 하나만
python -m pytest -k scope # 이름에 'scope' 든 것만
```
venv 를 활성화(`source .venv/bin/activate`)하지 않으면 `.venv/bin/python -m pytest` 로 직접 지정한다.
(시스템에 `python` 명령이 없거나 pytest 가 venv 밖에 없으면 맨 `python -m pytest` 는 실패한다.)
정상이면 마지막 줄에 `NN passed`.
동작 방식 (전부 [conftest.py](conftest.py) 가 자동 처리 — 손댈 것 없음):
- `APP_ENV``test` 로 자동 설정 → [config.test.toml](config/config.test.toml) 의 **`negosium_test_db`** 사용(dev DB `negosium_db` 와 완전 분리).
- **세션 시작 시 test DB 를 새로 만들고(CREATE), 끝나면 내린다(DROP).** 매번 현재 모델로 새로 빌드돼 스키마가 낡을 일이 없다. 남는 DB 도 없음.
- 테이블은 `create_all` 로 자동 생성, 매 테스트 전 `TRUNCATE` 로 비워 격리.
- 안전가드: 이름에 `test` 없는 DB 는 만들지도 지우지도 않는다(실 DB 보호).
> 즉 새로 clone 받은 팀원도 **Postgres 만 켜져 있으면 `python -m pytest` 한 방**이면 끝.