Files
portfolio-hub/docs/discovery/dcy-005_20260601_rel-001-feedback-cleanup.md
T
genichin b774e40678 docs: consolidate REL-001 scope across BAT-001~016
- fold BAT-015 and BAT-016 into REL-001 release scope
- mark split REL-002 and REL-003 plans as superseded in discovery docs
- update release and batch indexes to match the unified release plan
- expand REL-001 rollout, rollback, verification, and evidence notes
2026-06-05 22:49:10 +09:00

22 KiB

0. 문서 상태

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

  • 상태: confirmed
  • 문서 ID: dcy-005_20260601_rel-001-feedback-cleanup
  • 이슈명: REL-001 Feedback 정합화 — ledger_trades.asset_id 명명 / session no_expiry enforcement / health db_enabled
  • 작성 시각(KST): 2026-06-01 17:00
  • 마지막 갱신 시각(KST): 2026-06-01 19:00
  • 대체됨: 없음
  • 후속 Discovery 참조: 없음
  • 생성된 REQ 참조: REQ-033 (req-033), REQ-034 (req-034), REQ-035 (req-035)

1. 계획 상태 요약

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

  • 연계 문서 현황:
  • 해석:
    • REL-001 배포 준비 단계에서 누적된 3건의 잔여 정합화 항목(ledger_trades.asset_id 컬럼명 혼용, session no_expiry 코드 enforcement 누락, /health 응답 모델 db_enabled 부재)을 한 묶음 Discovery로 추적해 다음 batch에서 일괄 정리한다.
    • 3건 모두 작은 범위 변경이며 backwards-compat 영향이 제한적이므로 단일 Discovery + 단일 batch(Lite 또는 표준)로 처리한다. REL-001은 현 상태로 배포 진행하고 dcy-005 3건은 별도 REL-002로 분리(OQ-3 결정, 2026-06-01).

2. 요구사항 판정 결과

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

  • 판정: 신규
  • 근거
    • 3건 모두 기존 REQ(REQ-021, REQ-029) Notes/follow-up에 등록된 잔여 항목이며 별도 REQ로 분해된 적이 없다.
    • 정합화 묶음이라는 trigger(REL-001 Feedback 일괄 정리)는 기존 Discovery에 없다.

3. 문제점의 요약

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

  • 현재 상태
    • 3건-1 ledger_trades.asset_id 컬럼명 혼용: REQ-029 구현에서 ledger 경로를 /v1/portfolios/{id}/ledger/trades로 이전했으나, ledger_trades 테이블의 asset_id 컬럼은 실제로는 portfolio_id를 저장한다(app/repositories/postgres.py:1284-1294 INSERT 컬럼 순서 + RETURNING). 컬럼명과 의미가 일치하지 않아 schema reader가 혼동.
    • 3건-2 session no_expiry 코드 enforcement 누락: REQ-021 정책 본문(docs/srs/Documentation/req-021_token-expiry-and-rotation-policy.md:15-17)은 "no_expiry는 service 한정, session/client에서 시도하면 HTTP 422 또는 ValueError로 거부"로 명시. 그러나 현재 AuthService.login(app/services/auth_service.py:47-65)과 LoginRequest 스키마(app/schemas/auth.py:13-23)에는 no_expiry 옵션 자체가 존재하지 않아 실질적으로 시도 불가하지만, 정책 명세 ↔ 코드 매핑(REQ-021 AC-5)에서 "명시적 거부 로직 미구현" follow-up이 등록됨.
    • 3건-3 /health 응답에 db_enabled 필드 미노출: HealthResponse 모델(app/api/health.py:17-21)이 status, service, version, checked_at 4필드만 노출. REL-001 verification 체크리스트는 "GET /health → HTTP 200 + db_enabled: true"를 요구하지만 응답 모델에 해당 필드 없음 — 외부 health probe가 DB 연결 상태를 직접 관찰할 수 없는 gap.
  • 기대 상태
    • 3건-1: 컬럼명을 의미와 일치시키거나(portfolio_id로 rename), 또는 ledger_trades가 정말 asset 단위로 의미가 있는지 재정의. 마이그레이션 동반.
    • 3건-2: session 발급 경로에서 no_expiry 시도 자체를 거부하는 명시적 enforcement 코드 추가(현재는 옵션 부재로 시도 불가지만 정책 명문화 완결성). 또는 정책 본문에 "현재 코드 구조상 시도 불가, 명시적 거부 로직 불요"로 갱신.
    • 3건-3: HealthResponse에 db_enabled: bool 필드 추가 또는 별도 /readiness endpoint 분리. 어느 경로로 갈지 결정.
  • 이해관계자
    • Backend 팀 — 코드 변경 및 마이그레이션 책임
    • Operations — /health 응답 변경 시 health probe 동작 검증
    • REL-001 배포 이후 trex-basic 등 ledger 호출 클라이언트 — ledger_trades.asset_id 컬럼명 변경 시 외부 query 의존성 확인 필요(있는 경우)
  • 제약사항
    • REL-001은 현 상태로 배포 진행, dcy-005 결과물은 별도 REL-002로 분리(OQ-3 결정, 2026-06-01). REL-001 verification 재실행 불요.
    • 마이그레이션을 동반하면 dcy-004 / REQ-017 패턴(CREATE INDEX CONCURRENTLY 등)과 정합 유지. REL-001 마이그레이션 chain(20260521_0023) 다음 revision으로 자연스럽게 추가.
    • /health 응답 schema 변경은 backwards-compat 영향 — 기존 client(probe, monitoring)가 추가 필드를 허용하는지 확인.

