확장 안전 공고수집 배치 구현 통합 계획
목적과 범위
이 문서는 20260730-확장-안전-공고수집-배치-tdd.md,
20260730-확장-안전-공고수집-배치-design-db.md,
20260730-확장-안전-공고수집-배치-design-fe-web.md 및 BE-33~BE-43,
FE-19를 구현 순서로 통합한다. MySQL의 expand-only schema를 선행하고,
짧은 수집 slice와 durable SLA snapshot을 BE에서 구현한 뒤 기존 운영 화면에
additive 계약을 표시한다. 코드 변경은 이 계획의 범위가 아니다.
구현 전 차단 게이트 (G0)
C-01 — SLA snapshot item 계약 불일치
| 소비자 | slaSnapshot.items[] 계약 |
|---|---|
| BE TDD API Contract §API Contract | jobSourceId, sourceLabel, cycleStatus, isComplete, fetchedCount, abnormal, closeGuarded |
| DB 설계 snapshot source 테이블 | cycle status, fetched count, is-complete, abnormal, close-guarded만 영속 |
| FE 설계 §API 연동 | 위 필드 중 isComplete는 없고, 대신 listPageCount, detailRequestCount를 요구 |
두 요청량 필드는 09:00 당시의 immutable 값이어야 하지만 현 DB schema에는
captured_list_page_count와 captured_detail_request_count가 없다. 구현자가
current cycle의 값을 합성하면 SLA snapshot 불변성 계약을 깨므로 금지한다.
해결 소유자: senior BE + senior DBA + senior FE. 권고 결정: 요청량은
SLA late-source 목록의 요구사항이 아니므로 FE CollectionSlaSnapshotItem에서
listPageCount와 detailRequestCount를 제거하고, TDD/DB 계약의
isComplete를 추가한다. 요청량은 current CollectionRun에서만 표시한다.
요청량을 SLA item에도 표시해야 한다는 제품 요구가 확인되면 반대로 DB에 두
captured count 컬럼을 expand-only로 추가하고 BE TDD/API와 FE 타입을 함께
정정한다. 결정·문서 정정·세 시니어의 재확인이 G0 통과 근거다.
C-02 — MySQL 작업의 티켓 식별자 부재
DBA migration은 모든 BE 티켓의 선행조건이지만 tickets/에는 DB/INFRA 티켓이
없다. 구현 시작 전에 DBA가 DB-01(또는 프로젝트가 정한 새 식별자) 티켓을
추가하고 migration, rollback, 검증을 그 티켓에 귀속해야 한다. 이 계획에서는
이를 DB-01: collection cycle queue expand schema로 표기한다. 실제 파일명과
PR 제목은 생성된 티켓 식별자에 맞춰 바꾼다.
G0가 닫히기 전에는 branch/worktree를 만들거나 구현 wave를 열지 않는다.
확정 계약 체크리스트
G0 통과 뒤 아래가 모든 작업자의 공통 계약이다.
| 경계 | 계약 | 검증 책임 |
|---|---|---|
| MySQL → BE | cycles (job_source_id, run_date) unique, seen (cycle_id, source_job_id) unique, host limit unique host, SLA header/item unique keys와 Q1~Q10 indexes; FK/ENUM/inline backfill 없이 Flyway expand-only | MySQL implementer + infra reviewer |
| BE 수집 → external | collectSlice(descriptor)는 transaction 밖에서 실행하며 slice/daily budget, opaque cursor, host throttle을 지킨다 | BE-34~39 tests |
| BE 상태 → Operations | cycleStatus 6종, server-owned closeGuarded, BUDGET_EXHAUSTED는 abnormal=false, additive nullable fields 및 current summary | BE-40 contract tests |
| BE SLA → Operations | API에는 PENDING/CAPTURED/MISSED/NOT_APPLICABLE/UNAVAILABLE만 노출하고 CAPTURING은 노출하지 않는다. CAPTURED item/count는 immutable 09:00 gate 결과이고 MISSED는 재구성하지 않는다 | BE-43 + BE-40 tests |
| Operations API → FE | URL/queryKey는 유지, v2 field가 없는 응답은 legacy 표시, closeGuarded 키가 있을 때 추론 금지, snapshot status는 서버 값만 소비 | FE-19 MSW/component/page tests |
| Release | legacy collection scheduler와 v2 dispatcher의 같은 날 동시 outbound 요청 금지; ON 전 legacy disable, rollback은 v2 OFF → lease expiry → 다음 KST day legacy 재개 | BE-41 release test/operational evidence |
통합 DAG
flowchart LR G0[G0: SLA item contract + DB-01 ticket] --> DB01[DB-01 MySQL expand migration] DB01 --> IR[Infra review APPROVED/COMMENT] IR --> BE33[BE-33 cycle domain contract] G0 --> FE19[FE-19 optional API types, MSW, UI] BE33 --> BE35[BE-35 DB host throttle] BE33 --> BE36[BE-36 Saramin/Wanted slice] BE33 --> BE37[BE-37 Jumpit/Surfit slice] BE33 --> BE38[BE-38 Remember/Jobkorea slice] BE33 --> BE39[BE-39 company-bound slice] BE33 --> BE42[BE-42 seen lifecycle] BE33 --> BE43[BE-43 SLA snapshot] IR --> BE35 IR --> BE42 IR --> BE43 BE35 --> BE34[BE-34 dispatcher/lease recovery] BE36 --> BE34 BE37 --> BE34 BE38 --> BE34 BE39 --> BE34 BE43 --> BE34 BE33 --> BE40[BE-40 Operations contract] BE43 --> BE40 BE42 --> BE40 BE34 --> BE41[BE-41 v2 integration release] BE40 --> BE41 BE42 --> BE41 BE43 --> BE41 BE41 --> V[Cross-domain release verification] BE40 -. additive API integration evidence .-> V FE19 -. UI/API integration evidence .-> V
BE-34 ticket의 명시 의존에는 adapter tickets가 없지만, dispatcher가 실제
collectSlice를 실행하는 통합 테스트를 하려면 BE-35~39의 slice 구현이 필요하다.
따라서 이 계획에서는 기능 게이트로 이들을 모두 선행시킨다. BE-40에는
seenRowsOlderThan48Hours summary metric이 있으므로 BE-42도 API gate의
선행으로 둔다.
Wave 및 게이트
| Wave | 도메인 | 티켓/작업 | branch 및 worktree | 시작 조건 | 종료 근거 / 다음 게이트 |
|---|---|---|---|---|---|
| 0 | 계약 | G0 C-01, C-02 | 코드 branch 없음 | 사용자 구현 승인 | 정정 문서 + 세 시니어 확인; DB-01 ticket 존재 |
| 1 | MySQL | DB-01 migration | feat/DB-01-collection-cycle-queue-schema, .worktrees/DB-01 | G0 | Flyway migration/test raw output, expand-only 확인, private-infra-reviewer APPROVED/COMMENT (G1) |
| 2 | BE / FE | BE-33; FE-19 | feat/BE-33-collection-cycle-domain-contract, .worktrees/BE-33; feat/FE-19-collection-cycle-operations-visibility, .worktrees/FE-19 | G1 for BE-33; G0 for FE-19 mock-only 작업 | 각 PR code review merge. FE-19은 optional MSW/legacy tests만으로 병합 가능, 실제 API 통합은 G4에서 확인 |
| 3 | BE | BE-35, BE-36, BE-37, BE-38, BE-39, BE-42, BE-43 | 각 feat/<ticket-id>-<ticket-slug>, .worktrees/<ticket-id> | BE-33 merged, G1 | 티켓별 test exit 0 + code reviewer merge (G2); same-wave file overlap 없음 확인 |
| 4 | BE | BE-34, BE-40 | feat/BE-34-collection-dispatcher-lease-recovery, .worktrees/BE-34; feat/BE-40-collection-operations-contract, .worktrees/BE-40 | G2: BE-34는 35~39/43, BE-40은 42/43까지 merge | 티켓별 tests/review merge (G3). API response fixture를 FE-19 test와 교차 검증 |
| 5 | BE 통합 | BE-41 | feat/BE-41-collection-v2-integration-release, .worktrees/BE-41 | G3 및 BE-42 merged | multi-instance + 2,594 fixture + legacy/v2 mutual-exclusion test, review merge (G4) |
| 6 | 통합 확인 | 코드 티켓 없음 | branch 없음 | G4 + FE-19 merged | BE-41 release verification와 FE-19 real additive response smoke evidence. main 머지는 dev 배포 트리거이며 prod 배포는 별도 QA PASS 필요 |
Wave 너비 분포는 코드 wave 기준 1, 2, 7, 2, 1이다. 단독 wave 1과 5는
각각 DB 인프라 리뷰 게이트와 공통 config/scheduler wiring이라는 의도된 단일
writer 통합 작업이다. 모든 작업 branch는 매 wave 시작 전 origin/main에서
독립으로 만들며, 한 ticket PR이 리뷰 통과하면 즉시 main에 squash merge하고
branch/worktree를 삭제한다. 장수명 feature branch는 만들지 않는다.
도메인 간 게이트 운영
- G0 계약 게이트: C-01/C-02가 해소되기 전 MySQL, BE, FE 구현을 시작하지 않는다.
- G1 인프라 게이트: DB-01의 Flyway 검증과 infra review verdict가
APPROVED또는COMMENT여야 BE-33을 시작한다. - G2 slice/SLA 게이트: BE-33 및 BE-35~39/42/43의 merged PR과 raw test evidence를 확인한 뒤 BE-34/40을 시작한다.
- G3 API 게이트: BE-40의 additive API contract tests와 BE-34의 transaction-boundary/lease tests가 통과해야 BE-41의 common scheduler/config wiring을 시작한다.
- G4 release gate: BE-41은 legacy/v2 mutual exclusion, two-instance claim, budget fixture, 09:00 snapshot-before-notification을 증명해야 한다. FE-19은 real BE-40 response fixture로 legacy/v2 rendering을 확인한다.
각 ticket의 private-code-reviewer verdict가 모든 P0P3 반영을 확인한 뒤에만
PR auto-merge한다. G1의 MySQL migration만 4의 raw test/PR artifacts가 있어야 한다.private-infra-reviewer를 사용한다.
완료 선언은 rules/COMPLETION-RULE.md §1
작업자 handoff 메시지
MySQL implementer
G0 통과와 DB-01 ticket 생성 후 시작하세요.
origin/main에서feat/DB-01-collection-cycle-queue-schema/.worktrees/DB-01을 만드세요. DB 설계의 신규 cycles, seen postings, external host limits, SLA snapshot header, SLA snapshot sources와 Q1~Q10 index를 Flyway expand-only DDL로 구현하세요. FK, ENUM, JSON, BOOLEAN, inline backfill DML은 금지하고 DATETIME(6), table/column COMMENT, 유일키와 index 명명 규칙을 지키세요. 기존 runs table은 변경하지 마세요. Flyway migration validation/integration test raw output, migration filename, rollback (새 테이블은 남겨도 됨)을 보고하세요. push 후 infra review를 요청하고, verdict APPROVED/COMMENT 전에는 merge하지 마세요.
BE lead (private-senior-be)
G0/G1 통과 후 BE wave를 지휘하세요. 티켓별 branch/worktree는 최신
origin/main에서 독립 생성하고 single-writer 검사를 하세요. Wave 2는 BE-33, wave 3은 BE-35~39/42/43 병렬, wave 4는 BE-34/40, wave 5는 BE-41입니다. 특히 HTTP 호출과 throttle wait에는 Spring transaction이 없어야 하며, stale lease token 저장은 0 row로 거부하고 같은 KST day 재호출을 만들면 안 됩니다. SLA capture는 DB gate로 cycle write와 직렬화하고, cutoff를 놓치면 current state를 재구성하지 않고MISSED만 기록하세요. BE-40은 C-01에서 확정된 exact item contract만 노출하세요. 각 ticket은 test exit 0 raw output, reviewer verdict, PR URL/merge raw output을 TPM에 보내고, 선행 merged evidence 없이 후행 wave를 열지 마세요.
FE lead (private-senior-fe)
G0의 C-01 exact API 타입이 정정·확정된 뒤 FE-19를 진행하세요.
origin/main기준feat/FE-19-collection-cycle-operations-visibility/.worktrees/FE-19을 사용합니다. BE-40 병합 전에는 MSW/optional additive contract와 legacy fallback으로 구현·검토할 수 있지만, G4에서 실제 BE-40 response fixture로 반드시 다시 검증하세요.closeGuarded키가 있으면 서버 값만 사용하고 null도 legacy fallback의 근거로 쓰지 마세요. SLA UI는 captureStatus만으로 분기하며 브라우저 시간·current cycles로 count/items를 추론하지 마세요. build/test raw output, review verdict, PR merge evidence를 보고하세요.
Reviewers
Infra reviewer: DB-01에 대해 expand-only, no-FK/ENUM/inline-DML, column/index/unique key, Flyway validation, rollback compatibility를 확인하고
APPROVED,COMMENT,REQUEST_CHANGESverdict 및 근거를 남기세요.Code reviewer: 각 BE/FE ticket PR에서 TDD/DB/FE 계약, wave의 single-writer scope, tests raw output, P0
P3를 검토하세요. P0P3 모두 반영될 때만 squash auto-merge를 수행하세요. BE-43/40은 CAPTURING 비노출·immutable CAPTURED·MISSED no-reconstruction, BE-41은 legacy/v2 no-double-collection, FE-19는 optional/legacy 및 closeGuarded semantics를 필수 확인 항목으로 삼으세요.
진행 상태
| 도메인 | Wave 0 | Wave 1 | Wave 2 | Wave 3 | Wave 4 | Wave 5 | Wave 6 |
|---|---|---|---|---|---|---|---|
| 계약 | 차단: C-01/C-02 | - | - | - | - | - | - |
| MySQL | - | 대기 DB-01 | - | - | - | - | - |
| BE | - | - | 대기 BE-33 | 대기 35~39/42/43 | 대기 34/40 | 대기 41 | 검증 대기 |
| FE | - | - | 대기 FE-19 | - | - | - | API 통합 검증 대기 |
차단 요인
- C-01: FE SLA item 필드와 BE/DB immutable snapshot 필드가 일치하지 않는다.
- C-02: MySQL 선행 migration을 추적·리뷰·PR 머지할 ticket ID가 없다.
이 두 항목이 해소되면 위 계획을 진행 중으로 전환하고 G1부터 실행한다.