# REQ-047: broker_credentials 테이블 daemon-scoped PK 전환 마이그레이션 - Status: Implemented - Type: Migration - Priority: High - Owner: Justin Ko ## Intent - 현재 `broker_credentials` 테이블의 PK는 `(user_id, broker)`로, 같은 user의 두 데몬이 서로 다른 KIS 계좌를 가질 수 없다. PK를 `(daemon_id, broker)`로 전환하여 인증정보를 daemon-scoped로 소유하게 한다. - dcy-010에서 결정된 단계적 마이그레이션 경로(nullable `daemon_id` 컬럼 추가 → 백필(REQ-048) → PK 전환)에서, 이 REQ는 스키마 변경 단계를 담당한다. PK 전환 전 데이터 백필은 REQ-048에서 별도로 수행한다. - 이 마이그레이션은 비가역 PK 변경을 포함하는 고위험 작업으로, 팀장/사장님 결재 게이트(외부 비가역 작업)를 거쳐야 한다. ## Requirement - Alembic 신규 revision을 작성한다. (down_revision은 현재 최신 head에서 파악) - **Step 1 (nullable 컬럼 추가)**: `broker_credentials`에 `daemon_id TEXT NULL` 컬럼을 추가한다. 기존 데이터는 `daemon_id=NULL` 상태로 남는다(백필은 REQ-048이 별도 처리). - **Step 2 (PK 전환 준비)**: 백필 완료(REQ-048) 이후 적용할 별도 revision을 작성한다. 이 revision에서: - 기존 PK 제약 `(user_id, broker)`를 삭제한다. - `daemon_id NOT NULL` 제약을 추가한다. - 새 PK 제약 `(daemon_id, broker)`를 추가한다. - `user_id` 컬럼은 감사/이력 목적으로 유지한다(삭제 금지). - `app/repositories/postgres.py`의 `ensure_schema()` 내 `broker_credentials` 정의를 최종 daemon-scoped 스키마(daemon_id NOT NULL, PK=(daemon_id, broker))와 정합하게 갱신한다. 단, `ensure_schema()`의 갱신은 PK 전환 revision 적용 이후 활성화한다. - downgrade 함수는 PK 전환 이전 상태로 되돌릴 수 있도록 작성한다(daemon_id 컬럼 제거, 기존 PK 복원). 단, PK 전환 revision의 downgrade는 데이터 손실 위험이 있어 실 운영 적용 전 롤백 스크립트를 별도 제공한다. ## Acceptance Criteria 1. Given Step 1 revision 적용 후, When `\d broker_credentials` 확인, Then `daemon_id TEXT NULL` 컬럼이 존재하고 기존 PK `(user_id, broker)` 제약이 그대로 유지된다. 2. Given Step 1 revision 적용 후, When 기존 행을 조회, Then `daemon_id`가 NULL인 상태로 데이터가 보존된다. 3. Given Step 2(PK 전환) revision 적용 후 (REQ-048 백필 완료 이후), When `\d broker_credentials` 확인, Then PK 제약이 `(daemon_id, broker)`로 변경되고 `daemon_id NOT NULL` 제약이 존재한다. 4. Given AC-3 환경, When `user_id` 컬럼 존재 확인, Then `user_id` 컬럼이 여전히 존재한다(삭제되지 않음). 5. Given AC-3 환경, When `ensure_schema()` 단독 실행, Then `broker_credentials` 스키마가 마이그레이션과 동일하다(daemon_id NOT NULL, PK=(daemon_id, broker), user_id 존재). 6. Given Step 1 revision의 downgrade 실행, When `\d broker_credentials` 확인, Then `daemon_id` 컬럼이 제거되고 기존 PK `(user_id, broker)` 제약이 복원된다. 7. Given Step 2 revision의 downgrade 실행, When 실행, Then downgrade가 실패하거나 데이터 손실 경고를 명시적으로 표시한다(안전 장치 존재). ## Impacted Area - Docs: - `docs/data-model.md` — `BrokerCredential` 엔티티 PK 변경(`(user_id, broker)` → `(daemon_id, broker)`), `user_id` 유지 명시, daemon-scoped 소유 정책 기술. - Modules: - `migrations/versions/` — Step 1 revision (nullable daemon_id 추가), Step 2 revision (PK 전환). down_revision은 현재 최신 head. - `app/repositories/postgres.py` — `ensure_schema()` 내 `broker_credentials` 정의 갱신(PK 전환 revision 이후). - Tests: - alembic upgrade head 후 schema 검증(AC-1~5). - Step 1 downgrade 테스트(AC-6). - 기존 broker credential CRUD 회귀 테스트(마이그레이션 전후 데이터 보존). ## Notes - 이 마이그레이션은 **PK 변경**이라는 비가역 작업을 포함한다. 팀장/사장님 결재 게이트 대상이다(dcy-010 R-1, §범위 경계 — "DB 마이그레이션"은 외부 비가역 작업). 실 운영 DB 적용 전 반드시 결재를 받는다. - Step 1과 Step 2를 별도 revision으로 분리하는 이유: REQ-048 백필이 완료된 후에만 PK 전환이 가능하기 때문이다. Step 1만 먼저 적용하고, REQ-048 백필 → Step 2 순서로 진행한다. - `user_id` 컬럼을 유지하는 이유: 기존 인증정보의 소유자 이력 보존 및 admin 조회 기능 유지. - daemon-scoped 강제(user 폴백 없음): dcy-010 OQ-1 결정. `daemon_id=NULL`인 인증정보는 PK 전환 이후 INSERT 불가(NOT NULL 제약). 기존 NULL 데이터는 REQ-048 백필 이후만 PK 전환이 가능하다. - Step 2 downgrade는 daemon_id → user_id PK 복원 시 데이터 무결성을 보장하기 어려우므로, 운영 환경에서는 rollback 전 별도 데이터 검증이 필요하다. - Source: dcy-010 FR-1, R-1 완화 방안, §4 C1. ## Change Log ### CHG-20260628-01 - Date: 2026-06-28 - Author: Justin Ko #### Change Summary - 신규 생성 (Proposed) — dcy-010 FR-1 산출물 (마이그레이션 계층 분리). #### Intent - dcy-010 FR-1의 스키마 PK 전환 작업을 독립 마이그레이션 REQ로 정규화한다. Step 1(nullable 추가)과 Step 2(PK 전환)를 별도 revision으로 분리하는 설계를 반영한다. #### Acceptance Criteria Delta - Added: - AC-1 ~ AC-7 전체 (신규 초안) - Updated: 없음 - Removed: 없음 #### Impacted Area Delta - Docs: - `docs/srs/Migration/req-047_broker-credentials-daemon-scoped-pk-migration.md` 신규 생성 - `docs/data-model.md` 갱신 예고 - Modules: - `migrations/versions/` (Step 1, Step 2 revision 신규), `app/repositories/postgres.py` 갱신 예고 - Tests: - alembic 스키마 검증, downgrade, CRUD 회귀 신규 #### 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 후 batch 배정. REQ-048(백필)과 순서 의존 관계 명시. #### Validation Plan - alembic upgrade head 후 schema 검증(AC-1~5). Step 1 downgrade 검증(AC-6). 개발 DB에서 dry-run. ### CHG-20260628-02 - Date: 2026-06-28 - Author: 팀장(Claude) - confirm-req: `Proposed` → `Approved`. dcy-010 FR-1/R-1 추적. Step1/Step2 분리 + REQ-048 백필 사이 순서 정합. **Step2 PK 전환(실DB 적용)은 비가역 → 사장님 결재 게이트 대상**(Notes 명시 확인). blocker 없음. ### CHG-20260628-03 - Date: 2026-06-28 - Author: 팀장(Claude) #### Change Summary - BAT-020 PR #17 머지(mergeCommit d4acfd4) — Step1/Step2 마이그레이션 revision 코드가 포함됨. 그러나 **실 운영 DB 적용 및 실 PostgreSQL 통합검증이 비가역 사장님 결재 게이트로 미완** → Implemented 보류, `Approved` 상태 유지. #### Intent - AC-3/4/5(Step2 적용 후 schema)는 real-DB evidence 대기 중. 결재·적용 시 동반수정 TODO: `ensure_schema()`·upsert ON CONFLICT를 (daemon_id, broker)로 갱신. Implemented 전환은 실 DB 적용 결재 이후 별도 수행. #### Acceptance Criteria Delta - Added: 없음 - Updated: 없음 - Removed: 없음 #### Impacted Area Delta - 없음 (상태 전환 보류 기록) #### Delivery Trace Delta - Batches: BAT-020 (PR #17, mergeCommit d4acfd4) — revision 코드 머지됨 - Releases: 미정 (Step2 실DB 적용은 사장님 결재 후) #### Revalidation Impact - Reverification Needed: yes — Step2 실DB 적용 후 AC-3/4/5 re-evidence 필요 - Existing Implementation Invalidated: no - Status Recommendation: keep-approved (Implemented 보류) #### Validation Plan - 사장님 결재 → Step1 실DB 적용 → REQ-048 백필 → Step2 실DB 적용 → AC-1~7 전건 real-DB evidence 확보 후 Implemented 전환. ### CHG-20260628-04 - Date: 2026-06-28 - Author: 팀장(Claude) - confirm-req-implemented: `Approved` → `Implemented`. #### Change Summary - BAT-021 PR #18 머지(mergeCommit 7164f9e) — ensure_schema() broker_credentials 최종 daemon-scoped 스키마 갱신, _row_to_broker_credential 컬럼순서 정정, upsert ON CONFLICT (user_id,broker) → (daemon_id,broker) 수정, daemon 폴백 트랜잭션 abort 이슈(fallback 제거) 코드가 포함됨. - 실 dev PostgreSQL 통합검증: Step1 → backfill → Step2 → downgrade 순서 전건 실행, AC-1~7 PASS. - QC(opus) PASS-WITH-NOTES(blocker 0) 확인됨. #### Intent - Implemented 전환. AC-3/4/5(Step2 PK 전환 적용 후 schema) real-DB evidence 확보 완료. - **주의**: Implemented는 코드 + dev DB 통합검증 완료 기준. **Step2(PK 전환) 실 운영 DB 적용은 별도 release 결재 게이트**(런북 `docs/runbooks/bat-021-req047-step2-migration-runbook.md`, 배포순서 Step1→backfill→Step2→코드배포 엄수, alembic stamp 사전확인). 운영 적용 시 AC-3/4/5 실운영 evidence 재확보. #### Acceptance Criteria Delta - Added: 없음 - Updated: 없음 - Removed: 없음 #### Impacted Area Delta - 없음 (상태 전환 기록) #### Delivery Trace Delta - Batches: BAT-021 (PR #18, mergeCommit 7164f9e) — 동반 코드 수정 + 통합검증 완료 - Releases: 미정 (Step2 실 운영 DB 적용은 사장님 결재 후) #### Revalidation Impact - Reverification Needed: no (dev DB AC-1~7 전건 PASS, 운영 적용 시 재확보 별도 필요) - Existing Implementation Invalidated: no - Status Recommendation: Implemented #### Validation Plan - BAT-021 verification.md의 AC-1~7 전건 real-DB evidence 참조. 운영 적용 시 런북 절차 준수.