주요 사용자 시나리오

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

  • DB schema reader가 ledger_trades.asset_id를 보고 assets 테이블 참조라고 오해해 잘못된 join query 작성.
  • 정책 감사자가 REQ-021 vs 실제 코드를 매핑할 때 session에서 no_expiry 거부가 코드에 명시적으로 없어 명확성 부족으로 audit finding 작성.
  • 운영자가 prod에서 health probe 응답을 monitoring 도구로 받을 때 DB 연결 끊김을 빠르게 감지하지 못함(현재는 status: ok만 노출).

4. 이번에 정의할 변경

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

  • ledger_trades.asset_id 컬럼명을 의미에 맞게 portfolio_id로 변경하는 마이그레이션 + 코드 경로 정합화.
  • LoginRequest에 model_config = ConfigDict(extra="forbid") 적용으로 session no_expiry 시도 자체를 HTTP 422로 거부 (OQ-1 결정, 2026-06-01).
  • HealthResponse 모델에 db_enabled: bool 필드 추가 + healthcheck() 핸들러가 settings.db_enabled 노출 (OQ-2 결정, 2026-06-01).

영향 범위

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

  • 코드 영향:
    • 3건-1: app/repositories/postgres.py ledger_trades INSERT/SELECT 컬럼 변경, app/domain/models.py LedgerTrade dataclass 필드명 (있다면), app/api/v1_portfolios.py ledger route handler, migrations/versions/ 신규 revision (rename column with backfill).
    • 3건-2: app/schemas/auth.py:LoginRequest — model_config = ConfigDict(extra="forbid") 추가 (OQ-1 결정 (a) 경로). app/services/auth_service.py:login은 변경 없음.
    • 3건-3: app/api/health.py HealthResponse에 db_enabled: bool 필드 추가 + healthcheck() 핸들러가 settings.db_enabled 노출 (OQ-2 결정 (a) 경로). 별도 /readiness router 신설은 본 Discovery 범위 밖.
  • 문서 영향:
    • 3건-1: docs/data-model.md ledger_trades 컬럼 명세, docs/interface-contract.md ledger 응답 schema(asset_id가 응답에 노출되는 경우)
    • 3건-2: docs/srs/Documentation/req-021_token-expiry-and-rotation-policy.md (선택 시 본문 갱신), docs/runtime-flows.md Token Expiry 섹션
    • 3건-3: docs/interface-contract.md /health 응답 schema(db_enabled 추가), docs/runtime-flows.md health probe flow에 "db_enabled는 설정값 노출, actual ping 아님" 명문화 (OQ-2 결정 (a) 경로)
    • 3건 공통: 본 Discovery + 후속 REQ + (선택) baseline 문서 정합
  • 운영 영향:
    • 3건-1: 마이그레이션 실행 시 ledger_trades 테이블 잠금 영향. CREATE INDEX CONCURRENTLY 패턴 적용 또는 짧은 ALTER COLUMN RENAME 트랜잭션.
    • 3건-2: extra="forbid" 적용으로 LoginRequest 전체에 추가 필드 거부 효과. 기존 client(현재는 username/password만 전송)는 영향 없음. 향후 외부 client가 ignored field를 보내고 있었다면 422로 깨질 수 있으나, 본 워크스페이스는 개발용이라 영향 무.
    • 3건-3: health probe 응답 필드 확장 — 본 워크스페이스는 개발용이라 외부 monitoring 영향 없음. 미래 prod 도입 시 도구가 추가 필드를 허용하는지 사전 확인.

