- 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
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가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
- 연계 문서 현황:
- 관련 점수 파일: 미생성
- 관련 TODO/메모: rel-001 Feedback Handoff에 등록된 3건의 follow-up 후보
- 해석:
- 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).
- REL-001 배포 준비 단계에서 누적된 3건의 잔여 정합화 항목(
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_at4필드만 노출. REL-001 verification 체크리스트는 "GET /health → HTTP 200 +db_enabled: true"를 요구하지만 응답 모델에 해당 필드 없음 — 외부 health probe가 DB 연결 상태를 직접 관찰할 수 없는 gap.
- 3건-1
- 기대 상태
- 3건-1: 컬럼명을 의미와 일치시키거나(
portfolio_id로 rename), 또는 ledger_trades가 정말 asset 단위로 의미가 있는지 재정의. 마이그레이션 동반. - 3건-2: session 발급 경로에서
no_expiry시도 자체를 거부하는 명시적 enforcement 코드 추가(현재는 옵션 부재로 시도 불가지만 정책 명문화 완결성). 또는 정책 본문에 "현재 코드 구조상 시도 불가, 명시적 거부 로직 불요"로 갱신. - 3건-3:
HealthResponse에db_enabled: bool필드 추가 또는 별도/readinessendpoint 분리. 어느 경로로 갈지 결정.
- 3건-1: 컬럼명을 의미와 일치시키거나(
- 이해관계자
- 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")적용으로 sessionno_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.pyledger_trades INSERT/SELECT 컬럼 변경,app/domain/models.pyLedgerTrade dataclass 필드명 (있다면),app/api/v1_portfolios.pyledger 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.pyHealthResponse에db_enabled: bool필드 추가 +healthcheck()핸들러가settings.db_enabled노출 (OQ-2 결정 (a) 경로). 별도/readinessrouter 신설은 본 Discovery 범위 밖.
- 3건-1:
- 문서 영향:
- 3건-1:
docs/data-model.mdledger_trades 컬럼 명세,docs/interface-contract.mdledger 응답 schema(asset_id가 응답에 노출되는 경우) - 3건-2:
docs/srs/Documentation/req-021_token-expiry-and-rotation-policy.md(선택 시 본문 갱신),docs/runtime-flows.mdToken Expiry 섹션 - 3건-3:
docs/interface-contract.md/health 응답 schema(db_enabled추가),docs/runtime-flows.mdhealth probe flow에 "db_enabled는 설정값 노출, actual ping 아님" 명문화 (OQ-2 결정 (a) 경로) - 3건 공통: 본 Discovery + 후속 REQ + (선택) baseline 문서 정합
- 3건-1:
- 운영 영향:
- 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/loginbody에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 정착 시/readinessendpoint 분리는 별도 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/loginbody에 추가 필드 포함 시 HTTP 422 회귀.
범위 경계
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
- In Scope
ledger_trades.asset_id컬럼명 정합화 (rename + 코드/스키마 갱신)- session
no_expiry코드 enforcement (LoginRequestextra="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) 별도/readinessendpoint 분리 중 어느 경로로 갈지.- 상태: 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_idrename 마이그레이션을 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/loginbody에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/loginbody에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 환경) + 로컬 smokecurl 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 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
- 처리 결과: 생성
- 생성 경로: docs/discovery/dcy-005_20260601_rel-001-feedback-cleanup.md
- 참조 문서:
- docs/releases/rel-001_20260521_v1-api-full-surface.md Feedback Handoff Discovery Input 항목
- docs/srs/Documentation/req-021_token-expiry-and-rotation-policy.md (REQ-021 정책 본문)
- docs/srs/Interface/req-029_v1-portfolios-ledger-trades-migration.md Notes (ledger_trades 컬럼명 follow-up 출처)
- app/api/health.py:17-32 HealthResponse 모델/handler
- app/repositories/postgres.py:1284-1294 ledger_trades INSERT
- app/services/auth_service.py:47-65 AuthService.login
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 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
섹션 참조
- 범위: # 5. 요구사항 목록 > 범위 경계
- 리스크/오픈 질문: # 6. 리스크/가정 목록
- 성공 기준: # 7. 초기 성공 기준
- 인계 전 확인: # 8. REQ로 넘기기 전 확인 체크
- 사용자 결정 필요: # 10. 사용자 결정 필요 항목 요약
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로 진행 권장.