Files
portfolio-hub/docs/discovery/dcy-003_20260502_portfolio-list-user-required.md
T
genichin 2f686f79ce feat(req-013): add portfolio-list-user-required requirement and batch
- Add REQ-013: Portfolio list filtered by user requirement with acceptance criteria
- Add DCY-003 discovery document for portfolio list user filtering
- Create BAT-003 batch with planning, design, implementation, and verification
- Enhance app/cli/commands/portfolio.py with user-based list filtering
- Add comprehensive tests for portfolio list CLI command with user filtering
- Update docs indices (discovery, srs, batches)
- Improve README with latest capability status
2026-05-02 13:22:41 +00:00

14 KiB

0. 문서 상태

이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.

  • 상태: confirmed
  • 문서 ID: dcy-003_20260502_portfolio-list-user-required
  • 이슈명: CLI pfhub portfolio list — --user 옵션 필수화 및 소유자 기반 조회 강제
  • 작성 시각(KST): 2026-05-02T00:00:00+09:00
  • 마지막 갱신 시각(KST): 2026-05-02T09:10:00+09:00
  • 대체됨: 없음
  • 후속 Discovery 참조: 없음
  • 생성된 REQ 참조:
    • REQ-013 — CLI portfolio list --user 필수화 및 소유자 기반 조회

1. 계획 상태 요약

현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.

  • 연계 문서 현황:
    • 관련 점수 파일: 미생성
    • 관련 TODO/메모: 없음
  • 해석:
    • CLI pfhub portfolio list는 현재 --user 옵션이 없어 owner_user_id=None으로 서비스를 호출하며, DB에 저장된 전체 포트폴리오를 반환한다. HTTP API는 _ownership_required 설정 및 ADMIN 여부에 따라 소유자 필터를 적용하지만 CLI에는 동일한 접근 통제가 존재하지 않는다.
    • 이번 Discovery에서는 CLI portfolio list 명령에 --user 옵션을 필수로 지정하고, 해당 username으로 owner_user_id를 조회하여 소유자 기반 필터 조회를 강제하는 방향을 정의한다.

2. 요구사항 판정 결과

이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.

  • 판정: 신규
  • 근거
    • dcy-001(실DB 마이그레이션 검증)과 dcy-002(trex-basic API 계약)는 모두 CLI portfolio list 인터페이스 및 소유자 필터 정책을 다루지 않는다.
    • 기존 REQ 목록(docs/srs/index.md, REQ-001~REQ-012)에 CLI portfolio list 접근 통제 관련 요구사항이 존재하지 않는다.

3. 문제점의 요약

문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.

  • 현재 상태
    • app/cli/commands/portfolio.py의 list_portfolios 명령은 --user 옵션이 없으며, owner_user_id=None으로 PortfolioService.list_portfolios를 호출한다.
    • owner_user_id=None이면 app/repositories/postgres.py의 list_portfolios가 소유자 WHERE 절을 추가하지 않아 전체 포트폴리오를 반환한다.
    • HTTP API(app/api/portfolios.py)는 _ownership_required 설정 플래그와 ADMIN 권한 여부에 따라 소유자 필터를 조건부 적용하지만, CLI에는 동일한 로직이 없다.
  • 기대 상태
    • pfhub portfolio list --user <username> 형태로 --user를 필수 옵션으로 지정한다.
    • --user 없이 실행하면 명확한 오류 메시지와 함께 종료된다.
    • --user로 전달된 username을 users 테이블에서 조회하여 owner_user_id(UUID)로 변환한 뒤 조회한다.
  • 이해관계자
    • CLI 운영자(관리자 계정으로 pfhub CLI를 사용하는 사람)
    • 백엔드 개발자(CLI 인터페이스 및 서비스 레이어 관리자)
  • 제약사항
    • CLI는 기존 PortfolioService.list_portfolios와 UserService (또는 user repository)를 재사용해야 한다. 별도 API 호출 없이 직접 DB를 통해 username → user_id 변환이 이루어진다.
    • pfhub_DB_ENABLED=false(in-memory) 모드에서도 동일한 인터페이스 검증이 적용되어야 한다.

주요 사용자 시나리오

사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.

  • 관리자가 특정 사용자의 포트폴리오만 확인하기 위해 pfhub portfolio list --user alice를 실행한다.
  • 관리자가 --user 없이 pfhub portfolio list를 실행했을 때 "Error: --user 옵션이 필요합니다" 형태의 메시지를 받고 올바른 사용법을 인지한다.
  • 존재하지 않는 username으로 pfhub portfolio list --user unknown을 실행하면 사용자를 찾을 수 없다는 오류 메시지와 함께 종료된다.