5. 요구사항 목록

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

기능 요구사항

  • FR-1: ledger_trades.asset_id 컬럼을 portfolio_id로 rename하는 Alembic 마이그레이션을 작성한다. 기존 데이터는 동일 의미(portfolio_id 저장)이므로 backfill 불요. INSERT/SELECT/RETURNING SQL 및 LedgerTrade dataclass 필드명도 함께 정합화한다.
  • FR-2: REQ-021의 "session에서 no_expiry 거부" 정책을 LoginRequest 스키마 단계에서 코드 enforcement로 명시적 거부한다(OQ-1 결정, 2026-06-01 — (a) 경로). 구현 방법: LoginRequest에 model_config = ConfigDict(extra="forbid") 적용 또는 별도 validator로 no_expiry 패턴 거부. 외부 client가 LoginRequest에 정의되지 않은 필드(no_expiry 포함)를 전송하면 HTTP 422로 거부된다. pytest 회귀 케이스 1건(TestClient /auth/login body에 no_expiry: true 포함 → 422 확인) 신설.
  • FR-3: /health 응답 본문에 db_enabled: bool 필드를 노출한다(OQ-2 결정 (a) 경로, 2026-06-01). HealthResponse 모델에 db_enabled: bool 필드 추가 + healthcheck() 핸들러가 settings.db_enabled 값을 그대로 채운다. 본 값은 환경변수 PORTFOLIOHUB_DB_ENABLED 설정값을 노출하는 것이며 실제 DB connectivity ping이 아님을 docs/runtime-flows.md(또는 응답 모델 docstring)에 명문화한다. 미래 production monitoring 정착 시 /readiness endpoint 분리는 별도 Discovery로 분리 가능.

비기능 요구사항

성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.

  • NFR-1: 3건 모두 backwards-compat 영향 최소화. 마이그레이션(FR-1)은 짧은 ALTER COLUMN RENAME으로 처리하고 운영 lock 시간을 < 1초 권장. /health 응답 확장(FR-3)은 필드 추가형으로 기존 client 영향 없음.
  • NFR-2: 본 Discovery 결과물(후속 batch verification)은 자동 테스트로 검증 가능해야 한다. ledger_trades column rename은 마이그레이션 적용 후 INSERT/SELECT 회귀, /health 확장은 응답 본문 schema 검증, no_expiry enforcement는 /auth/login body에 추가 필드 포함 시 HTTP 422 회귀.

범위 경계

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

  • In Scope
    • ledger_trades.asset_id 컬럼명 정합화 (rename + 코드/스키마 갱신)
    • session no_expiry 코드 enforcement (LoginRequest extra="forbid") — OQ-1 결정 (a)
    • HealthResponse에 db_enabled: bool 필드 추가 (settings 값 노출, OQ-2 결정 (a))
    • 위 3건의 마이그레이션 + 회귀 테스트
  • Out of Scope
    • ledger_trades 의미 재정의(asset 단위 vs portfolio 단위 — 본 REQ는 portfolio_id 의미 확정 가정)
    • session token TTL 정책 변경 (REQ-021 본문 자체 정책 갱신은 본 Discovery 범위 밖)
    • prod 운영 health probe 도구 변경 (운영 영역)
    • prod admin token rotation 등 보안 hotfix (본 워크스페이스는 개발용 — 참조 메모리)

6. 리스크/가정 목록

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

리스크

  • R-1 (Low): ledger_trades.asset_id 컬럼명 변경 시 외부 client(trex-basic 등)가 컬럼명을 직접 query하면 깨질 수 있음.
    • 완화 방안: trex-basic은 HTTP API(POST /v1/portfolios/{id}/ledger/trades)만 호출하므로 DB 컬럼명 의존 없음 추정. 마이그레이션 전 외부 query 의존성 확인.
  • R-2 (Low): /health 응답 본문 확장 시 monitoring 도구가 strict JSON schema validation을 사용하면 깨질 수 있음.
    • 완화 방안: 본 워크스페이스는 개발용이라 외부 monitoring 영향 없음. prod 배포 시 사전 점검.
  • R-3 (Low): 마이그레이션 적용 순서가 잘못되어 ledger_trades INSERT가 일시 실패할 수 있음.
    • 완화 방안: 배포 전 staging에서 dry-run, 또는 ALTER COLUMN RENAME과 코드 배포를 같은 release window에 묶음.

