docs(sdlc): sdlc-002 service-token user-based Discovery/Planning/Index/summary 문서 초안
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
# 0. 문서 상태
|
||||
이 문서의 현재 상태와 식별 정보를 기록한다.
|
||||
|
||||
- 상태: draft
|
||||
- 문서 ID: sdlc-002_20260423_service-token-user-based
|
||||
- 이슈명: service-token user-based 소유권
|
||||
- 참조 Discovery: docs/sdlc/sdlc-002_20260423_service-token-user-based/1_discovery.md
|
||||
- 작성 시각(KST): 2026-04-23
|
||||
- 마지막 갱신 시각(KST): 2026-04-23
|
||||
|
||||
# 1. Discovery 인계 요약
|
||||
Discovery에서 확정된 핵심 내용을 요약한다. 원본은 1_discovery.md를 참조하며, 이 섹션은 Planning 작업의 입력 기준선이 된다.
|
||||
|
||||
## 이슈 목적
|
||||
`bot_principals` 테이블과 audit log를 제거하고, service token을 `auth_tokens` 단일 엔티티로 직접 발급하도록 재구현한다. CLI 명령 `pfhub service-token issue <name> --user <username>`으로 사용자 기반 소유권을 명확히 한다.
|
||||
|
||||
## 확정 범위
|
||||
- In Scope
|
||||
- `bot_principals` 테이블 및 관련 코드 완전 제거 (도메인/스키마/서비스/리포지터리/API/CLI, 약 15개 파일, DB 마이그레이션 포함)
|
||||
- `auth_audit_logs` 테이블 및 audit 함수(`build_audit_event`, `record_audit_event`, `_record_audit_event`) 삭제
|
||||
- `auth_tokens.token_type = "bot"` → `"service"` rename (기존 데이터 UPDATE 포함)
|
||||
- `auth_tokens.user_id` 소유권 보정 및 `bot_principal_id` 컬럼 DROP (마이그레이션)
|
||||
- service token 직접 발급 CLI 구현 (`pfhub service-token issue <name> --user <username>`)
|
||||
- `AuthContext.token_type` 필드 추가, service token 가드 교체
|
||||
- 기존 테스트 업데이트 및 신규 검증 테스트 추가
|
||||
- 기술 문서(`api-contracts.md`, `auth-and-token-lifecycle.md`, `README.md`) 업데이트
|
||||
- Out of Scope
|
||||
- 외부 IAM(OAuth/OIDC) 연동
|
||||
- service_principal 셀프서비스 UI/포털 제공
|
||||
- token 권한 모델(read/trade-write/admin) 변경
|
||||
- API 경로 변경에 대한 하위 호환성 레이어(alias route) 제공 — 외부 클라이언트 없으므로 즉시 제거
|
||||
|
||||
## 기능 요구사항 (FR) 요약
|
||||
- FR-1: `bot_principals` 테이블, `BotPrincipal` 모델, 관련 스키마/서비스/리포지터리/API, `auth_tokens.bot_principal_id` 컬럼, `auth_audit_logs` 테이블, audit 함수 전체 제거
|
||||
- FR-2: service 토큰 발급 시 `auth_tokens.user_id`에 username에서 조회한 실제 user_id 직접 저장
|
||||
- FR-3: 기존 bot 토큰 `user_id` → `bot_principal.owner_user_id`로 마이그레이션 보정 후 `bot_principals` DROP
|
||||
- FR-4: `get_token_principal()` COALESCE/JOIN 제거 → `t.user_id` 단순 참조
|
||||
- FR-5: service token `name` 유니크, 중복 시 오류 반환
|
||||
- FR-6: `--user <username>` → users 테이블 조회 → `auth_tokens.user_id`로 저장, 존재하지 않는 username이면 오류
|
||||
- FR-7: `--user` 필수 옵션, 생략 시 오류
|
||||
|
||||
## 비기능 요구사항 (NFR) 요약
|
||||
- NFR-1: 변경 전후 모든 테스트 통과 (pytest 기준)
|
||||
- NFR-2: DB 마이그레이션 upgrade/downgrade 양방향 지원
|
||||
- NFR-3: 변경 완료 후 CLI help 텍스트 및 오류 메시지에서 "bot" 잔류 참조 없음
|
||||
|
||||
## 이월된 오픈 질문 / 리스크
|
||||
Discovery에서 Deferred 처리된 항목 없음. 모든 OQ 해소 완료 (OQ-1 Answered, OQ-2/3/5 Dropped, OQ-4 Answered).
|
||||
|
||||
# 2. 작업 분해 (Task Breakdown)
|
||||
FR/NFR 각각을 실행 가능한 단위 태스크로 분해한다.
|
||||
|
||||
| ID | 태스크 제목 | 유형 | 참조 요구사항 | 의존 태스크 | 담당자 |
|
||||
|-----|-----------|------|--------------|------------|--------|
|
||||
| T-1 | DB 마이그레이션 파일 작성 | 개발 | FR-3, NFR-2 | - | {{TASK_OWNER_1:TBD}} |
|
||||
| T-2 | 도메인/스키마/인터페이스 정리 | 개발 | FR-1 | - | {{TASK_OWNER_2:TBD}} |
|
||||
| T-3 | postgres.py 구현체 정리 | 개발 | FR-1, FR-4 | T-2 | {{TASK_OWNER_3:TBD}} |
|
||||
| T-4 | AuthContext/security.py 변경 | 개발 | FR-1 (OQ-4) | T-2 | {{TASK_OWNER_4:TBD}} |
|
||||
| T-5 | AuthService 재구현 | 개발 | FR-2, FR-5, FR-6, FR-7 | T-3, T-4 | {{TASK_OWNER_5:TBD}} |
|
||||
| T-6 | API 경로 정리 | 개발 | FR-1 | T-5 | {{TASK_OWNER_6:TBD}} |
|
||||
| T-7 | CLI 재구현 | 개발 | FR-5, FR-6, FR-7, NFR-3 | T-5 | {{TASK_OWNER_7:TBD}} |
|
||||
| T-8 | 테스트 업데이트 | 개발 | NFR-1 | T-5, T-6, T-7 | {{TASK_OWNER_8:TBD}} |
|
||||
| T-9 | 기술 문서 업데이트 | 문서 | - | T-7, T-8 | {{TASK_OWNER_9:TBD}} |
|
||||
|
||||
## 태스크 완료 조건 (DoD)
|
||||
|
||||
- T-1: `alembic upgrade head` 실행 시 `bot_principals`, `auth_audit_logs` 테이블이 DROP되고, `auth_tokens.user_id`가 users FK를 정상 참조하며, `token_type = 'service'`로 UPDATE됨. `alembic downgrade -1` 실행 시 역방향 복원 가능.
|
||||
- T-2: `BotPrincipal`, `AuthToken.bot_principal_id`, `TokenPrincipal.bot_principal_id`, `CreateBotPrincipalRequest`, `BotPrincipalResponse`, `BotTokenIssueCliResponse.bot_id`, `BotTokenRevokeCliResponse.bot_id` 삭제/정리 완료. `PortfolioRepository` 프로토콜에서 bot_principal 메서드 제거. `python -c "from app.domain.models import AuthToken"` import 오류 없음.
|
||||
- T-3: `ensure_schema()` 재실행 시 `bot_principals`, `auth_audit_logs` 테이블 미생성. `get_token_principal()`이 단일 테이블 `auth_tokens`만 참조. `record_audit_event()` 메서드 삭제 완료.
|
||||
- T-4: `AuthContext`에 `token_type: str | None` 필드 추가 완료. `_resolve_auth_context()`가 DB 토큰 조회 시 `principal.token_type`을 전달. `build_audit_event()` 함수 삭제 완료.
|
||||
- T-5: `issue_service_token(name, username, ...)` 메서드가 존재하고, username → user_id 조회 후 `auth_tokens` 직접 발급. bot_principal 관련 메서드 전체 삭제. `issue_client_token`, `list_tokens`, `revoke_token`의 가드가 `auth_context.token_type == "service"` 체크로 교체됨.
|
||||
- T-6: FastAPI 앱 기동 후 `/auth/bots`, `/auth/me/bots` 경로 미노출. `curl /openapi.json`에서 해당 경로 없음.
|
||||
- T-7: `pfhub service-token issue --help`에서 `<name>`, `--user` 인수 노출. help 텍스트에 "bot" 문자열 없음. `pfhub service-token issue kis-token --user justin` 명령이 토큰을 발급하고 출력.
|
||||
- T-8: `pytest` 실행 결과 0 failures, 0 errors. service token 발급/폐기 시나리오 테스트 신규 추가.
|
||||
- T-9: `auth-and-token-lifecycle.md`, `api-contracts.md`, `management-cli-operations.md`, `README.md`에서 "bot_principal", "bot token" 잔류 참조 없음. `pfhub service-token` 명령 예제 포함.
|
||||
|
||||
# 3. 공수 추정
|
||||
|
||||
| ID | 태스크 제목 | 추정 공수 | 추정 단위 | 비고 |
|
||||
|-----|-----------|---------|---------|------|
|
||||
| T-1 | DB 마이그레이션 파일 작성 | 2 | SP | user_id 보정 로직 + downgrade 구현 포함 |
|
||||
| T-2 | 도메인/스키마/인터페이스 정리 | 2 | SP | 다수 파일이나 삭제 위주 — 복잡도 낮음 |
|
||||
| T-3 | postgres.py 구현체 정리 | 2 | SP | ensure_schema DDL 제거 + get_token_principal 단순화 — 주의 필요 |
|
||||
| T-4 | AuthContext/security.py 변경 | 1 | SP | 변경 범위 소규모 |
|
||||
| T-5 | AuthService 재구현 | 3 | SP | username 조회 신규 로직 + 가드 교체 + bot 메서드 삭제 |
|
||||
| T-6 | API 경로 정리 | 1 | SP | 삭제 위주 |
|
||||
| T-7 | CLI 재구현 | 2 | SP | 파일 rename + 인수 변경 + 출력 함수 rename |
|
||||
| T-8 | 테스트 업데이트 | 3 | SP | 4개 파일 업데이트 + 신규 시나리오 추가 |
|
||||
| T-9 | 기술 문서 업데이트 | 1 | SP | 내용 갱신 위주 |
|
||||
|
||||
- 총 추정 공수: 17 SP
|
||||
- 추정 신뢰도: Medium
|
||||
- 추정 근거: sdlc-001(CLI bot token 발급 구현, 약 8 SP)과 유사한 레이어 범위이나 삭제 중심 + 기존 테스트 보정 작업이 추가됨. T-5/T-8이 가장 불확실 요소.
|
||||
|
||||
# 4. 일정 계획
|
||||
|
||||
## 마일스톤
|
||||
| 마일스톤 | 목표 날짜 | 포함 태스크 | 완료 조건 |
|
||||
|---------|---------|-----------|---------|
|
||||
| M-1: 정리 완료 | {{MILESTONE_1_DATE:TBD}} | T-1, T-2, T-3, T-4 | bot_principals/auth_audit_logs 관련 코드·스키마·DDL 전체 삭제, import 오류 없음 |
|
||||
| M-2: 구현·검증 완료 | {{MILESTONE_2_DATE:TBD}} | T-5, T-6, T-7, T-8, T-9 | pytest 0 failures, CLI --help 정상 노출, 문서 갱신 완료 |
|
||||
|
||||
## 이터레이션 계획
|
||||
- 이터레이션 단위: 없음(단일 배포)
|
||||
- 이터레이션 1 ({{ITERATION_1_PERIOD:TBD}}): T-1, T-2 (병렬 가능)
|
||||
- 이터레이션 2 ({{ITERATION_2_PERIOD:TBD}}): T-3, T-4 (T-2 완료 후)
|
||||
- 이터레이션 3 ({{ITERATION_3_PERIOD:TBD}}): T-5 (T-3, T-4 완료 후)
|
||||
- 이터레이션 4 ({{ITERATION_4_PERIOD:TBD}}): T-6, T-7 (병렬 가능, T-5 완료 후)
|
||||
- 이터레이션 5 ({{ITERATION_5_PERIOD:TBD}}): T-8, T-9 (T-6, T-7 완료 후)
|
||||
|
||||
# 5. 의존성 및 제약
|
||||
|
||||
## 태스크 간 의존성
|
||||
- T-1은 T-2와 독립적으로 병렬 진행 가능
|
||||
- T-3, T-4는 T-2 완료 후 시작 가능 (인터페이스 정리 선행 필요)
|
||||
- T-5는 T-3, T-4 완료 후 시작 가능 (repository 메서드, AuthContext 변경 선행 필요)
|
||||
- T-6, T-7은 T-5 완료 후 병렬 시작 가능
|
||||
- T-8은 T-5, T-6, T-7 완료 후 시작 가능
|
||||
- T-9는 T-8 완료 후 시작 (최종 동작 확인 후 문서화)
|
||||
|
||||
## 외부 의존성
|
||||
- DB 접속 가능 환경 필요 (마이그레이션 실행 및 S-3 검증)
|
||||
- pytest 실행 환경 (Docker Compose 또는 로컬 DB)
|
||||
|
||||
## 기술/환경 제약
|
||||
- Alembic 마이그레이션 파일명 패턴: `YYYYMMDD_NNNN_<slug>.py` (기존 패턴 준수)
|
||||
- `ensure_schema()`는 테스트 환경에서도 호출되므로 DDL 제거 후 테스트 환경에서 재실행 검증 필요
|
||||
- `app/cli/commands/bot_tokens.py` → `service_tokens.py` 파일 rename 시 `app/cli/main.py`의 import 경로도 함께 수정 필요
|
||||
|
||||
# 6. 검증 계획 개요
|
||||
Discovery의 성공 기준과 연결해 검증 방법 초안을 적는다.
|
||||
|
||||
| 성공 기준 | 검증 방법 초안 | 담당자 |
|
||||
|---------|-------------|--------|
|
||||
| S-1: pytest 0 failures | `pytest` 전체 실행 후 결과 확인 | {{VERIFY_OWNER_1:TBD}} |
|
||||
| S-2: pfhub service-token --help 정상 노출 | 터미널에서 `pfhub service-token issue --help` 실행, `<name>` 및 `--user` 인수 확인 | {{VERIFY_OWNER_2:TBD}} |
|
||||
| S-3: auth_tokens.user_id FK 정합성 | 마이그레이션 upgrade 후 `SELECT COUNT(*) FROM auth_tokens WHERE user_id NOT IN (SELECT id FROM users)` = 0 확인 | {{VERIFY_OWNER_3:TBD}} |
|
||||
|
||||
# 7. 리스크
|
||||
|
||||
## 리스크
|
||||
- R-1 (High): 기존 `auth_tokens`에 `user_id = bot_principal.id`인 행의 `bot_principal.owner_user_id`가 NULL이거나 `users` 테이블에 없으면 마이그레이션 FK 위반 발생.
|
||||
- 완화 방안: T-1 구현 시 upgrade 전 사전 검증 쿼리(`SELECT COUNT(*) FROM bot_principals WHERE owner_user_id IS NULL`) 포함. NULL 존재 시 오류 후 중단.
|
||||
- R-2 (Medium): 약 15개 파일 rename 중 일부 누락 시 런타임 `AttributeError`/`NameError`.
|
||||
- 완화 방안: T-8 완료 후 `grep -r "bot_principal\|BotPrincipal\|bot_token\|token_type.*bot"` 잔류 참조 검사. 테스트 전체 통과를 gate로 설정.
|
||||
- R-3 (Medium): ensure_schema() DDL 제거 누락 시 재실행 때 bot_principals 테이블 재생성.
|
||||
- 완화 방안: T-3 DoD에 ensure_schema() 재실행 검증 포함.
|
||||
|
||||
## 가정
|
||||
- PA-1: 현재 운영 중인 bot 토큰은 모두 `owner_user_id`가 설정된 `bot_principal`에 연결되어 있다 (migration 0011 backfill 완료). user_id 보정 후 DROP이 안전하다.
|
||||
- PA-2: `app/cli/main.py`의 `name="service-token"` 등록은 이미 완료되어 있으므로 T-7에서 import 경로만 수정하면 된다.
|
||||
- PA-3: 테스트 환경에서 Docker Compose DB 접속이 가능하다.
|
||||
|
||||
## 오픈 질문
|
||||
없음. Discovery에서 모든 OQ 해소 완료.
|
||||
|
||||
# 8. Design으로 넘기기 전 확인 체크
|
||||
|
||||
- [ ] 전체 태스크 목록 확정 및 담당자 지정
|
||||
- 기준: T-1 ~ T-9 모든 태스크에 담당자(`{{TASK_OWNER_N}}`)가 지정되어야 한다
|
||||
- [ ] 공수 추정 완료 및 총 공수 확인
|
||||
- 기준: 17 SP — 모든 태스크 추정값 채움 완료
|
||||
- [ ] 마일스톤 및 이터레이션 계획 확정
|
||||
- 기준: M-1, M-2 목표 날짜(`{{MILESTONE_N_DATE}}`)가 확정되어야 한다
|
||||
- [ ] 외부 의존성 해소 계획 확인
|
||||
- 기준: DB 접속 환경 및 pytest 실행 환경 확인
|
||||
- [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||
- 기준: 없음 — Discovery에서 전체 해소 완료
|
||||
- [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||
- 기준: 담당자 Justin 지정 완료 — 문서 서명 대기
|
||||
|
||||
# 9. 파일 처리 결과
|
||||
|
||||
- 처리 결과: 생성
|
||||
- 생성 경로: docs/sdlc/sdlc-002_20260423_service-token-user-based/2_planning.md
|
||||
- 참조 문서:
|
||||
- docs/sdlc/sdlc-002_20260423_service-token-user-based/1_discovery.md
|
||||
- app/domain/models.py
|
||||
- app/core/auth.py
|
||||
- app/core/security.py
|
||||
- app/services/auth_service.py
|
||||
- app/repositories/postgres.py
|
||||
- app/repositories/portfolio_repository.py
|
||||
- app/schemas/auth.py
|
||||
- app/api/auth_routes.py
|
||||
- app/cli/commands/bot_tokens.py
|
||||
- app/cli/core/output.py
|
||||
|
||||
# 10. 사용자 결정 필요 항목 요약
|
||||
|
||||
## DECIDE
|
||||
- (없음) — Discovery 범위에서 모든 설계 결정 완료
|
||||
|
||||
## CONFIRM
|
||||
- T-1 ~ T-9 담당자 지정: 현재 전부 `TBD`. 단독 개발자 환경이면 Justin으로 일괄 지정 가능
|
||||
- M-1, M-2 목표 날짜 및 이터레이션 기간 확정
|
||||
|
||||
## DATA
|
||||
- 마이그레이션 실행 전 운영 DB `bot_principals` 행 수 및 `owner_user_id NULL` 행 수 확인 필요 (R-1 완화)
|
||||
|
||||
# 11. Planning Freeze
|
||||
|
||||
## 섹션 참조
|
||||
- 작업 분해: [# 2. 작업 분해](#2-작업-분해-task-breakdown)
|
||||
- 공수/일정: [# 3. 공수 추정](#3-공수-추정), [# 4. 일정 계획](#4-일정-계획)
|
||||
- 의존성: [# 5. 의존성 및 제약](#5-의존성-및-제약)
|
||||
- 검증 계획: [# 6. 검증 계획 개요](#6-검증-계획-개요)
|
||||
- 리스크/오픈 질문: [# 7. 리스크](#7-리스크)
|
||||
- 인계 전 확인: [# 8. Design으로 넘기기 전 확인 체크](#8-design으로-넘기기-전-확인-체크)
|
||||
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
||||
|
||||
## Freeze 플래그
|
||||
- Handoff Decision: 보류
|
||||
- Handoff Rationale: 담당자(TASK_OWNER_1~9), 마일스톤 날짜(MILESTONE_1_DATE, MILESTONE_2_DATE), 이터레이션 기간(ITERATION_1~5_PERIOD)이 TBD 상태. 확정 후 인계 가능.
|
||||
- Ready for Design: false
|
||||
- Confirmed By: {{CONFIRMED_BY:TBD}}
|
||||
- Confirmed At (KST): {{CONFIRMED_AT_KST:TBD}}
|
||||
Reference in New Issue
Block a user