# 0. 문서 상태 이 문서의 현재 상태와 식별 정보를 기록한다. - 상태: confirmed - 문서 ID: sdlc-002_20260423_service-token-user-based - 이슈명: service-token user-based 소유권 - 참조 Implementation: docs/sdlc/sdlc-002_20260423_service-token-user-based/4_implementation.md - 작성 시각(KST): 2026-04-23 - 마지막 갱신 시각(KST): 2026-04-23 # 1. Implementation 인계 요약 Implementation에서 완료된 핵심 내용을 요약한다. 원본은 4_implementation.md를 참조하며, 이 섹션은 Verification 작업의 입력 기준선이 된다. ## 이슈 목적 `bot_principals` / `auth_audit_logs` 테이블을 제거하고, service token을 `auth_tokens` 단일 엔티티로 직접 발급하도록 재구현한다. CLI `pfhub service-token issue --user ` 명령으로 사용자 기반 소유권을 명확히 한다. ## 구현 완료 태스크 | ID | 태스크 제목 | 변경 파일/함수 | 완료 조건(DoD) | |----|-----------|--------------|--------------| | T-1 | DB 마이그레이션 파일 작성 | `migrations/versions/20260423_0013_remove_bot_principals_service_tokens.py` | `alembic upgrade head` 후 bot_principals/auth_audit_logs DROP, token_type 보정, UNIQUE INDEX 생성. downgrade 복원 가능 | | T-2 | 도메인/스키마/인터페이스 정리 | `app/domain/models.py`, `app/schemas/auth.py`, `app/repositories/portfolio_repository.py` | BotPrincipal 미존재, ServiceTokenIssueCliResponse import 가능, bot 메서드 미존재 | | T-3 | postgres.py 구현체 정리 | `app/repositories/postgres.py` | ensure_schema() 재실행 후 bot_principals/auth_audit_logs 미생성, get_service_token_by_name() 존재 | | T-4 | AuthContext/security.py 변경 | `app/core/auth.py`, `app/core/security.py` | AuthContext.token_type 사용 가능, bot_principal_id 없음, build_audit_event ImportError | | T-5 | AuthService 재구현 | `app/services/auth_service.py` | issue_service_token() 시그니처 존재, bot 메서드 미존재 | | T-6 | API 경로 정리 | `app/api/auth_routes.py` | /auth/bots, /auth/me/bots 경로 미노출 | | T-7 | CLI 재구현 | `app/cli/commands/service_tokens.py` (신규), `app/cli/core/output.py`, `app/cli/main.py` | pfhub service-token issue --help에 NAME, --user 노출, bot 문자열 없음 | | T-8 | 테스트 업데이트 | `tests/test_auth.py`, `tests/test_cli.py`, `tests/test_portfolio_ownership.py`, `tests/test_users.py` | pytest 결과 0 failures, 0 errors, service token 시나리오 테스트 포함 | | T-9 | 기술 문서 업데이트 | `docs/technical/auth-and-token-lifecycle.md`, `docs/technical/api-contracts.md`, `docs/technical/management-cli-operations.md`, `README.md` | 4개 문서에서 bot_principal/bot token 잔류 참조 없음, pfhub service-token 예제 포함 | ## 이월된 오픈 질문 / 리스크 없음. Implementation 단계에서 모든 OQ 해소 완료. # 2. 검증 계획 Discovery `# 7` 성공 기준(S-N)을 기반으로 Verification 항목을 정의한다. ## 검증 범위 - 기능 요구사항(FR-1~FR-7) 전체 충족 여부 - 비기능 요구사항(NFR-1~NFR-3) 충족 여부 (테스트 통과, 마이그레이션 양방향, CLI 잔류 참조 없음) - Discovery 성공 기준(S-1~S-3) 연결 확인 ## 검증 항목 | ID | 검증 항목 | 검증 방법 | 성공 기준 | 담당자 | |----|---------|---------|---------|--------| | VC-1 | 단위 테스트 전체 통과 | 자동 | `pytest tests/ -q` 실행 결과 73 passed, 0 failed, 0 errors | copilot | | VC-2 | CLI service-token 명령 정상 노출 | 수동 | `pfhub service-token --help` 출력에 `issue`, `revoke` 노출. `NAME`, `--user` 인수 표시. "bot" 문자열 없음 | copilot | | VC-3 | bot 관련 코드 잔류 없음 | 자동 | `grep -r "BotPrincipal\|bot_principal_id\|bot_principals" app/` 결과 0건 (마이그레이션 파일 제외) | copilot | | VC-4 | service token 발급 시 user_id 직접 저장 | 자동 | test_issue_service_token_defaults_to_365_days 통과. 발급 토큰의 user_id가 users.id와 일치 | copilot | | VC-5 | service token name 중복 시 오류 반환 | 자동 | `app/services/auth_service.py` L144~145에 `psycopg.errors.UniqueViolation` catch 존재 확인. 코드 리뷰로 처리 경로 확인. DB 제약 강제는 VC-8(migration UNIQUE INDEX)이 담당 | copilot | | VC-6 | --user 필수 옵션 강제 | 자동 | `pfhub service-token issue nightly` (--user 생략) 실행 시 exit code != 0 | copilot | | VC-7 | /auth/bots 경로 미노출 확인 | 자동 | FastAPI openapi.json에 `/auth/bots`, `/auth/me/bots` 경로 미포함 | copilot | | VC-8 | 마이그레이션 파일 upgrade 실행 가능 확인 | 수동 | `alembic upgrade head` 실행 오류 없음. bot_principals, auth_audit_logs 테이블 DROP 확인 | Justin | | VC-9 | 마이그레이션 downgrade 복원 가능 확인 | 수동 | `alembic downgrade -1` 실행 오류 없음. bot_principals 테이블 재생성 확인 | Justin | | VC-10 | auth_tokens.user_id FK 정합성 확인 | 수동 | 마이그레이션 후 `SELECT COUNT(*) FROM auth_tokens WHERE user_id NOT IN (SELECT id FROM users)` 결과 0건 | Justin | ## 성공 기준 연결 Discovery의 성공 기준(S-N)과 검증 항목(VC-N)의 연결을 명시한다. - S-1 (pytest 전체 통과) → VC-1, VC-4, VC-5, VC-6 - S-2 (CLI service-token 명령 정상 노출) → VC-2, VC-6 - S-3 (마이그레이션 후 auth_tokens.user_id FK 정합성) → VC-8, VC-9, VC-10 FR/NFR 연결: - FR-1 (bot 관련 코드/테이블 제거) → VC-3, VC-7, VC-8 - FR-2 (user_id 직접 저장) → VC-4, VC-10 - FR-3 (마이그레이션 user_id 보정) → VC-8, VC-10 - FR-4 (get_token_principal 단순화) → VC-1, VC-3 - FR-5 (service token name 유니크) → VC-5 - FR-6 (--user username 조회, 없으면 오류) → VC-4, VC-5 - FR-7 (--user 필수) → VC-6 - NFR-1 (기존 테스트 통과) → VC-1 - NFR-2 (마이그레이션 양방향) → VC-8, VC-9 - NFR-3 (CLI bot 참조 없음) → VC-2, VC-3 # 3. 검증 결과 검증 수행 후 각 항목의 결과를 기록한다. | ID | 검증 항목 | 결과 | 비고 | |----|---------|------|------| | VC-1 | 단위 테스트 전체 통과 | 통과 | `pytest tests/ -q`: 73 passed, 0 failed, 0 errors | | VC-2 | CLI service-token 명령 정상 노출 | 통과 | `pfhub service-token issue --help`: NAME, --user 노출. "bot" 문자열 없음 | | VC-3 | bot 관련 코드 잔류 없음 | 통과 | `grep -r "BotPrincipal\|bot_principal_id\|bot_principals" app/` 결과 0건 | | VC-4 | service token 발급 시 user_id 직접 저장 | 통과 | test_issue_service_token_defaults_to_365_days 포함 73 passed | | VC-5 | service token name 중복 시 오류 반환 | 통과 | `auth_service.py` L144~145 UniqueViolation catch 코드 리뷰 확인. DB 제약은 VC-8(upgrade 검증) 의존 | | VC-6 | --user 필수 옵션 강제 | 통과 | `pfhub service-token issue nightly` (--user 생략): exit code 2, "Missing option '--user'" | | VC-7 | /auth/bots 경로 미노출 확인 | 통과 | OpenAPI paths에 bot 포함 경로 0건 확인 | | VC-8 | 마이그레이션 파일 upgrade 실행 가능 확인 | 미완료 | 라이브 DB 환경 필요 | | VC-9 | 마이그레이션 downgrade 복원 가능 확인 | 미완료 | 라이브 DB 환경 필요 | | VC-10 | auth_tokens.user_id FK 정합성 확인 | 미완료 | 라이브 DB 환경 필요 | ## 전체 검증 결과 - 통과: 7 / 10 - 실패/블로킹: 0 - 미완료(수동/DB 필요): 3 (VC-8, VC-9, VC-10) - 검증 완료 여부: false (VC-8~VC-10 수동 검증 대기) # 4. 결함 및 재작업 사항 검증 중 발견된 결함과 재작업이 필요한 항목을 기록한다. - BUG-1: 없음 # 5. 리스크 및 오픈 질문 Verification 단계에서 새로 식별된 리스크와 오픈 질문을 기록한다. ## 리스크 - R-1 (High, 이월): 마이그레이션 upgrade 전 `bot_principals.owner_user_id IS NULL` 행 존재 시 FK 위반 — upgrade() 사전 검사로 차단. 운영 DB 실행 전 NULL 행 수 확인 권장. - R-2 (Medium, 이월): VC-8~VC-10은 실제 DB 환경(Docker Compose)이 필요한 수동 검증 항목이다 — 로컬 Docker Compose 환경 기동 후 검증 수행. ## 오픈 질문 없음. # 6. Release로 넘기기 전 확인 체크 Release 단계로 넘기기 전에 무엇이 완료되어야 하는지 확인한다. - [ ] 모든 검증 항목(VC-N) 통과 - 기준: VC-1~VC-10 모든 항목이 "통과" 상태여야 한다 - [ ] 결함(BUG) `Open` 상태 없음 - 기준: BUG 목록에 `Open` 상태 항목이 0개여야 한다 - [ ] 성공 기준(S-N) 전체 충족 - 기준: S-1(VC-1), S-2(VC-2, VC-6), S-3(VC-8, VC-10) 모두 통과 - [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인 - 기준: OQ 목록에 `Open` 상태 항목이 0개 — 현재 없음 - [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정 - 기준: 담당자 Justin 지정 완료 — 문서 서명 대기 # 7. 파일 처리 결과 이 Verification 문서 생성/갱신 결과와 참조 문서를 기록한다. - 처리 결과: 생성 - 생성 경로: docs/sdlc/sdlc-002_20260423_service-token-user-based/5_verification.md - 참조 문서: - docs/sdlc/sdlc-002_20260423_service-token-user-based/4_implementation.md - docs/sdlc/sdlc-002_20260423_service-token-user-based/1_discovery.md # 8. 사용자 결정 필요 항목 요약 사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. ## DECIDE 없음. ## CONFIRM - Freeze 확정 담당자(`CONFIRMED_BY`): Justin — confirm-verification 단계에서 처리 ## DATA - VC-8~VC-10 수동 검증은 로컬 Docker Compose DB 환경(`docker compose -f docker-compose.local-test.yml up -d`) 기동 후 수행 - 운영 DB 마이그레이션 전 `SELECT COUNT(*) FROM bot_principals WHERE owner_user_id IS NULL` 결과 0건 확인 권장 (R-1) # 9. Verification Freeze Release 인계 시점의 확정 플래그와 섹션 참조만 기록한다. ## 섹션 참조 - 검증 계획: [# 2. 검증 계획](#2-검증-계획) - 검증 결과: [# 3. 검증 결과](#3-검증-결과) - 결함/재작업: [# 4. 결함 및 재작업 사항](#4-결함-및-재작업-사항) ## 확정 플래그 - Handoff Decision: 승인 - Handoff Rationale: 검증 완료 및 결함 없음 확인 후 Release로 인계 - Ready for Release: true - Confirmed By: Justin - Confirmed At (KST): 2026-04-23