가정

아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.

  • A-1: ledger_trades.asset_id가 저장하는 값은 portfolio_id로 일관됨(과거 다른 의미가 섞이지 않음). REQ-029 구현 시점부터 portfolio_id를 저장한 것으로 확인. 사용자 confirm-discovery(2026-06-01)로 동의 확정.
  • A-2: 본 워크스페이스는 개발용이며 prod 배포 영향 검증은 release 단계에서 별도 수행. 본 Discovery 범위는 코드/문서/마이그레이션 작성에 한정.
  • A-3: 3건은 같은 batch로 묶어 처리 가능하다(서로 독립적 변경이라 상호 충돌 없음). 사용자 confirm-discovery(2026-06-01)로 동의 확정.

오픈 질문

답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.

  • OQ-1: session no_expiry 처리를 (a) 코드 enforcement 추가, (b) REQ-021 본문 갱신("코드 구조상 시도 불가") 중 어느 경로로 갈지.
    • 상태: Answered (2026-06-01)
    • 결정: (a) 코드 enforcement. LoginRequest에 model_config = ConfigDict(extra="forbid") 적용으로 정의되지 않은 필드(no_expiry 포함)를 HTTP 422로 거부. 정책 명문(REQ-021) 그대로 유지, 미래 안전망 효과 확보.
    • 본문 반영: FR-2, # 4 코드/운영 영향, # 7 S-2 측정 방식, # 10 DECIDE 섹션에서 항목 제거
  • OQ-2: /health 응답에 db_enabled 추가 방식을 (a) HealthResponse 필드 추가, (b) 별도 /readiness endpoint 분리 중 어느 경로로 갈지.
    • 상태: Answered (2026-06-01)
    • 결정: (a) HealthResponse 필드 추가. settings.db_enabled(환경변수 PORTFOLIOHUB_DB_ENABLED 설정값)를 응답에 노출. 본 값은 설정값이며 actual DB connectivity ping이 아님을 docs/runtime-flows.md 및 응답 모델 docstring에 명문화한다. 본 워크스페이스가 개발용이므로 Kubernetes-grade readiness 분리는 over-spec. 미래 production monitoring 정착 시 /readiness 분리는 별도 Discovery로 분리 가능.
    • 본문 반영: FR-3, # 4 코드/문서/운영 영향, # 7 S-3 측정 방식, # 10 DECIDE 섹션에서 항목 제거
  • OQ-3: ledger_trades.asset_id rename 마이그레이션을 REL-001 배포 전/후 어디에 넣을지(별도 release 또는 REL-001 후속).
    • 상태: Answered (2026-06-01)
    • 결정: 별도 REL-002로 분리. REL-001은 현 상태(BAT-001~014 + REQ-028 augmentation, verification 완료, smoke 통과)로 배포 진행. dcy-005 3건은 신규 batch(BAT-015 가정) + REL-002로 분리해 sequence를 명확히 한다.
    • 사유: REL-001 verification 안정성 최우선, 마이그레이션 sequence 분리, dcy-005가 "REL-001 잔여 정리" 묶음 trigger로 독립 추적 가치. REL-001 verification gap(/health db_enabled 미노출 [~] 부분 통과)은 우회 검증으로 blocker 아님.
    • 본문 반영: # 1 해석, # 3 제약사항, # 10 DECIDE 섹션에서 항목 제거
    • Superseded (2026-06-05): 사용자 결정으로 미배포 batch 전체(BAT-001~016)를 단일 REL-001로 통합 → 별도 REL-002는 생성하지 않음. BAT-015는 REL-001에 흡수. (rel-001 Scope 통합 노트)

오픈 질문 처리 기준

  • Open: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
  • Answered: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
  • Deferred: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
  • Dropped: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.

