o2o-negosium-original/backend/README.md
민헌 3b1719666f refactor(auth): popup API 리뷰 개선 + 무토큰 테스트 보강
원작성자 컨벤션 대조 리뷰 반영.

- README 엔드포인트 표에 logout·popup/status·popup/hide 추가(누락분 포함)
- user_crud: get_hide_service_info 의 불필요한 bool() 캐스트 제거
  (execute 가 단일 컬럼 select 에서 이미 스칼라 반환 — get_supplier_name 과 동일)
- account: popup 라우터를 me 뒤로 이동(로그인→계정→토큰→내정보→팝업 흐름)
- test_auth: test_popup_hide_without_token 추가(me 섹션과 무토큰 가드 대칭)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:36:37 +09:00

66 lines
3.6 KiB
Markdown
Raw 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.

# Negosium Backend
DerbyMasters_Server 아키텍처를 이식한 FastAPI 골격. 기능은 **JWT id/pw 로그인**만 예시 구현.
실행·테스트·벤치마크 종합은 [레포 최상위 README](../README.md) 참고.
## 디렉토리 구조
```
backend/
├── web_main.py # 엔트리포인트 (uvicorn)
├── config/ # 환경설정 (APP_ENV 별 toml 로드)
├── common/
│ ├── enums.py # ErrorType / DBType / DBWRType / EXCEPTION_*
│ ├── models/gmodel.py # 프로토콜 베이스 (WebPacketProtocol 등)
│ └── database/
│ ├── db_session_manager.py# ★ DB Read/Write + 람다 실행 핵심
│ └── model/models.py # ORM 모델 (tbl_account)
├── crud/user_crud.py # DB 접근 (I*CRUD 인터페이스 + 구현)
├── services/auth_service.py # 비즈니스 로직
└── router/
├── router.py # FastAPI app
└── v1/
├── auth/{account,protocol}.py # 엔드포인트 / Req_·Res_
└── validator/dependencies.py # ★ JWT 발급·검증, 해시, RemoveNoneResponse
```
## 핵심 패턴
- **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#성능--벤치마크)
## 엔드포인트
| Method | Path | 설명 |
|---|---|---|
| POST | `/v1/auth/create` | 계정 생성 (pw bcrypt 해시) |
| POST | `/v1/auth/login` | 로그인, access/refresh 토큰 발급 |
| POST | `/v1/auth/refresh_token` | access 토큰 재발급 (refresh 필요) |
| POST | `/v1/auth/logout` | 로그아웃, 저장 토큰 폐기 (access 토큰 필요) |
| GET | `/v1/auth/me` | 내 정보 (access 토큰 필요) |
| GET | `/v1/auth/popup/status` | 팝업 '안내 보지 않기' 저장 상태 (access 토큰 필요) |
| POST | `/v1/auth/popup/hide` | 팝업 안내 보지 않기 영구 저장 (access 토큰 필요) |
## 실행 / 테스트
```bash
# 레포 최상위에서 docker compose up -d (backend만; DB 는 외부 PostgreSQL). 상세는 루트 README.
cd backend
pip install -r requirements.txt # 실행
python web_main.py # APP_ENV 기본 local
pip install pytest pytest-asyncio httpx # 테스트 도구
python -m pytest
```
- 서버: http://localhost:9300/docs
- 환경: `config.{local,test,docker}.toml` (`APP_ENV` 로 선택, docker 는 DB 호스트=`host.docker.internal`)