Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
7.7 KiB
7.7 KiB
req-112: portfolio-hub broker credential read 포트 메서드 + HTTP 어댑터
- Status: Implemented
- Type: Integration
- Priority: High
- Owner: trex-core-dev
Intent
- 현재
PortfolioHubReadAdapter.whoami()는GET /v1/user_info에서daemon_id만 읽는다. dcy-036 C1에 따라 같은 엔드포인트가 daemon-scoped 단일broker객체(account_ref,app_key,app_secret,credential_version)와trading_enabled필드를 함께 반환하도록 확장된 portfolio-hub 계약(REQ-049, dcy-010)을 소비해야 한다. - 이 REQ는 트랜스포트 레이어 — 포트 인터페이스 정의, HTTP 어댑터 구현, 테스트용 stub — 만 다루며, KISConfig 구성 로직(REQ-113)과 로드 게이트(REQ-114)의 기반을 제공한다.
Requirement
trex_core/application/portfolio.py에BrokerCredentialPayload데이터클래스를 추가한다:account_ref: str,app_key: str,app_secret: str,credential_version: int | None = None.PortfolioHubReadPort프로토콜에fetch_broker_credential() -> BrokerCredentialPayload | None메서드를 추가한다.- 반환 값 None: portfolio-hub 응답
broker: null(해당 daemon에 인증정보 없음). trading_enabled필드도 함께 읽어 반환해야 하므로, 포트 메서드는TradingContext데이터클래스를 반환한다:broker: BrokerCredentialPayload | None,trading_enabled: bool.
- 반환 값 None: portfolio-hub 응답
PortfolioHubReadAdapter의 기존whoami()메서드를 내부적으로GET /v1/user_info를 공유하는 내부 헬퍼(_fetch_user_info())로 리팩토링하고,fetch_broker_credential()구현이 이를 재사용하도록 한다. 외부 동작은 변경하지 않는다(whoami()시그니처·예외 동작 불변, 회귀 차단 NFR-1).fetch_broker_credential()은 다음 규칙을 따른다:broker: null응답 →TradingContext(broker=None, trading_enabled=trading_enabled)반환.credential_payload: null(복호화 실패) →TradingContext(broker=None, trading_enabled=trading_enabled)반환(정보 부재로 처리).- 네트워크 오류 / 비-2xx / JSON 파싱 실패 →
PortfolioHubUnavailableErrorraise. - HTTP 403(
daemon_id없는 토큰) →PortfolioHubUnavailableErrorraise.
trex_core/adapters/portfolio/stub_read.py의 stub에도fetch_broker_credential()결정론 stub을 추가한다.app_secret필드는 어댑터 내에서 로그에 직접 출력하지 않는다(NFR-2 보안 — 마스킹).
Acceptance Criteria
- Given daemon_id에 매칭된 broker 인증정보가 있고
trading_enabled: true인/v1/user_info응답, Whenfetch_broker_credential()호출, ThenTradingContext(broker=BrokerCredentialPayload(account_ref=..., app_key=..., app_secret=...), trading_enabled=True)반환. - Given
broker: null응답(인증정보 없음), Whenfetch_broker_credential()호출, ThenTradingContext(broker=None, trading_enabled=<응답값>)반환 (예외 없음). - Given
credential_payload: null(복호화 실패) 응답, Whenfetch_broker_credential()호출, ThenTradingContext(broker=None, trading_enabled=<응답값>)반환 (예외 없음, 인증정보 부재로 처리). - Given 네트워크 오류 또는 비-2xx(403/500 등) 응답, When
fetch_broker_credential()호출, ThenPortfolioHubUnavailableErrorraise. - Given
fetch_broker_credential()구현 이후, Whenwhoami()호출, Then 기존whoami()계약(daemon_id str 반환, PortfolioHubUnavailableError on failure)이 변경되지 않는다 (회귀 없음). - Given stub_read.py stub, When
fetch_broker_credential()호출, Then 미리 설정한 고정TradingContext를 네트워크 없이 반환한다. - Given
fetch_broker_credential()구현, When 에러/성공 로그 출력 확인, Thenapp_secret값이 평문으로 로그에 출력되지 않는다. - Given import sweep, When
trex_core/domain을 검사, Then 신규 포트/어댑터 관련 IO/네트워크 의존이 domain에 없다 (도메인 순수성 불변).
Impacted Area
- Docs: 없음 (baseline 문서 갱신은 dcy-036 scope 밖)
- Modules:
trex_core/application/portfolio.py—BrokerCredentialPayload,TradingContext데이터클래스 신규 정의;PortfolioHubReadPort에fetch_broker_credential()메서드 추가trex_core/adapters/portfolio/hub_read.py—_fetch_user_info()내부 헬퍼 추출,fetch_broker_credential()구현 추가;whoami()동작 보존trex_core/adapters/portfolio/stub_read.py—fetch_broker_credential()stub 추가
- Tests:
tests/adapters/portfolio/test_hub_read.py— AC-1~5, AC-7 케이스 추가tests/adapters/portfolio/test_stub_read.py— AC-6 stub 케이스 추가tests/domain/test_domain_import_sweep.py— AC-8 회귀(기존 sweep 재실행)
Notes
- 소비 계약: portfolio-hub
req-049_user-info-daemon-scoped-broker-delivery.md(Implemented, dcy-010 FR-2). 응답 스키마:broker: { account_ref, credential_payload: { app_key, app_secret } | null, credential_version, ... } | null+trading_enabled: bool. TradingContext를 별도 데이터클래스로 도입하는 이유: 포트 메서드가 broker 인증정보와trading_enabled를 한 번의 호출로 함께 반환해야 불필요한 중복 HTTP 호출을 피할 수 있기 때문(원자적 읽기).- OQ-4(그날-시작 로드 실패 알람 trex-notifier 연동): Deferred — 알람 발신은 REQ-114 Notes에서 다룬다.
- 선행 의존: portfolio-hub dcy-010 / REQ-049 배포 완료 여부에 따라 실 통합 테스트 가능 시점이 달라진다. CI 단계에서는 stub 기반 단위 테스트로 AC-1~8 검증.
- Source: dcy-036 C1, FR-1, dcy-010 OQ-2 Answered.
Change Log
CHG-20260628-01
- Date: 2026-06-28
- Author: trex-core-dev
Change Summary
- 신규 생성 (Proposed) — dcy-036 C1 산출물.
Intent
- dcy-036 FR-1(그날-시작 portfolio-hub
/v1/user_info에서 KIS 인증정보 fetch)의 트랜스포트 계층을 REQ로 정규화한다. KISConfig 구성(REQ-113) 및 로드 게이트(REQ-114)의 선행 의존.
Acceptance Criteria Delta
- Added:
- AC-1~8 전체 (신규 초안)
- Updated: 없음
- Removed: 없음
Impacted Area Delta
- Docs: 없음
- Modules:
portfolio.py포트 확장 (BrokerCredentialPayload, TradingContext, fetch_broker_credential)hub_read.py어댑터 확장 (_fetch_user_info 헬퍼, fetch_broker_credential 구현)stub_read.pystub 확장
- Tests:
test_hub_read.py,test_stub_read.py,test_domain_import_sweep.py
Delivery Trace Delta
- Batches: 미정 (suggest-batch-reqs 대기)
- Releases: 미정
Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 승인 게이트 후 REQ-112~115 일괄 batch 배정 검토.
Validation Plan
- pytest로 AC-1~8 각 케이스 확인. app_secret 마스킹(AC-7)은 로그 캡처 검증. domain sweep(AC-8)은 기존 test_domain_import_sweep.py 재실행.
CHG-20260628-02
- Date: 2026-06-28
- Author: 팀장(Claude)
- confirm-req:
Proposed→Approved. dcy-036 FR-1/C1 추적. TradingContext 원자 read(broker+trading_enabled 1회 호출) 설계 타당 — /v1/user_info 단일 응답에 정합. whoami 회귀보존(AC-5)·도메인 순수성(AC-8)·app_secret 마스킹(AC-7) 적절. blocker 없음. 선행 의존 portfolio-hub REQ-049(머지완료).
CHG-20260628-03
- Date: 2026-06-28
- Author: 팀장(Claude)
- confirm-req-implemented:
Approved→Implemented. 근거: BAT-053 PR #34 머지(merge commit01655ab). fetch 포트(fetch_broker_credential(),BrokerCredentialPayload,TradingContext) + HTTP 어댑터 + stub AC-1~8 전 케이스 pytest 검증 완료. QC PASS-WITH-NOTES(opus, blocker 0). 745 passed/0 회귀.