7. 초기 성공 기준

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

  • S-1: ledger_trades 테이블에 portfolio_id 컬럼이 존재하고 asset_id 컬럼이 더 이상 사용되지 않는다(코드 grep + DB \d 검증). 기존 INSERT/SELECT 회귀 테스트 모두 통과.
  • S-2: REQ-021 정책과 실제 코드가 일치한다 — /auth/login body에 no_expiry: true(또는 LoginRequest에 정의되지 않은 추가 필드)를 포함해 시도 시 HTTP 422 응답 (OQ-1 결정 (a) 경로, 2026-06-01).
  • S-3: GET /health 응답이 db_enabled 필드를 직접 노출하고 그 값이 settings.db_enabled와 일치한다 (OQ-2 결정 (a) 경로, 2026-06-01). 응답 본문 schema 검증 + grep.

측정 방식 및 데이터 출처

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

  • S-1 측정 방식: grep -rn "ledger_trades\..*asset_id\|.asset_id\b" app/repositories/postgres.py app/domain/models.py app/api/v1_portfolios.py 결과 0건 + DB \d ledger_trades에서 portfolio_id 컬럼 확인 / 데이터 출처: 코드 grep + alembic upgrade 후 DB schema
  • S-2 측정 방식: pytest TestClient /auth/login body에 no_expiry: true 포함 → HTTP 422 응답 회귀 / 데이터 출처: 신규 pytest 케이스 (예: tests/test_auth.py::test_login_rejects_no_expiry_extra_field)
  • S-3 측정 방식: pytest TestClient GET /health 응답 JSON에 "db_enabled": true 매치 (PORTFOLIOHUB_DB_ENABLED=true 환경) + 로컬 smoke curl http://127.0.0.1:18000/health / 데이터 출처: 신규 pytest 케이스 (예: tests/test_app.py::test_health_exposes_db_enabled) + 로컬 smoke

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

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

  • 범위(In Scope/Out of Scope) 확정
    • 기준: OQ-1/OQ-2/OQ-3 모두 2026-06-01 Answered. 범위 + 배포 시점(REL-002 분리) 확정.
  • 성공 지표의 측정 방식/데이터 출처 확정
    • 기준: S-1(코드 grep + DB \d), S-2(pytest 422 회귀), S-3(pytest TestClient + 로컬 smoke) 모두 확정.
  • High 영향 리스크 우선순위 및 해소 책임자 지정
    • 기준: 본 Discovery에는 High 리스크 없음 (R-1/R-2/R-3 모두 Low) — 해소 책임자 지정 불요.
  • 모든 오픈 질문(OQ) Open 상태 없음 확인
    • 기준: OQ-1/OQ-2/OQ-3 모두 Answered (2026-06-01). Open 항목 0개.
  • Freeze 확정 담당자(CONFIRMED_BY) 지정
    • 기준: CONFIRMED_BY = "Justin Ko" (confirm-discovery 2026-06-01).

9. 파일 처리 결과

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

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

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

DECIDE

  • (none — OQ-3 결정 완료, DECIDE 섹션 비움)

CONFIRM

  • (none — 두 항목 모두 사용자 confirm-discovery(2026-06-01)로 가정 A-1/A-3에 대한 동의 확정. 본문 가정 섹션에 반영됨.)

DATA

  • (none — 본 Discovery는 본 repo 내부 정보만으로 자립)

11. Discovery Freeze

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

섹션 참조

Freeze 플래그

  • Handoff Decision: 승인
  • Handoff Rationale: OQ-1/OQ-2/OQ-3 모두 Answered, CONFIRM 2건은 사용자 confirm-discovery(2026-06-01)로 가정 A-1/A-3 동의 확정. # 8 체크리스트 5건 모두 [x]. REQ Drafting 진입 준비 완료.
  • Ready for REQ Drafting: true
  • Confirmed By: Justin
  • Confirmed At (KST): 2026-06-01 18:30

12. 작성 가이드

  • 이 파일은 dcy-005 작성 완료본이다. confirm-discovery 진입 전까지 OQ 결정 + 본문 반영 필요.
  • 3건이 서로 독립이므로 OQ 결정도 항목별로 진행 가능. 결정이 끝나는 항목부터 본문(FR-2/FR-3)을 (a)/(b) 선택안으로 압축한다.
  • 본 Discovery는 코드/문서/마이그레이션 모두 영향이 있는 표준 SDLC 트랙. minor-change fast path는 3건 묶음 특성상 부적합 — 표준 batch로 진행 권장.