4. 이번에 정의할 변경

이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.

  • CLI portfolio list 명령에 --user <username> 필수 옵션을 추가하고, 미입력 시 에러로 종료한다.
  • CLI 내부에서 username → owner_user_id(UUID) 변환 로직을 추가한다(user repository 또는 user service 직접 조회).
  • PortfolioService.list_portfolios 호출 시 변환된 owner_user_id를 항상 전달하도록 CLI 호출부를 수정한다.

영향 범위

변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.

  • 코드 영향:
    • app/cli/commands/portfolio.py — list_portfolios 명령 시그니처 및 서비스 호출부 수정
    • app/repositories/postgres.py — get_user_by_username(line 419) 직접 호출. UserService에는 해당 메서드 없음 — repository를 통해 직접 조회
  • 문서 영향:
    • README.md — pfhub portfolio list 예제 라인에 --user 옵션 추가
    • docs/runtime-flows.md — CLI portfolio list 흐름 갱신(있을 경우)
  • 운영 영향:
    • 기존 스크립트에서 pfhub portfolio list를 --user 없이 호출하면 즉시 에러로 변경됨 → 스크립트 마이그레이션 필요

5. 요구사항 목록

실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.

기능 요구사항

  • FR-1: CLI pfhub portfolio list 명령에 --user <username> 옵션을 필수로 추가한다. --user 없이 실행하면 오류 메시지를 출력하고 비정상 종료(exit code 1)한다.
  • FR-2: CLI 내부에서 --user로 전달받은 username을 user 저장소에서 조회하여 owner_user_id(UUID)를 얻고, 이를 PortfolioService.list_portfolios의 owner_user_id 인자로 전달한다.
  • FR-3: --user로 전달된 username이 존재하지 않으면 "사용자를 찾을 수 없습니다" 형태의 오류 메시지를 출력하고 비정상 종료한다.

비기능 요구사항

  • NFR-1: username → user_id 변환 실패 메시지는 기존 CLI 오류 출력 형식(Rich Console 또는 typer.echo)과 일관성을 유지한다.
  • NFR-2: --user 필수화는 in-memory 모드(pfhub_DB_ENABLED=false)에서도 동일하게 적용되어야 하며, 테스트에서 검증 가능해야 한다.

범위 경계

이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.

  • In Scope
    • CLI portfolio list 명령의 --user 필수 옵션화
    • username → owner_user_id 변환 로직 (user repo/service 직접 조회)
    • README 예제 수정
  • Out of Scope
    • HTTP API GET /portfolios의 접근 통제 변경 (현행 유지)
    • portfolio list의 --all 관리자 전체 조회 옵션 추가 (후속 Discovery로 분리)
    • CLI에 HTTP API 방식의 _ownership_required 설정 플래그 도입 (이번 반복 외)

6. 리스크/가정 목록

불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.

리스크

  • R-1 (Medium): 기존 스크립트 또는 자동화에서 pfhub portfolio list를 --user 없이 호출하는 경우, 변경 후 즉시 에러로 중단된다.
    • 완화 방안: README에 변경 사항을 명확히 기재하고, 커밋 메시지에 breaking change 표기.
  • R-2 (Low): in-memory 모드에서 username 조회를 위한 user repo 인터페이스가 충분히 구현되어 있지 않을 수 있다. → 해소: FakeRepo.get_user_by_username(tests/test_cli.py:109)과 PortfolioRepository Protocol stub(portfolio_repository.py:16) 모두 존재 확인.
    • 완화 방안: 해소 완료 — 추가 구현 불필요.

가정

  • A-1: CLI는 관리자 권한으로만 실행된다는 기존 설계를 유지한다. 따라서 --user로 타인의 포트폴리오도 조회 가능하며 CLI 레벨의 추가 권한 검사는 이번 범위에서 제외한다.
  • A-2: get_user_by_username이 이미 PortfolioRepository Protocol(portfolio_repository.py:16), PostgresRepo(postgres.py:419), FakeRepo(tests/test_cli.py:109) 모두에 구현되어 있음이 확인되었다.
  • A-3: PortfolioService.list_portfolios의 시그니처는 변경하지 않는다. CLI 호출부에서만 owner_user_id를 전달한다.

오픈 질문

  • OQ-1: in-memory FakeRepo에서 username → user_id 조회 메서드가 없는 경우 테스트를 어떻게 구성할지.
    • 상태: Answered
    • 처리 방안: tests/test_cli.py의 FakeRepo에 get_user_by_username(username) -> User | None 메서드가 이미 구현되어 있음(line 109). _users dict에서 username 일치 항목을 선형 탐색하여 반환한다. 추가 stub 없이 기존 메서드를 그대로 사용하면 된다. app/repositories/postgres.py에도 동일 시그니처(line 419)가 구현되어 있어 in-memory/DB 모드 모두 지원 가능하다.
    • 종료 조건: 저장소 현황 검토 완료 — FakeRepo 및 PostgresRepo 양쪽 모두 get_user_by_username 존재 확인됨.

