아이티데일리 E2E MVP — TO-BE 가이드

Concat · TO-BE — ADR-0013

아이티데일리 E2E MVP — TO-BE 가이드

TO-BE 재설계(PR #25)가 무엇을 바꿨는지, AS-IS 가이드와 짝을 이뤄 비교로 정리했습니다. 파일럿 완료가 「배관 검증」에서 「수집 → 재구성 → 검수 → 발행 → REST 제공」 서비스 실증으로 넓어집니다.

기준 2026-08-11 PR #25 · 브랜치 docs/tobe-itdaily-e2e-mvp 결정 본체 ADR-0013 + rev 3건(0005·0008·0012) Status: Proposed — 곽팀장 확인 2건 대기

📌 AS-IS 가이드(같은 날 작성)의 짝 문서입니다. 회색 점선 카드가 AS-IS, 초록 카드가 TO-BE입니다. 코드는 아직 0줄 — PR #25는 문서(설계·계획)만 바꿉니다.

§1

한눈 비교 — 무엇이 바뀌나

6개 축에서 갈립니다. 왼쪽(AS-IS)은 어제까지의 계획, 오른쪽(TO-BE)이 새 기준입니다.

완료 기준 · AS-IS

배관 검증 — 수집 원문이 매거진 raw_items에 도달하면 파일럿 완료. AC 7술어, 발행 술어 0개

완료 기준 · TO-BE

서비스 실증 — 재구성·검수·발행·REST 제공·파트너 어드민까지. AC 12술어 (배관 7 승계 + 발행 5 신설)

MVP 산출물 · AS-IS

아이티데일리 원문뿐 — GCS 원본 HTML · engine.articles · raw_items 사본. 재구성 기사 0건

MVP 산출물 · TO-BE

원문 + 발행된 재구성 기사 — Cloud SQL 행 + GCS 발행 스냅샷 + /v1/published 응답

LLM · AS-IS

MVP 범위 안에 LLM 호출 0건 — 재구성은 "이후 운영"

LLM · TO-BE

재구성(LLM)이 MVP 안 — 단, 위치는 그대로 매거진 계층. 엔진 코어 LLM 0은 불변(검사기 강제)

DB · AS-IS

인스턴스 2개 계획 — 엔진(서울, 실물) + 매거진(도쿄, 미생성)

DB · TO-BE

서울 인스턴스 1개 + DB 2개postgres(engine)·magazine(신설). 매거진 코드 무수정(DSN 교체뿐)

서빙 · AS-IS

표면 2개로 분리 — 엔진 Pull API(MVP 안) / 매거진 서빙 API는 web/에 별도(M7, 미착수·MVP 밖)

서빙 · TO-BE

apps/api 하나로 통합/v1/articles(원문) + /v1/published(발행 기사). web/은 휴면

파트너·검수 · AS-IS

파트너 접수구는 구글 폼 수준(M4) · 검수는 SQL 콘솔(전이 규칙을 우회하는 raw UPDATE)

파트너·검수 · TO-BE

계정 로그인형 파트너 어드민(삭제 즉시 반영·정정 접수) + 내부 검수·발행 UI(transitions.py 경유 — 우회 갭 해소)

§2

완료 기준 — AC 7술어 → 12술어

배관 7개는 문면 그대로 승계됩니다(설계 근거 3가지도 유효). 발행 5개가 새로 붙습니다. 문면의 단일 출처는 ADR-0012 rev4 = ADR-0013이고, CLAUDE.md와 바이트 동일하게 유지됩니다.

승계 itdaily-kr.yamlload_source_configs() 검증 통과 — contract_note 실재 = 계약 반입의 기계적 증명
승계 engine.sources에서 slug='itdaily'enabled=true
승계 Scheduler 실트리거 1회 → GCS 객체 1개 + engine.articles 행 1건
승계 takedown 접수 → suppression 행 + Pull API 미반환(전/후 diff)
승계 어댑터 1회 → raw_items 행 1건 + suppression 시 매거진 기사 unpublished 전이
승계 셀렉터 오염 강제 → parse_status='empty' 행 1건 + 알림 발화 — 문면 정정: 구 문면 'failed'는 현재 코드로 재현 불가(파서는 empty를 냄, PR #24 의미 확정 반영)
승계 backfill 1회 → 같은 행의 body_hash 변경
신설 어댑터 적재분에서 재구성 기사 1건 생성(writer_mode='reconstruct') → in_review 도달
신설 admin-web 검수 화면에서 승인 → published 전이 (transitions.py 경유 — 불변 원칙 4)
신설 발행 시 GCS 발행 스냅샷 객체 1개 (버킷 vertical-news-published, body_md + 메타 JSON)
신설 GET /v1/published가 해당 기사 반환 — publisher·source_url·ai_notice 3필드 포함
신설 파트너 로그인 → 자사 기사 삭제 → suppression + unpublished + 양 API 모두 미반환 · 정정 요청 → 접수 행 + 재파싱·재구성 후 in_review 재진입
검수 게이트는 완화가 아니라 강화

발행이 MVP에 들어와도 approved 없이 published 불가(불변 원칙 4)는 그대로입니다. 달라지는 것은 수단 — 지금 검수는 SQL 콘솔의 raw UPDATE라 전이 규칙을 우회하는데, ⑨의 검수 UI는 transitions.py를 경유해 그 알려진 갭을 닫습니다.

§3

TO-BE E2E 워크플로우 — 경계선이 내려간다

AS-IS 가이드의 그 그림에서 달라진 것은 하나입니다 — MVP 경계선의 위치. 구 경계(회색 유령선)는 어댑터 뒤에 있었고, 새 경계는 파트너 어드민까지 내려갑니다.

엔진 트랙 asia-northeast3 서울 · LLM 0 (불변)
트리거 Cloud Scheduler · fetch-feed-itdaily

OIDC로 worker를 깨움 — AS-IS와 동일.

②③
RSS 분해 → 큐 배급 Cloud Run Service · worker Cloud Tasks · engine-normal

기사 1건 = 태스크 1건, 1 req/s — AS-IS와 동일.

GCS 원본 저장(파싱 전) → 파싱 → 적재 Cloud Storage · vertical-news-engine-raw Cloud SQL · postgres/engine

AS-IS와 동일 — 규칙 기반 파싱, 모델 호출 없음.

⑤⑥
Pull API → 어댑터 Cloud Run Service · api Cloud SQL · magazine DB

/v1/articles를 당겨 raw_items에 적재. 이제 같은 인스턴스 안에서의 이동이다(§4).

TO-BE: 여기부터도 MVP 매거진 트랙 서울 (도쿄 아님 — ADR-0005 rev)
재구성 — 모델 배선 Cloud Run Job · pipeline-worker LiteLLM → Gemini

30분 사이클이 raw_items를 집어 KR+contract 라우팅 → reconstruct/(PR #23 품질) → in_review. 코드 0줄 재사용 — 어댑터가 넣으면 기존 사이클이 그대로 돈다.

검수 — 사람 승인 NEW UI Cloud Run Service · admin-web

in_review 큐 → 본문·verify_detail·batch_stats 확인 → 승인/반려. SQL 콘솔 대신 transitions.py 경유.

발행 = 전이 + 스냅샷 NEW Cloud Storage · vertical-news-published

published 전이(publish.py 첫 실호출) + 발행본 JSON 스냅샷 — 불변 증빙·서빙 캐시 원본. ISR revalidate는 대상이 없어 스냅샷+API가 대체.

외부 제공 NEW Cloud Run Service · api — GET /v1/published

비즈크러시 등 소비 서비스가 커서로 당겨감. publisher·source_url·ai_notice 3필드 포함.

파트너 어드민 NEW Cloud Run Service · admin-web

제휴 언론사 로그인 → 자사 기사 삭제(즉시 suppress+unpublish)·정정 요청. 기존 takedown 배관의 첫 실호출자.

§4

인프라 — 2리전 계획에서 서울 단일로

도쿄 인스턴스는 실물이 만들어진 적이 없습니다 — 제거는 지우는 작업이 아니라 계획에서 빼는 것입니다.

AS-IS 계획 — 인스턴스 2개 · 리전 2개

vertical-news-engine (서울 · 실물)
postgres → 스키마 engine — 수집 원문·suppression
매거진 Cloud SQL (도쿄 · 미생성)계획에서 제거
raw_items·articles… — 만들어진 적 없음 (deploy.md 매거진 트랙 0/6)

어댑터가 리전을 건너 써야 했고, "문서 도쿄 vs 실물 서울" 미결이 걸려 있었다

TO-BE — 인스턴스 1개 · DB 2개 (서울)

vertical-news-engine (서울 · 실물 그대로)
postgres → 스키마 engine — 현행 무변경
magazine NEW — 매거진 테이블 전부 (public 스키마 그대로)

매거진 코드는 스키마 접두 없이 짜여 있어 코드 무수정 — DATABASE_URL 교체뿐. 격리는 DB 단위 계정·GRANT로 유지(킥오프 #1을 인스턴스→DB 분리로 완화, ADR-0008 rev4)

🔴 판단이 갈렸던 지점 — mag 스키마가 아니라 별 DB인 이유

같은 인스턴스에 얹는 방법이 둘이었습니다. mag 스키마 방식은 search_path 설정 또는 전 쿼리 수정이 필요했고, 별 DB 방식은 코드 0줄입니다. 매거진 워커 Job이 이미 서울에 배포돼 있어(PAUSED) 리전 정합도 이것으로 확정됩니다.

§5

서빙·어드민 — 금지 하나를 해제하고, 폼을 어드민으로 승격

"엔진 api에 매거진 라우터 금지"는 DB 물리 분리가 전제였던 규칙입니다 — 전제가 §4로 사라져 함께 개정됐습니다(ADR-0008 rev4).

apps/api — FastAPI 하나, 표면 둘

엔드포인트데이터DB소비자
GET /v1/articles수집 원문 (M2 설계 승계)postgres/engine어댑터 (내부)
GET /v1/published NEW재구성 발행 기사 + 3필드magazine (published_articles 뷰)비즈크러시 등 외부 서비스
admin 라우트 NEW검수·발행·파트너 인증·정정 접수둘 다 (라우트별 DSN 분리)admin-web (DB 직결 금지)

apps/admin-web — 화면 6개, 사용자군 2개

화면사용자기능
/login공통이메일+비밀번호(bcrypt) · 세션 쿠키 · engine.partner_accounts(소스 스코프)
/my/articles파트너자사 기사 목록 — 수집 원문 + 발행 기사, 스코프 강제
/my/articles/[id]파트너삭제 = 즉시 takedown→suppress→unpublish · 정정 = 접수→재파싱+재구성→재검수
/my/requests파트너요청 이력·처리 상태
/review내부in_review 큐 → 승인/반려
/review/[id]내부발행 버튼 · unpublish (전부 transitions.py 경유)
새로 만드는 배관이 거의 없다

삭제 경로(takedown_requests → article_suppressions → unpublished 전이)는 DDL·전이 함수까지 전부 구현돼 있고 호출자만 0이었습니다. 파트너 어드민의 삭제 버튼과 검수 UI의 발행 버튼이 unpublish_article·publish_and_revalidate의 첫 실호출자가 됩니다.

§6

마일스톤 — M 체계에서 T0~T6으로

260811 계획서의 M0~M6을 승계·재편했습니다 (M0·M1→T0·T1 / M2→T2 / M3→T3 / M6→T4·T6 / M4·M5→T5). T1·T2·T3은 병행 가능하고, T2는 테크월드(epnc) 시드로 T0 완료 전에도 착수할 수 있습니다.

T0재민
사람 작업 — 전체 블로커

UA 사전 공유 메일(🔴 유일한 전역 블로커) → robots.txt → M0 셀렉터 실측 → 임베딩 계약 판단 → 파트너 계정 정책. 코드가 아니라서 재설계로도 없어지지 않는다.

T1정훈
DB 통합 T2·T3과 병행

magazine DB 신설 + 계정 2종 → 마이그레이션 0001~0017 적용 → 시드. 게이트: DSN만 바꾸고 매거진 pytest 전량 그린(코드 무수정 증명).

T2정훈·어진
worker + api epnc 시드로 선착수 가능

M2 지시서 §3~§5 전량 승계 + /v1/published·admin 라우트 추가. 게이트: 로컬 e2e — published 반환→unpublish 시 소거까지.

T3정훈
인프라 — Terraform

Cloud Run Service 3 + Job 2(기존 pipeline-worker는 import) + Scheduler 2 + vertical-news-published 버킷 + CI 2레인. 게이트: plan drift 0 → 승인 apply.

T4어진
어댑터 + 발행 배선

Pull 폴링→raw_items(게이트 seam 경유) · suppressions 역추적→unpublish · 발행 버튼→publish.py+GCS 스냅샷. 재구성 자체는 코드 0줄.

T5어진·정훈
파트너 어드민 + 검수 UI

화면 6개 + 계약 이행 6건 체크리스트(발행이 MVP에 들어와 함께 크리티컬 패스가 됐다). 게이트: 스코프 침범 403 · 미승인 발행 거부.

T6전원
개통 데모 = 파일럿 완료

AC 12술어 스테이징 1회 실증 + 되돌림 확인 → docs/audit/ 기록. 곽팀장 참관.

§7

바뀌지 않는 것 · 대기 중인 것

불변
불변 원칙 4개 전부

원문 차단 모듈 경계(재구성은 원래 계약 경로) · ND 차단 · 팩트 무결성 · 검수 게이트. 엔진 코어 LLM 0도 검사기 그대로.

BLOCKER
T0 사람 작업 — 재설계로 없어지지 않는다

UA 사전 공유 메일이 여전히 모든 네트워크 요청을 막고 있고, M0 실측 없는 시드는 침묵 실패(부록 A 그대로 유효)다.

승인 대기
ADR-0013 Status: Proposed — 곽팀장 확인 2건

① 파트너 어드민 공개(대외 표면) ② 킥오프 #1 개정(인스턴스→DB 분리). 확인 즉시 Accepted 전환.

선행
PR #24 머지

apps/ 골격·가드·UA 정정과 M2 지시서가 그 브랜치에 있다 — T2의 전제.

유지
JP 보류 · digest 게이트 · 테크월드 2호

JP 트랙은 T6까지 보류(테스트 그린·langpack/ja 동결 유지) · digest 발행은 인공지능신문 계약 대기(ADR-0009) · 테크월드는 enabled=false로 유지.

새로 커진 리스크 — 파트너 어드민은 대외 표면

세션·비밀번호·소스 스코프 강제가 데모 수준이면 안 됩니다. T5 게이트에 스코프 침범 403 테스트가 명시된 이유이고, 계약 이행 의무 6건(출처 블록·표시 시안·성과 공유 계측 등)도 발행과 함께 MVP 크리티컬 패스로 들어왔습니다.