Files
portfolio-hub/docs/sdlc/sdlc-002_20260423_service-token-user-based/4_implementation.md
T
2026-04-23 09:56:08 +00:00

15 KiB

0. 문서 상태

이 문서의 현재 상태와 식별 정보를 기록한다.

  • 상태: confirmed
  • 문서 ID: sdlc-002_20260423_service-token-user-based
  • 이슈명: service-token user-based 소유권
  • 참조 Design: docs/sdlc/sdlc-002_20260423_service-token-user-based/3_design.md
  • 작성 시각(KST): 2026-04-23
  • 마지막 갱신 시각(KST): 2026-04-23 (confirmed)

1. Design 인계 요약

Design에서 확정된 핵심 내용을 요약한다. 원본은 3_design.md를 참조하며, 이 섹션은 Implementation 작업의 입력 기준선이 된다.

이슈 목적

bot_principals / auth_audit_logs 테이블을 제거하고, service token을 auth_tokens 단일 엔티티로 직접 발급하도록 재구현한다. CLI pfhub service-token issue <name> --user <username> 명령으로 사용자 기반 소유권을 명확히 한다.

확정 태스크 및 변경 대상

ID 태스크 제목 변경 대상 파일/함수 담당자
T-1 DB 마이그레이션 파일 작성 migrations/versions/20260423_0013_remove_bot_principals_service_tokens.py (신규) copilot
T-2 도메인/스키마/인터페이스 정리 app/domain/models.py, app/schemas/auth.py, app/repositories/portfolio_repository.py copilot
T-3 postgres.py 구현체 정리 app/repositories/postgres.py copilot
T-4 AuthContext/security.py 변경 app/core/auth.py, app/core/security.py copilot
T-5 AuthService 재구현 app/services/auth_service.py copilot
T-6 API 경로 정리 app/api/auth_routes.py copilot
T-7 CLI 재구현 app/cli/commands/bot_tokens.py (삭제), app/cli/commands/service_tokens.py (신규), app/cli/core/output.py, app/cli/main.py copilot
T-8 테스트 업데이트 tests/test_auth.py, tests/test_cli.py, tests/test_portfolio_ownership.py, tests/test_users.py copilot
T-9 기술 문서 업데이트 docs/technical/auth-and-token-lifecycle.md, docs/technical/api-contracts.md, docs/technical/management-cli-operations.md, README.md copilot

이월된 오픈 질문 / 리스크

없음. Design 단계에서 모든 OQ 해소 완료.

2. 구현 계획

태스크(T-N)별 구현 순서와 접근 방법을 기술한다.

구현 순서

태스크 간 의존 관계에 따른 순서:

  1. T-1 (DB 마이그레이션 파일 작성) — T-2와 병렬 가능, 독립적
  2. T-2 (도메인/스키마/인터페이스 정리) — T-1과 병렬 가능
  3. T-3 (postgres.py 구현체 정리) — T-2 완료 후 (인터페이스 변경 선행 필요)
  4. T-4 (AuthContext/security.py 변경) — T-2 완료 후 (모델 변경 선행 필요)
  5. T-5 (AuthService 재구현) — T-3, T-4 완료 후
  6. T-6 (API 경로 정리) — T-5 완료 후, T-7과 병렬 가능
  7. T-7 (CLI 재구현) — T-5 완료 후, T-6과 병렬 가능
  8. T-8 (테스트 업데이트) — T-5, T-6, T-7 완료 후
  9. T-9 (기술 문서 업데이트) — T-8 완료 후

