feat(V1-API): implement TREX-Basic consumer API contract with encryption and persistence

- Add V1 API endpoints: assets, ledger, user-info
- Implement Fernet-based credential payload encryption (req-010)
- Extend health response schema with runtime info (req-004)
- Add service token runtime ID issuance (req-005)
- Implement database schema migration for V1 API (req-009)
- Add comprehensive test coverage for V1 endpoints
- Update SRS with 10 new requirements (REQ-004 through REQ-011)
- Create batch planning, design, and verification for BAT-001
- Integrate review-discovery StagePilot skill
- Update documentation indices and baseline specs
This commit is contained in:
2026-05-02 09:06:15 +00:00
parent 24096a1e58
commit ce63c75c9c
37 changed files with 2851 additions and 21 deletions
@@ -0,0 +1,97 @@
# req-011: trex-basic 계약 반영을 위한 baseline 문서 갱신
- Status: Approved
- Type: Documentation
- Priority: Medium
- Owner: repository-maintainers
## Intent
- dcy-002 구현 완료 후 interface-contract.md, data-model.md, runtime-flows.md를 실제 구현 결과와 동기화한다.
- 이 REQ는 구현이 완료된 시점에 수행하는 문서 갱신이며, 구현 전 선행 작업이 아니다.
## Requirement
- REQ-004~010이 구현 완료(Implemented)된 시점에 다음 3개 baseline 문서를 갱신해야 한다.
1. docs/interface-contract.md: GET /health 응답 스키마, GET /v1/assets, GET /v1/user_info, POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 명세 및 service-token bearer 인증 방식 추가
2. docs/data-model.md: broker_credentials 테이블, ledger_trades 테이블, portfolios.market 컬럼, auth_tokens.runtime_id 컬럼 추가
3. docs/runtime-flows.md: /v1/* API 요청 처리 흐름(인증 → 서비스 → DB), service-token 발급 시 runtime_id 생성 흐름 추가
## Acceptance Criteria
1. Given docs/interface-contract.md를 검토하면, Then GET /health, GET /v1/assets, GET /v1/user_info, POST /v1/assets/{asset_id}/ledger/trades의 요청/응답 명세가 포함되어야 한다.
2. Given docs/data-model.md를 검토하면, Then broker_credentials, ledger_trades 테이블과 portfolios.market, auth_tokens.runtime_id 컬럼이 문서화되어야 한다.
3. Given docs/runtime-flows.md를 검토하면, Then v1 API 처리 흐름과 runtime_id 발급 흐름이 포함되어야 한다.
## Impacted Area
- Docs: docs/interface-contract.md, docs/data-model.md, docs/runtime-flows.md
- Modules: 없음
- Tests: 없음
## Notes
- 이 REQ는 구현 완료 후 수행하므로 batch 설계 단계에서 별도 implementation task로 명시한다.
- 문서 갱신 내용은 draft-batch-verification 단계에서 실제 구현과 비교해 검증한다.
- Source Discovery: dcy-002 (전체 FR/NFR 구현 완료 후 documentation closure)
## Change Log
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-011 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 3개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch. REQ-004~010 Implemented 전환 후 실행.
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-011 최초 생성 (source Discovery: dcy-002 문서화 클로저)
#### Intent
- dcy-002 구현 완료 후 baseline 문서 갱신 요구사항을 독립 Documentation REQ로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md
- docs/data-model.md
- docs/runtime-flows.md
- Modules:
- none
- Tests:
- none
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정. REQ-004~010 모두 Implemented 전환 후 이 REQ를 별도 구현 task로 수행.
@@ -0,0 +1,95 @@
# req-004: GET /health 응답 스키마 확장
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic Consumer-Driven Contract는 GET /health가 status, service, version, checked_at을 반환할 것을 요구한다.
- 현재 구현은 `{"status": "ok"}`만 반환하므로 계약 스키마를 충족하도록 응답을 확장한다.
## Requirement
- GET /health 엔드포인트는 status, service(고정값 "portfolio-hub"), version(앱 버전), checked_at(ISO 8601 KST) 필드를 포함한 JSON을 반환해야 한다.
- status 허용값은 ok, degraded, failed이며 정상 응답은 ok를 반환해야 한다.
## Acceptance Criteria
1. Given 서비스가 정상 동작 중일 때, When GET /health를 요청하면, Then 응답 JSON에 status, service, version, checked_at 필드가 모두 존재해야 한다.
2. Given GET /health 응답을 받을 때, When status 값을 확인하면, Then "ok", "degraded", "failed" 중 하나여야 한다.
3. Given GET /health 응답을 받을 때, When checked_at 형식을 검증하면, Then ISO 8601 형식을 만족해야 한다.
4. Given GET /health 응답을 받을 때, When service 필드를 확인하면, Then 고정값 "portfolio-hub"여야 한다.
5. Given 인증 없이 GET /health를 요청해도, Then 200 응답이 반환되어야 한다(비인증 엔드포인트).
## Impacted Area
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/runtime-flows.md
- Modules: app/api/health.py, app/schemas/ (HealthResponse 스키마 추가)
- Tests: tests/test_health.py (신규) 또는 tests/test_app.py 확장
## Notes
- degraded 상태를 반환하는 트리거 조건은 이번 반복에서 정의하지 않는다(OQ-2 Answered). 스키마에 허용값으로만 포함한다.
- 앱 버전은 pyproject.toml의 version 필드 또는 settings에서 읽어 반환한다. 설계 단계에서 구체적 방법을 확정한다.
- Source Discovery: dcy-002 (FR-1)
## Change Log
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-004 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 5개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-004 최초 생성 (source Discovery: dcy-002 FR-1)
#### Intent
- trex-basic Consumer-Driven Contract의 GET /health 응답 스키마 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md (REQ-011 에서 갱신 예정)
- Modules:
- app/api/health.py
- Tests:
- tests/test_health.py 신규
#### Delivery Trace Delta
- Batches:
- none (batch 미생성 상태)
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정
@@ -0,0 +1,99 @@
# req-005: service-token 발급 시 runtime_id 동시 생성
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic 데몬은 GET /v1/user_info에서 runtime_id를 조회해 자신을 식별한다.
- runtime_id는 service-token 발급 시 함께 생성되어 해당 토큰에 연결 저장되어야 한다.
- CLI(pfhub service-token issue)와 API 엔드포인트 양쪽에서 발급 결과에 runtime_id가 포함되어야 한다.
## Requirement
- pfhub service-token issue CLI 커맨드와 대응 API 엔드포인트는 service-token 발급 시 고유 runtime_id를 동시 생성하고 auth_tokens 테이블에 연결 저장한 뒤 발급 결과(token + runtime_id)를 반환해야 한다.
- runtime_id는 해당 service-token에 1:1 연결되며, 동일 사용자의 다른 service-token은 각기 다른 runtime_id를 가진다.
## Acceptance Criteria
1. Given `pfhub service-token issue <name> --user <username>` 실행 시, When 발급이 성공하면, Then 출력에 token 문자열과 runtime_id가 모두 포함되어야 한다.
2. Given API 엔드포인트(POST /auth/tokens 또는 동등 경로)로 service-token 발급 시, When 성공 응답을 받으면, Then 응답 JSON에 token과 runtime_id가 포함되어야 한다.
3. Given runtime_id가 발급된 service-token으로 GET /v1/user_info를 호출하면, Then 응답의 runtime_id가 발급 시 생성된 값과 동일해야 한다.
4. Given 동일 사용자에 대해 두 번의 service-token 발급을 수행하면, Then 각각 다른 runtime_id가 반환되어야 한다.
5. Given auth_tokens 테이블을 직접 조회하면, Then 발급된 service-token 행에 runtime_id 값이 저장되어 있어야 한다.
## Impacted Area
- Docs: docs/runtime-flows.md (REQ-011에서 갱신 예정)
- Modules: app/cli/commands/service_tokens.py, app/api/auth_routes.py, app/domain/models.py (AuthToken.runtime_id 필드 추가), app/services/auth_service.py, app/repositories/postgres.py
- Tests: tests/test_auth.py 확장 또는 tests/test_service_tokens.py (신규)
## Notes
- runtime_id 포맷은 UUID v4를 권장한다. 설계 단계에서 확정한다.
- auth_tokens 테이블에 runtime_id 컬럼 추가는 REQ-009 migration에서 처리한다.
- 기존 service-token(runtime_id 없음) 처리 방식은 R-4로 식별됨. GET /v1/user_info(REQ-007) 설계 단계에서 결정한다.
- Source Discovery: dcy-002 (FR-2, A-2)
## Change Log
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-005 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 5개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-005 최초 생성 (source Discovery: dcy-002 FR-2)
#### Intent
- service-token 발급 시 runtime_id 동시 생성 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/runtime-flows.md (REQ-011에서 갱신 예정)
- Modules:
- app/cli/commands/service_tokens.py
- app/api/auth_routes.py
- app/domain/models.py
- Tests:
- tests/test_auth.py 확장 또는 tests/test_service_tokens.py 신규
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정. REQ-009 migration과 함께 delivery 단위 구성 권장.
@@ -0,0 +1,100 @@
# req-006: GET /v1/assets 엔드포인트 구현
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic 데몬이 관리 자산 목록과 전략 파라미터를 조회할 수 있도록 portfolio 데이터를 asset 뷰로 변환해 제공한다.
- asset_id는 기존 portfolio_id를 그대로 사용하며 별도 식별자 체계를 만들지 않는다(A-3).
## Requirement
- GET /v1/assets는 service-token bearer 인증으로 토큰 소유자의 portfolio 목록을 asset_id, symbol, market, status, cash, quantity, broker_type, strategy_id, strategy_params, updated_at 필드로 반환해야 한다.
- status=active 쿼리 파라미터로 필터링이 가능해야 한다.
- strategy_params는 해당 portfolio의 active_strategy_id로 조회한 StrategySet.config에서 파생한다. strategy_id는 허용값 검증 없이 그대로 반환한다(OQ-1 Answered).
## Acceptance Criteria
1. Given 유효한 service-token bearer 인증으로 GET /v1/assets 요청 시, When 토큰이 유효하면, Then HTTP 200과 해당 사용자 소유 portfolio 목록이 반환되어야 한다.
2. Given GET /v1/assets?status=active 요청 시, Then status가 active인 항목만 반환되어야 한다.
3. Given 응답의 각 항목을 확인하면, Then asset_id, symbol, market, status, cash, quantity, broker_type, strategy_id, strategy_params, updated_at 필드가 모두 포함되어야 한다.
4. Given portfolio에 연결된 StrategySet이 있을 때, When strategy_params를 확인하면, Then StrategySet.config 값에서 파생된 JSON 객체여야 한다.
5. Given StrategySet config가 없거나 파싱 실패 시, Then strategy_params는 빈 객체({})를 반환하고 에러가 전파되지 않아야 한다.
6. Given portfolios.market이 NULL인 경우, Then 응답의 market 필드도 null로 반환되어야 한다(DECIDE_MARKET_DEFAULT Answered).
7. Given 인증 없이 GET /v1/assets 요청 시, Then HTTP 401이 반환되어야 한다.
## Impacted Area
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/project-structure.md (신규 라우터 파일 반영 필요)
- Modules: app/api/v1_assets.py (신규), app/services/ (assets 조회 서비스 메서드 추가), app/schemas/ (v1 assets 응답 스키마 추가), app/repositories/postgres.py (portfolio + strategy_set JOIN 쿼리 추가), app/main.py (/v1 라우터 등록)
- Tests: tests/test_v1_assets.py (신규)
## Notes
- broker_type은 portfolios.broker 컬럼을 그대로 매핑한다(m-2 finding 반영).
- strategy_id는 portfolios.active_strategy_id를 그대로 반환한다.
- GET /v1/assets의 소유권 스코프는 service-token 소유자(owner_user_id)를 기준으로 한다.
- Source Discovery: dcy-002 (FR-3, A-3, OQ-1)
## Change Log
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-006 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 7개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-006 최초 생성 (source Discovery: dcy-002 FR-3)
#### Intent
- trex-basic Consumer-Driven Contract의 GET /v1/assets 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md (REQ-011에서 갱신 예정)
- Modules:
- app/api/v1_assets.py 신규
- app/main.py (/v1 라우터 등록)
- Tests:
- tests/test_v1_assets.py 신규
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정. REQ-009 migration(market 컬럼 추가)과 함께 delivery 단위 구성 권장.
@@ -0,0 +1,103 @@
# req-007: GET /v1/user_info 엔드포인트 구현
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic 데몬이 사용자 거래 가능 여부와 브로커 인증 정보를 조회할 수 있도록 한다.
- runtime_id는 인증 service-token에 연결된 값을 반환하며, environment 필드는 포함하지 않는다(NFR-1).
- credential_payload는 DB에서 Fernet 복호화 후 반환한다(NFR-3/REQ-010 의존).
## Requirement
- GET /v1/user_info는 service-token bearer 인증으로 runtime_id, trading_enabled(users.is_active 매핑), disabled_reason, brokers[](broker, account_ref, credential_payload, credential_version, last_rotated_at)를 반환해야 한다.
- environment 필드를 응답에 포함하지 않는다.
- credential_payload는 Fernet으로 복호화된 JSON을 반환한다.
## Acceptance Criteria
1. Given runtime_id가 연결된 service-token으로 GET /v1/user_info 호출 시, Then HTTP 200과 응답 JSON에 runtime_id, trading_enabled, brokers[] 필드가 포함되어야 한다.
2. Given GET /v1/user_info 응답을 확인하면, Then environment 필드가 존재하지 않아야 한다.
3. Given users.is_active가 true인 사용자의 service-token으로 호출 시, Then 응답의 trading_enabled가 true여야 한다.
4. Given users.is_active가 false인 사용자의 service-token으로 호출 시, Then 응답의 trading_enabled가 false여야 한다.
5. Given broker_credentials에 KIS credential이 저장된 사용자의 토큰으로 호출 시, When 응답의 brokers[] 항목을 확인하면, Then credential_payload가 복호화된 JSON({app_key, app_secret})으로 반환되어야 한다.
6. Given runtime_id가 없는 구형 service-token으로 호출 시, Then 설계 단계에서 확정된 오류 응답이 반환되어야 한다(R-4, 404 또는 즉시 runtime_id 생성 중 선택).
7. Given 인증 없이 요청 시, Then HTTP 401이 반환되어야 한다.
## Impacted Area
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/data-model.md (REQ-011에서 갱신 예정)
- Modules: app/api/v1_user_info.py (신규), app/domain/models.py (BrokerCredential 모델 추가), app/services/ (user_info 조회 서비스 메서드 추가), app/schemas/ (v1 user_info 응답 스키마 추가), app/repositories/postgres.py (broker_credentials 조회 추가), app/main.py (/v1 라우터 등록)
- Tests: tests/test_v1_user_info.py (신규)
## Notes
- trading_enabled는 users.is_active 단순 매핑이다(A-4 Confirmed). 별도 trading 전용 상태 필드는 이번 반복 Out of Scope.
- disabled_reason: trading_enabled가 false일 때 이유 문자열. 이번 반복에서는 is_active가 false인 경우 "account_inactive" 고정값을 사용하는 방안을 설계 단계에서 확정한다.
- R-4(기존 service-token에 runtime_id 없는 경우 처리): 404 또는 즉시 생성 중 설계 단계에서 결정. 이 REQ의 Acceptance Criteria 6번은 설계 결정 후 구체화한다.
- Fernet 복호화는 REQ-010에서 정의한 암호화 유틸리티를 사용한다.
- Source Discovery: dcy-002 (FR-4, NFR-1, A-4)
## Change Log
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-007 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 7개, Impacted Area, Change Log) 모두 충족 확인 후 승인
- 설계 의존성 주의: AC #6(runtime_id 없는 구형 토큰 R-4 처리: 404 또는 즉시 생성)은 설계 단계에서 확정 필요. 핵심 Requirement가 명확하고 Notes에 명시적으로 deferred 처리됨 — 승인 게이트 통과 판단.
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch. 설계 단계에서 R-4 처리 방식 확정 후 AC #6 구체화.
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-007 최초 생성 (source Discovery: dcy-002 FR-4)
#### Intent
- trex-basic Consumer-Driven Contract의 GET /v1/user_info 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md (REQ-011에서 갱신 예정)
- Modules:
- app/api/v1_user_info.py 신규
- app/domain/models.py BrokerCredential 추가
- Tests:
- tests/test_v1_user_info.py 신규
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정. REQ-009(broker_credentials 테이블), REQ-010(Fernet 암호화), REQ-005(runtime_id)와 함께 delivery 단위 구성 권장.
@@ -0,0 +1,100 @@
# req-008: POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 구현
- Status: Approved
- Type: Interface
- Priority: High
- Owner: repository-maintainers
## Intent
- trex-basic이 체결 내역을 portfolio-hub에 idempotent하게 전송할 수 있도록 한다.
- 동일 (asset_id, order_id, execution_id) 조합의 중복 수신은 DB unique constraint로 감지하고 status=duplicate를 반환한다(NFR-2).
- 동일 키에 다른 payload가 오면 충돌로 처리한다.
## Requirement
- POST /v1/assets/{asset_id}/ledger/trades는 service-token bearer 인증으로 체결 내역(execution_id, order_id, side, quantity, price, fee_amount, tax_amount, executed_at)을 ledger_trades 테이블에 저장하고 HTTP 201을 반환해야 한다.
- 동일 (asset_id, order_id, execution_id) 재수신은 신규 행 생성 없이 status=duplicate와 기존 ledger_entry_id를 HTTP 200으로 반환해야 한다.
- 동일 키에 payload가 다른 경우 HTTP 409를 반환해야 한다.
- idempotency는 DB unique constraint로 보장하며, 애플리케이션 로직만으로 보장하지 않는다.
## Acceptance Criteria
1. Given 유효한 service-token과 신규 체결 데이터로 POST /v1/assets/{asset_id}/ledger/trades 요청 시, Then HTTP 201과 생성된 ledger_entry_id가 반환되어야 한다.
2. Given 동일 (asset_id, order_id, execution_id)와 동일 payload로 두 번째 요청 시, Then HTTP 200과 status="duplicate", 기존 ledger_entry_id가 반환되어야 하며 DB에 단일 행만 존재해야 한다.
3. Given 동일 (asset_id, order_id, execution_id)에 다른 payload로 요청 시, Then HTTP 409가 반환되어야 한다.
4. Given asset_id가 존재하지 않는 portfolio_id인 경우, Then HTTP 404가 반환되어야 한다.
5. Given 인증 없이 요청 시, Then HTTP 401이 반환되어야 한다.
## Impacted Area
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/data-model.md (REQ-011에서 갱신 예정)
- Modules: app/api/v1_ledger.py (신규), app/domain/models.py (LedgerTrade 모델 추가), app/services/ (ledger 저장 서비스 메서드 추가), app/schemas/ (v1 ledger 요청/응답 스키마 추가), app/repositories/postgres.py (ledger_trades 저장 및 중복 감지 쿼리 추가), app/main.py (/v1 라우터 등록)
- Tests: tests/test_v1_ledger.py (신규)
## Notes
- NFR-2(idempotency DB unique constraint): (asset_id, order_id, execution_id) unique constraint는 REQ-009 migration에서 정의한다.
- ledger_trades 테이블과 기존 portfolio_ledger 테이블은 별개다. portfolio_ledger는 내부 원장이고 ledger_trades는 trex-basic이 외부에서 기록하는 체결 내역이다.
- 중복 감지 구현: DB unique constraint 위반(psycopg UniqueViolation) 캐치 → 기존 행 조회 → payload 동일 여부 비교 → duplicate 또는 409 응답.
- Source Discovery: dcy-002 (FR-5, NFR-2)
## Change Log
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-008 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 5개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-008 최초 생성 (source Discovery: dcy-002 FR-5)
#### Intent
- trex-basic Consumer-Driven Contract의 POST /v1/assets/{asset_id}/ledger/trades 요구사항을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/interface-contract.md (REQ-011에서 갱신 예정)
- Modules:
- app/api/v1_ledger.py 신규
- app/domain/models.py LedgerTrade 추가
- Tests:
- tests/test_v1_ledger.py 신규
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정. REQ-009(ledger_trades 테이블 + unique constraint)와 함께 delivery 단위 구성 필수.
@@ -0,0 +1,103 @@
# req-009: v1 API 지원을 위한 DB 스키마 migration 추가
- Status: Approved
- Type: Migration
- Priority: High
- Owner: repository-maintainers
## Intent
- dcy-002에서 요구하는 4개 DB 변경(portfolios.market 컬럼, auth_tokens.runtime_id 컬럼, broker_credentials 테이블, ledger_trades 테이블)을 Alembic revision으로 구현한다.
- IF NOT EXISTS 패턴을 유지해 REQ-002 정책을 준수한다(NFR-4).
## Requirement
- 다음 스키마 변경을 IF NOT EXISTS 패턴을 사용하는 신규 Alembic migration으로 추가해야 한다.
1. portfolios 테이블에 market TEXT NULLABLE 컬럼 추가 (기존 행은 NULL로 유지)
2. auth_tokens 테이블에 runtime_id TEXT UNIQUE NULLABLE 컬럼 추가
3. broker_credentials 테이블 신규 생성 (user_id, broker, account_ref, encrypted_payload, credential_version, last_rotated_at 포함)
4. ledger_trades 테이블 신규 생성 (ledger_entry_id PK, asset_id, order_id, execution_id, side, quantity, price, fee_amount, tax_amount, executed_at, recorded_at 포함, (asset_id, order_id, execution_id) unique constraint 포함)
## Acceptance Criteria
1. Given alembic upgrade head 실행 후, When portfolios 테이블을 조회하면, Then market TEXT NULLABLE 컬럼이 존재해야 한다.
2. Given alembic upgrade head 실행 후, When auth_tokens 테이블을 조회하면, Then runtime_id TEXT NULLABLE UNIQUE 컬럼이 존재해야 한다.
3. Given alembic upgrade head 실행 후, Then broker_credentials 테이블이 존재해야 한다.
4. Given alembic upgrade head 실행 후, Then ledger_trades 테이블이 존재해야 한다.
5. Given (asset_id, order_id, execution_id) 동일 조합으로 ledger_trades에 두 번 삽입 시, Then UniqueViolation이 발생해야 한다.
6. Given migration을 이미 실행한 DB에 동일 migration을 재실행하면, Then 에러 없이 완료되어야 한다(IF NOT EXISTS 보장).
7. Given portfolios 테이블의 기존 행, When migration 실행 후 조회하면, Then market 값이 NULL이어야 한다(기존 데이터 보정 없음, DECIDE_MARKET_DEFAULT Answered).
## Impacted Area
- Docs: docs/data-model.md (REQ-011에서 갱신 예정)
- Modules: migrations/versions/ (신규 migration 파일 추가), app/domain/models.py (Portfolio.market, AuthToken.runtime_id, BrokerCredential, LedgerTrade 모델 필드 추가), app/repositories/postgres.py (신규 테이블 쿼리 추가)
- Tests: tests/test_app.py 또는 migration 검증 테스트
## Notes
- NFR-4(IF NOT EXISTS 패턴) 준수 — REQ-002 Alembic migration 정책 기반.
- broker_credentials 테이블의 encrypted_payload 컬럼은 Fernet 암호화된 ciphertext를 TEXT로 저장한다. 암복호화 로직은 REQ-010 참조.
- 컬럼 상세 타입(NUMERIC vs FLOAT, TIMESTAMPTZ 등)은 설계 단계(draft-batch-design)에서 확정한다.
- migration revision ID는 구현 시 기존 chain의 마지막 revision(20260423_0013)을 down_revision으로 참조한다.
- Source Discovery: dcy-002 (영향 범위, NFR-4, DECIDE_MARKET_DEFAULT)
## Change Log
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-009 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 7개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-009 최초 생성 (source Discovery: dcy-002)
#### Intent
- v1 API 지원에 필요한 DB 스키마 변경을 REQ backlog로 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/data-model.md (REQ-011에서 갱신 예정)
- Modules:
- migrations/versions/ 신규 migration 파일
- app/domain/models.py 모델 필드 추가
- Tests:
- migration 검증 테스트
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정. REQ-005, REQ-006, REQ-007, REQ-008과 함께 delivery 단위 구성 필수(runtime_id, market, broker_credentials, ledger_trades 모두 이 migration에 의존).
@@ -0,0 +1,99 @@
# req-010: credential_payload Fernet 암호화 및 startup validation
- Status: Approved
- Type: Non-Functional
- Priority: High
- Owner: repository-maintainers
## Intent
- broker_credentials의 credential_payload(KIS app_key/app_secret)는 민감 데이터이므로 DB 평문 저장이 금지된다(R-1 High).
- Python cryptography 패키지의 Fernet(AES-128-CBC + HMAC-SHA256)으로 저장 전 암호화, 조회 시 복호화한다.
- CREDENTIAL_ENCRYPTION_KEY 환경변수가 없으면 서비스 기동을 거부해 운영 실수를 사전에 방지한다.
## Requirement
- 서비스 기동 시 CREDENTIAL_ENCRYPTION_KEY 환경변수(base64 인코딩 32바이트)가 존재하지 않으면 기동을 중단하고 에러 메시지를 출력해야 한다.
- broker_credentials에 credential_payload를 저장할 때 Fernet으로 암호화한 ciphertext를 저장해야 한다.
- broker_credentials에서 credential_payload를 조회할 때 Fernet으로 복호화한 JSON을 반환해야 한다.
## Acceptance Criteria
1. Given CREDENTIAL_ENCRYPTION_KEY가 설정된 환경에서 서비스 기동 시, Then 정상 기동되어야 한다.
2. Given CREDENTIAL_ENCRYPTION_KEY가 설정되지 않은 환경에서 서비스 기동 시, Then 기동이 실패하고 키 누락을 알리는 에러 메시지가 출력되어야 한다.
3. Given credential_payload를 broker_credentials에 저장한 뒤 DB를 직접 조회하면, Then 암호화된 ciphertext가 저장되어 있어야 한다(평문 app_key/app_secret 불가).
4. Given 암호화 저장된 credential_payload를 조회 API(GET /v1/user_info)로 읽으면, Then 복호화된 JSON({app_key: ..., app_secret: ...})이 반환되어야 한다.
5. Given 잘못된 키로 복호화를 시도하면, Then cryptography.fernet.InvalidToken 예외가 발생하고 500 또는 적절한 오류 응답이 반환되어야 한다.
## Impacted Area
- Docs: docs/project-structure.md (app/core/encryption.py 신규 모듈 반영 필요)
- Modules: app/core/config.py (CREDENTIAL_ENCRYPTION_KEY 설정 필드 및 startup validation 추가), app/core/security.py 또는 app/core/encryption.py (Fernet 암복호화 유틸리티 신규), app/repositories/postgres.py (broker_credentials 저장·조회 시 암복호화 적용)
- Tests: tests/test_auth.py 또는 신규 테스트에서 startup validation 및 암복호화 검증
## Notes
- CREDENTIAL_ENCRYPTION_KEY는 `from cryptography.fernet import Fernet; Fernet.generate_key()`로 생성한 base64 인코딩 키를 사용한다.
- startup validation은 FastAPI lifespan 이벤트 또는 Settings 초기화 시 수행한다. 설계 단계에서 위치를 확정한다.
- 키 로테이션 절차는 이번 반복 Out of Scope이다.
- Source Discovery: dcy-002 (NFR-3, R-1, OQ-3 Answered)
## Change Log
### CHG-20260502-02
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-010 confirm-req 승인 전환 (Status: Proposed → Approved)
#### Intent
- 필수 필드(Status, Type, Priority, Owner, Intent, Requirement, Acceptance Criteria 5개, Impacted Area, Change Log) 모두 충족 확인 후 승인
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: suggest-batch-reqs → draft-batch
### CHG-20260502-01
- Date: 2026-05-02
- Author: repository-maintainers
#### Change Summary
- REQ-010 최초 생성 (source Discovery: dcy-002 NFR-3)
#### Intent
- credential_payload 암호화 및 startup validation 요구사항을 독립 Non-Functional REQ로 승격 정규화
#### Acceptance Criteria Delta
- Added:
- 전체 Acceptance Criteria 신규 작성
#### Impacted Area Delta
- Docs:
- docs/project-structure.md (encryption 모듈 반영 필요)
- Modules:
- app/core/config.py startup validation 추가
- app/core/encryption.py 신규
- Tests:
- startup validation 및 암복호화 테스트
#### Delivery Trace Delta
- Batches:
- none
- Releases:
- none
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 배정. REQ-007(GET /v1/user_info), REQ-009(broker_credentials 테이블)과 함께 delivery 단위 구성 권장.
+14 -6
View File
@@ -74,7 +74,7 @@
## Next Requirement ID
- Current: `REQ-004`
- Current: `REQ-012`
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
## Requirement Register
@@ -84,11 +84,19 @@
| REQ-001 | 로컬 DB 초기화 및 마이그레이션 검증 시나리오 정의 | Testing | Approved | High | repository-maintainers | [REQ-001](./Testing/req-001_local-db-validation-scenarios.md) |
| REQ-002 | Alembic와 runtime schema ownership 경계 정리 | Migration | Approved | High | repository-maintainers | [REQ-002](./Migration/req-002_alembic-runtime-schema-ownership.md) |
| REQ-003 | 로컬 DB 운영 runbook 및 baseline 문서 갱신 | Documentation | Approved | Medium | repository-maintainers | [REQ-003](./Documentation/req-003_local-db-operations-runbook-and-baseline-updates.md) |
| REQ-004 | GET /health 응답 스키마 확장 | Interface | Approved | High | repository-maintainers | [REQ-004](./Interface/req-004_health-response-schema-extension.md) |
| REQ-005 | service-token 발급 시 runtime_id 동시 생성 | Interface | Approved | High | repository-maintainers | [REQ-005](./Interface/req-005_service-token-runtime-id-issuance.md) |
| REQ-006 | GET /v1/assets 엔드포인트 구현 | Interface | Approved | High | repository-maintainers | [REQ-006](./Interface/req-006_v1-assets-endpoint.md) |
| REQ-007 | GET /v1/user_info 엔드포인트 구현 | Interface | Approved | High | repository-maintainers | [REQ-007](./Interface/req-007_v1-user-info-endpoint.md) |
| REQ-008 | POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 구현 | Interface | Approved | High | repository-maintainers | [REQ-008](./Interface/req-008_v1-ledger-trades-endpoint.md) |
| REQ-009 | v1 API 지원을 위한 DB 스키마 migration 추가 | Migration | Approved | High | repository-maintainers | [REQ-009](./Migration/req-009_v1-api-db-schema-migration.md) |
| REQ-010 | credential_payload Fernet 암호화 및 startup validation | Non-Functional | Approved | High | repository-maintainers | [REQ-010](./Non-Functional/req-010_credential-payload-fernet-encryption.md) |
| REQ-011 | trex-basic 계약 반영을 위한 baseline 문서 갱신 | Documentation | Approved | Medium | repository-maintainers | [REQ-011](./Documentation/req-011_baseline-docs-update-for-trex-basic-contract.md) |
## Recent Change Log Summary
- 2026-04-30: REQ-003 Approved 전환 — 로컬 DB 운영 runbook 및 baseline 문서 갱신
- 2026-04-30: REQ-002 Approved 전환 — Alembic와 runtime schema ownership 경계 정리
- 2026-04-30: REQ-001 Approved 전환 — 로컬 DB 초기화 및 마이그레이션 검증 시나리오 정의
- 2026-04-30: REQ-003 Proposed 생성 — 로컬 DB 운영 runbook 및 baseline 문서 갱신 (source Discovery: dcy-001)
- 2026-04-30: REQ-002 Proposed 생성 — Alembic와 runtime schema ownership 경계 정리 (source Discovery: dcy-001)
- 2026-05-02: REQ-011 Approved 전환 — trex-basic 계약 반영을 위한 baseline 문서 갱신 (source Discovery: dcy-002)
- 2026-05-02: REQ-010 Approved 전환 — credential_payload Fernet 암호화 및 startup validation (source Discovery: dcy-002)
- 2026-05-02: REQ-009 Approved 전환 — v1 API DB 스키마 migration 추가 (source Discovery: dcy-002)
- 2026-05-02: REQ-008 Approved 전환 — POST /v1/assets/{asset_id}/ledger/trades 구현 (source Discovery: dcy-002)
- 2026-05-02: REQ-007 Approved 전환 — GET /v1/user_info 엔드포인트 구현 (source Discovery: dcy-002)