오픈 질문 처리 기준

  • Open: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
  • Answered: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
  • Deferred: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
  • Dropped: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
  • 처리 원칙 1: 질문만 남기지 말고 반드시 상태, 처리 방안, 종료 조건을 함께 적는다.
  • 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
  • 처리 원칙 3: High 영향 질문은 Deferred 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.

7. 초기 성공 기준

이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.

  • S-1: pfhub portfolio list (without --user) 실행 시 exit code 1과 함께 오류 메시지가 출력된다.
  • S-2: pfhub portfolio list --user alice 실행 시 alice의 owner_user_id로 필터된 포트폴리오 목록만 반환된다.
  • S-3: 존재하지 않는 username으로 실행 시 "사용자를 찾을 수 없습니다" 형태의 오류 메시지와 함께 비정상 종료된다.

측정 방식 및 데이터 출처

각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.

  • S-1 측정 방식: pytest에서 CliRunner.invoke로 --user 없는 호출의 exit_code == 1 확인 / 데이터 출처: tests/test_cli.py
  • S-2 측정 방식: FakeRepo/DB에 복수 사용자 포트폴리오 세팅 후 CLI 결과에 alice 소유 항목만 존재하는지 검증 / 데이터 출처: tests/test_cli.py
  • S-3 측정 방식: 존재하지 않는 username 입력 시 exit_code != 0 및 오류 문자열 포함 여부 검증 / 데이터 출처: tests/test_cli.py

8. REQ로 넘기기 전 확인 체크

REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.

  • 범위(In Scope/Out of Scope) 확정
    • 기준: --user 필수화 In Scope, --all 옵션/HTTP API 변경 Out of Scope — #5에 명시. confirm-discovery 실행으로 확정.
  • 성공 지표의 측정 방식/데이터 출처 확정
    • 기준: S-1/S-2/S-3 각각 pytest CliRunner.invoke 기반 검증 케이스로 기술됨. #7 참조.
  • High 영향 리스크 우선순위 및 해소 책임자 지정
    • 기준: 리스크 중 High 등급 없음(R-1: Medium, R-2: Low → 해소). R-1 완화 방안(README 명기, breaking change 표기)을 문서에 명시.
  • 모든 오픈 질문(OQ) Open 상태 없음 확인
    • 기준: OQ-1 Answered 처리 완료.
  • Freeze 확정 담당자(CONFIRMED_BY) 지정
    • 기준: 프로젝트 오너가 confirm-discovery 실행으로 확정. 2026-05-02.

9. 파일 처리 결과

이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.

  • 처리 결과: 생성
  • 생성 경로: docs/discovery/dcy-003_20260502_portfolio-list-user-required.md
  • 참조 문서:
    • app/cli/commands/portfolio.py
    • app/services/portfolio_service.py
    • app/repositories/postgres.py
    • app/api/portfolios.py

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

사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.

DECIDE

  • {{DECIDE_ALL_OPTION}} → Resolved: --all 옵션은 #5 Out of Scope로 결정됨. 후속 Discovery로 분리.

CONFIRM

  • {{CONFIRM_BREAKING_CHANGE}} → Resolved: R-1(Medium)에 문서화됨. 완화 방안(README 명기, breaking change 표기) 채택. confirm-discovery 실행으로 인지 확인.
  • {{CONFIRM_SCOPE}} → Resolved: In Scope / Out of Scope가 #5에 명시됨. confirm-discovery 실행으로 확정.

DATA

  • {{DATA_FAKEREPO_USERNAME_LOOKUP}} → Answered: FakeRepo.get_user_by_username(tests/test_cli.py:109) 및 PostgresRepo.get_user_by_username(app/repositories/postgres.py:419) 모두 존재 확인 완료.

11. Discovery Freeze

REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.

  • Handoff Decision: 승인
  • Handoff Rationale: 저장소 현황 재확인 완료. 모든 C 유형 플레이스홀더 해소, OQ Open 없음, #8 체크리스트 전항 완료. REQ 초안 작성 준비 완료.
  • Ready for REQ Drafting: true
  • Confirmed By: Justin
  • Confirmed At (KST): 2026-05-02T09:00:00+09:00
  • 범위 참조: # 5. 요구사항 목록
  • 성공 기준 참조: # 7. 초기 성공 기준
  • 리스크 참조: # 6. 리스크/가정 목록