T-1: DB 마이그레이션 파일 작성

  • 구현 방법: Alembic 마이그레이션 파일을 신규 생성한다. upgrade() 내 첫 스텝은 bot_principals.owner_user_id IS NULL 검사로, NULL이 있으면 MigrationError를 raise하고 중단한다. 이후 user_id 보정 → token_type rename → unique index → bot_principal_id 컬럼 DROP → auth_audit_logs DROP → bot_principals DROP 순으로 실행한다. downgrade()는 역방향 구조 복원만 수행한다.
  • 변경 대상: migrations/versions/20260423_0013_remove_bot_principals_service_tokens.py (신규 생성)
  • 완료 조건: alembic upgrade head 실행 시 bot_principals, auth_audit_logs 테이블이 DROP되고, auth_tokens.user_id가 실제 user FK를 참조하며, token_type = 'service'로 업데이트됨. alembic downgrade -1 실행 시 역방향 복원 가능.
  • 주의사항: op.execute() SQL은 파라미터 바인딩 없이 직접 실행 (Alembic DDL 관례). NULL 검사 쿼리는 op.get_bind().execute() 패턴 사용.

T-2: 도메인/스키마/인터페이스 정리

  • 구현 방법: 파일별 삭제/변경을 수행한다. models.py에서 BotPrincipal, bot_principal_id 필드를 삭제한다. schemas/auth.py에서 bot 관련 스키마를 삭제하고 ServiceTokenIssueCliResponse, ServiceTokenRevokeCliResponse를 추가한다. portfolio_repository.py에서 bot 관련 메서드 시그니처를 삭제하고 get_service_token_by_name()을 추가한다.
  • 변경 대상: app/domain/models.py, app/schemas/auth.py, app/repositories/portfolio_repository.py
  • 완료 조건: python -c "from app.domain.models import AuthToken" ImportError 없음. BotPrincipal 클래스 미존재. ServiceTokenIssueCliResponse import 가능.
  • 주의사항: AuthTokenResponse.token_type Literal 변경("bot" → "service")은 API 응답 직렬화에 즉시 영향을 주므로 T-6 완료 전에 수행해도 무방 (기존 bot 토큰 데이터 없음 전제).

T-3: postgres.py 구현체 정리

  • 구현 방법: ensure_schema() 내 bot 관련 DDL 블록을 순서대로 제거한다. get_token_principal()의 SELECT문을 단순화한다. bot/audit 관련 메서드를 삭제하고 get_service_token_by_name()을 추가한다.
  • 변경 대상: app/repositories/postgres.py
  • 완료 조건: ensure_schema() 재실행 후 bot_principals, auth_audit_logs 테이블 미생성. get_token_principal() 반환값에 bot_principal_id 없음. get_service_token_by_name("test") 호출 시 None 또는 AuthToken 반환.
  • 주의사항: _SELECT_AUTH_TOKEN 상수에서 bot_principal_id 컬럼 제거. TokenPrincipal 생성 시 bot_principal_id 인자 제거.

T-4: AuthContext/security.py 변경

  • 구현 방법: AuthContext 데이터클래스에서 bot_principal_id 필드를 제거하고 token_type: str | None = None을 추가한다. _resolve_auth_context()에서 token_type=principal.token_type 전달을 추가하고 bot_principal_id 할당을 제거한다. security.py에서 build_audit_event() 함수를 삭제한다.
  • 변경 대상: app/core/auth.py, app/core/security.py
  • 완료 조건: AuthContext 생성 시 token_type 파라미터 사용 가능. bot_principal_id 파라미터 없음. from app.core.security import build_audit_event ImportError 발생.

T-5: AuthService 재구현

  • 구현 방법: bot 관련 메서드와 _require_db_user_context(), _build_bot_expiration(), _record_audit_event() 헬퍼를 삭제한다. issue_client_token(), list_tokens(), revoke_token() 내 bot_principal_id 가드를 token_type == "service" 가드로 교체한다. issue_service_token(), revoke_service_token() 메서드를 추가한다. IssuedBotToken → IssuedServiceToken 교체.
  • 변경 대상: app/services/auth_service.py
  • 완료 조건: AuthService().issue_service_token(name="test", username="user") 시그니처 존재. issue_client_token()에서 service token AuthContext로 호출 시 ValueError 발생. bot 관련 메서드 미존재.
  • 주의사항: issue_service_token() 내 unique constraint 위반 시 psycopg.errors.UniqueViolation 예외를 ValueError로 래핑해야 한다.

