Files
trex-core/docs/srs/Integration/req-112_portfolio-hub-broker-credential-read-adapter.md
T

7.3 KiB

req-112: portfolio-hub broker credential read 포트 메서드 + HTTP 어댑터

  • Status: Approved
  • 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.
  • 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 파싱 실패 → PortfolioHubUnavailableError raise.
    • HTTP 403(daemon_id 없는 토큰) → PortfolioHubUnavailableError raise.
  • trex_core/adapters/portfolio/stub_read.py의 stub에도 fetch_broker_credential() 결정론 stub을 추가한다.
  • app_secret 필드는 어댑터 내에서 로그에 직접 출력하지 않는다(NFR-2 보안 — 마스킹).

Acceptance Criteria

  1. Given daemon_id에 매칭된 broker 인증정보가 있고 trading_enabled: true인 /v1/user_info 응답, When fetch_broker_credential() 호출, Then TradingContext(broker=BrokerCredentialPayload(account_ref=..., app_key=..., app_secret=...), trading_enabled=True) 반환.
  2. Given broker: null 응답(인증정보 없음), When fetch_broker_credential() 호출, Then TradingContext(broker=None, trading_enabled=<응답값>) 반환 (예외 없음).
  3. Given credential_payload: null(복호화 실패) 응답, When fetch_broker_credential() 호출, Then TradingContext(broker=None, trading_enabled=<응답값>) 반환 (예외 없음, 인증정보 부재로 처리).
  4. Given 네트워크 오류 또는 비-2xx(403/500 등) 응답, When fetch_broker_credential() 호출, Then PortfolioHubUnavailableError raise.
  5. Given fetch_broker_credential() 구현 이후, When whoami() 호출, Then 기존 whoami() 계약(daemon_id str 반환, PortfolioHubUnavailableError on failure)이 변경되지 않는다 (회귀 없음).
  6. Given stub_read.py stub, When fetch_broker_credential() 호출, Then 미리 설정한 고정 TradingContext를 네트워크 없이 반환한다.
  7. Given fetch_broker_credential() 구현, When 에러/성공 로그 출력 확인, Then app_secret 값이 평문으로 로그에 출력되지 않는다.
  8. 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.py stub 확장
  • 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(머지완료).