T-6: API 경로 정리

  • 구현 방법: auth_routes.py에서 /auth/bots, /auth/me/bots 관련 라우트 핸들러와 헬퍼 함수를 삭제한다. 관련 import를 제거한다. _to_auth_token_response()에서 bot_principal_id 인자를 제거한다.
  • 변경 대상: app/api/auth_routes.py
  • 완료 조건: FastAPI 앱 기동 후 /openapi.json에 /auth/bots, /auth/me/bots 경로 미노출.

T-7: CLI 재구현

  • 구현 방법: app/cli/commands/service_tokens.py 신규 생성 (Design # 3 T-7 시그니처 기준). bot_tokens.py 파일 삭제. app/cli/main.py import 경로 변경. output.py에서 render_bot_result() → render_service_token_result() rename.
  • 변경 대상: app/cli/commands/service_tokens.py (신규), app/cli/commands/bot_tokens.py (삭제), app/cli/core/output.py, app/cli/main.py
  • 완료 조건: pfhub service-token issue --help 실행 시 NAME, --user 인수 노출. help 텍스트에 "bot" 문자열 없음.
  • 주의사항: service_tokens.py는 token_app = typer.Typer(...) 인스턴스를 export해야 한다 (app/cli/main.py에서 token_app으로 import).

T-8: 테스트 업데이트

  • 구현 방법: bot_principal 관련 픽스처와 테스트 함수를 제거하고 service token 시나리오 테스트를 추가한다. AuthContext mock에서 bot_principal_id → token_type 교체.
  • 변경 대상: 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: 기술 문서 업데이트

  • 구현 방법: 4개 문서에서 "bot_principal", "bot token" 참조를 제거하고 service token 기반 설명으로 갱신한다.
  • 변경 대상: 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 명령 예제 포함.

3. 변경 파일 목록

이번 Implementation에서 생성/수정/삭제되는 파일을 미리 나열한다.

파일 경로 변경 유형 관련 태스크 비고
migrations/versions/20260423_0013_remove_bot_principals_service_tokens.py 생성 T-1 Alembic 마이그레이션 신규
app/domain/models.py 수정 T-2 BotPrincipal 삭제, bot_principal_id 필드 삭제
app/schemas/auth.py 수정 T-2 bot 스키마 삭제, ServiceToken* 스키마 추가
app/repositories/portfolio_repository.py 수정 T-2 bot 메서드 삭제, get_service_token_by_name 추가
app/repositories/postgres.py 수정 T-3 ensure_schema DDL 제거, get_token_principal 단순화
app/core/auth.py 수정 T-4 AuthContext.token_type 추가, bot_principal_id 삭제
app/core/security.py 수정 T-4 build_audit_event 함수 삭제
app/services/auth_service.py 수정 T-5 bot 메서드 삭제, issue/revoke_service_token 추가
app/api/auth_routes.py 수정 T-6 /auth/bots, /auth/me/bots 라우트 삭제
app/cli/commands/bot_tokens.py 삭제 T-7 service_tokens.py로 대체
app/cli/commands/service_tokens.py 생성 T-7 신규 CLI 명령 모듈
app/cli/core/output.py 수정 T-7 render_bot_result → render_service_token_result
app/cli/main.py 수정 T-7 import 경로 변경
tests/test_auth.py 수정 T-8 bot 테스트 삭제, service token 테스트 추가
tests/test_cli.py 수정 T-8 service-token CLI 테스트 업데이트
tests/test_portfolio_ownership.py 수정 T-8 token_type 기반 mock으로 교체
tests/test_users.py 수정 T-8 bot_principal 참조 제거 (있는 경우)
docs/technical/auth-and-token-lifecycle.md 수정 T-9 bot → service 전반 업데이트
docs/technical/api-contracts.md 수정 T-9 /auth/bots 경로 삭제
docs/technical/management-cli-operations.md 수정 T-9 service-token 명령 예제 교체
README.md 수정 T-9 CLI 섹션 service-token 예제 추가

4. 리스크 및 제약

Implementation 단계에서 새로 식별된 리스크와 제약사항을 기록한다.

리스크

  • R-1 (High, 이월): 마이그레이션 upgrade 전 bot_principals.owner_user_id IS NULL 행 존재 시 FK 위반 — upgrade() 사전 검사로 차단.
  • R-2 (Medium, 이월): 15개 파일 rename 중 누락 시 런타임 AttributeError — T-8 완료 후 grep 잔류 검사.
  • R-3 (Medium, 이월): revoke_service_token(name) 호출 시 이미 폐기된 토큰 → "찾을 수 없음" 오류 — 설계상 의도된 동작.
  • R-4 (Low, 이월): ensure_schema() DDL 제거 후 테스트 환경 스키마 캐시로 인한 bot_principals 잔류 — 재실행 검증으로 확인.
  • R-5 (Medium): psycopg.errors.UniqueViolation 예외를 ValueError로 래핑하지 않으면 FastAPI/CLI에서 500 응답 발생.
    • 완화 방안: T-5 구현 시 try/except UniqueViolation → raise ValueError(...) 처리 포함.

가정

  • IA-1: get_user_by_username() 메서드가 app/repositories/postgres.py에 이미 구현되어 있다 (auth_service.login()에서 사용 중으로 확인됨).
  • IA-2: revoke_auth_token(user_id, token_id) 메서드가 app/repositories/postgres.py에 구현되어 있다.
  • IA-3: app/cli/main.py의 name="service-token" 등록이 완료되어 있어 T-7에서 import 경로만 변경하면 된다.
  • IA-4: 현재 테스트 환경에서 Docker Compose DB 접속이 가능하다.

오픈 질문

없음.

5. Verification으로 넘기기 전 확인 체크

  • 모든 T-N 태스크 구현 완료
    • 기준: T-1T-9 각 태스크의 완료 조건(DoD)을 충족해야 한다 — **T-1T-9 모두 완료**
  • 변경 파일 목록과 실제 변경 일치
    • 기준: 3_design.md의 변경 대상과 실제 변경된 파일이 일치해야 한다 — 일치 확인
  • 단위 테스트 또는 로컬 검증 완료
    • 기준: pytest 실행 결과 0 failures, 0 errors — 73 passed
  • 모든 오픈 질문(OQ) Open 상태 없음 확인
    • 기준: OQ 목록에 Open 상태 항목이 0개 — 확인 완료
  • Freeze 확정 담당자(CONFIRMED_BY) 지정
    • 기준: 담당자 Justin 지정 완료 — 문서 서명 대기

6. 파일 처리 결과

  • 처리 결과: 생성
  • 생성 경로: docs/sdlc/sdlc-002_20260423_service-token-user-based/4_implementation.md
  • 참조 문서:
    • docs/sdlc/sdlc-002_20260423_service-token-user-based/3_design.md
    • docs/sdlc/sdlc-002_20260423_service-token-user-based/2_planning.md

7. 사용자 결정 필요 항목 요약

DECIDE

없음. 모든 구현 결정이 Design에서 완료되었다.

CONFIRM

  • Freeze 확정 담당자(CONFIRMED_BY): Justin — confirm-implementation 단계에서 처리

DATA

  • 마이그레이션 실행 전 운영 DB bot_principals 행 수 및 owner_user_id NULL 행 수 확인 권장 (R-1 완화)

8. Implementation Freeze

섹션 참조

확정 플래그

  • Handoff Decision: 승인
  • Handoff Rationale: T-1~T-9 전체 태스크 완료, 73개 단위 테스트 통과, 변경 파일 목록 일치 확인
  • Ready for Verification: true
  • Confirmed By: Justin
  • Confirmed At (KST): 2026-04-23