<PonsLink/>
작업
글
주제
시리즈
카테고리
연락
⌘K 검색
About
코드와 제품으로

코드와 제품으로

연결되는 세계

연결되는 세계

PonsLink는 만남 뒤 요청을 세션으로 잇고, PonsWarp는 서버 보관 없이 파일을 직접 전송합니다. 이 블로그는 기능 소개보다 설계 판단과 운영 증거를 남깁니다.

연락처

[email protected]github.com/DeclanJeon

운영

콘텐츠는 WordPress headless CMS, 공개 UI는 Next.js입니다.

초안 미리보기
© 2026 PonsLink Blog|Next.js · WordPress REST · Framer Motion
Local-first · Backup-before-deploy

[Blueprint L2] PonsLink Control Tower — Threads에 dry-run으로 쏘는 이유

Declan·2026년 8월 28일

  • Blueprint
  • P2P/분산기술
  • 개발 회고

Python Threads 컨트롤타워. cardnews 재사용→생성 폴백, n8n 패치, dry-run 기본 게이트로 안전하게 쏘는 자동발행.

# [Blueprint L2] PonsLink Control Tower — Threads에 dry-run으로 쏘는 이유 > Series: `blueprint-ponslink` (2/3) · Category: `P2P/분산기술` + `개발 회고` · Slug: `blueprint-ponslink-07-control-tower-threads` > Source: `blueprints/specs/P02_ponslink_web.md` §07 + `blueprints/07-ponslink-sns-control-tower.png` + `ponslink-sns-control-tower/` — ## 0. TL;DR `control-tower`는 `Python`으로 만든 `Threads` 자동발행 컨트롤타워. `cardnews`가 있으면 재사용, 없으면 생성 폴백으로 돌리고, `n8n` 워크플로를 패치한 뒤 `Threads`에 쏜다. 기본은 `dry-run`. 실제 발행은 스위치를 켜야만 간다. 컨트롤 플레인을 분리해, 발행 실패가 `cardnews` 생성까지 오염시키지 않게 했다.
PonsLink Control Tower 설계도 07
— ## 1. 문제 정의 — 왜 컨트롤타워가 필요한가 `card_news` 파이프라인은 `마크다운→Hermes→storyboard→imagen→sharp`로 8단계다. 그러나 `Threads`에 올릴 때 매번 새로 생성하면 비용·시간·품질이 흔들린다. 이미 만든 `cardnews`가 있으면 재사용하는 게 낫다. 그리고 `n8n` 워크플로를 직접 고치면, `Threads API` 변경에 취약하다. 그래서 `control-tower`가 `재사용/생성`을 결정하고, `n8n`은 `패치`만 한다. — ## 2. 설계도 한눈에 — 결정 다이아몬드와 dry-run 게이트 중앙 `HTTP API+UI` → `cardnews resolve` 다이아몬드 → 두 갈래: `재사용` 실선 / `생성 폴백` 점선(`card_news` 호출) → `n8n 워크플로 패치/트리거` → `Threads 발행 어댑터` → `Threads SNS`. * **결정:** `cardnews`가 `DB`에 있으면 `재사용`, 없으면 `생성`. `card_news`의 `Prisma/SQLite`를 조회. * **게이트:** 기본 경로는 `dry-run` — `Threads`에 안 쏘고 `n8n`에 `dry-run` 플래그만 전달. 실제 발행은 `?dryRun=false`를 명시해야 한다. * **어댑터:** `Threads` API 변경 시 `어댑터`만 교체. `control-tower`는 `n8n`에 `워크플로 JSON`을 패치하는 역할만. — ## 3. 아키텍처 해부 — 3개 컴포넌트 “` control-tower/ ├── api.py # HTTP API + UI ├── resolver.py # cardnews 재사용/생성 결정 └── n8n_client.py # 워크플로 패치/트리거 “` * **api.py:** `FastAPI`로 `POST /publish` — `dryRun` 기본 `true`. `UI`는 `dry-run` 결과를 먼저 보여준다. * **resolver.py:** `cardnews`의 `job`을 조회. `status=completed`이고 `7일 이내`면 재사용. 아니면 `card_news`에 `POST /api/pipeline/generate`로 생성 요청. * **n8n_client.py:** `n8n`의 `workflowId`를 `GET`으로 읽고, `nodes` 중 `Threads` 노드의 `content`를 패치한 뒤 `POST /workflows/{id}/trigger`로 실행. — ## 4. 로직 딥다이브 — 2개만 깊게 ### 4.1 재사용/생성 폴백 결정 “`python job = prisma.job.find_first(where={“topic”: topic, “status”: “completed”}, order={“createdAt”: “desc”}) if job and (now – job.createdAt).days < 7: return job # 재사용 else: return await card_news.generate(topic) # 생성 ``` * `7일`은 `cardnews`의 유효기간. 그 이상이면 트렌드가 바뀌어 재생성. ### 4.2 dry-run 기본 게이트 * `control-tower`의 `POST /publish`는 `dryRun=true`가 기본. `n8n`에 `dryRun`을 전달하면, `Threads` 노드는 `API`를 호출하지 않고 `log`만 남긴다. 운영자가 `dry-run` 로그를 보고 `dryRun=false`로 재호출해야 실제 발행. 실수로 `Threads`에 도배되는 걸 막는 안전장치. --- ## 5. 왜 이 선택인가 | 선택 | 채택 | 버린 대안 | 버린 이유 | |---|---|---|---| | 결정 | 재사용→생성 폴백 | 항상 생성 | 비용·시간 낭비, 품질 편차 | | 게이트 | dry-run 기본 | 바로 발행 | 테스트 중 실수 발행, `Threads` 스팸 처리 | | 분리 | 컨트롤 플레인 분리 | `card_news`에 `Threads` 직접 연동 | `Threads` 장애가 `card_news` 생성까지 블로킹 | --- ## 6. 실패와 복구 * **실패:** 처음엔 `control-tower`가 `card_news`의 `DB`를 직접 읽었다. 그러나 `card_news`가 `Prisma` 마이그레이션을 하자 `control-tower`가 깨짐. `card_news`에 `GET /api/jobs` API를 추가해 간접 조회로 변경. * **복구:** `n8n` 워크플로 `ID`가 바뀌면 `control-tower`가 `404`. `workflow`를 `name`으로 조회해 `ID`를 동적으로 찾게 수정. --- ## 7. 운영 증거 * `control-tower` — `Python, FastAPI, n8n, Threads API`, `dry-run` 100% 기본 * `card_news` — `Next.js, Bun pipeline-service, Prisma/SQLite, Caddy`, `Socket.io` 진행률 * `n8n` — `workflow` 패치 후 `trigger`, `Threads` 발행 어댑터 --- ## 8. 다음 편 예고 **L3: `포트폴리오` — 의존성 0 정적 사이트.** `index.html` 단일 파일, `디자인 토큰 SSOT`, `13개 제품 쇼케이스`를 다룬다. 가장 단순한 아키텍처가 왜 가장 오래 가는지 보여준다. *부록: `blueprints/07-ponslink-sns-control-tower.png` 프롬프트 `prompts.part1.json#07`, 코드 진입점 `ponslink-sns-control-tower/api.py`*

Related

관련 운영 노트

같은 주제에서 이어서 읽기 좋은 글입니다.

  • [Blueprint L3] Pons 포트폴리오 — 의존성 0, 단일 index.html이 오래 가는 이유

    의존성 0, 단일 index.html 41KB. 디자인 토큰 SSOT와 13개 제품 쇼케이스로 만든 정적 포트폴리오가 오래 가는 이유.

  • [Blueprint L1] PonsLink P2P Room — WebRTC 화상회의를 BFF로 감싼 이유

    React SPA와 Express BFF가 Socket.IO로 시그널링하고, 미디어는 mesh로 가는 2티어. TURN 그랜트와 presence lease로 룸을 만든 화상회의.

  • [Blueprint W5] PonsWarp Desktop — WarpInfra, 브라우저를 떠난 QUIC 메시

    브라우저를 떠난 QUIC 메시. Tauri2 warp-core, warp-agentd, mDNS, rarest-first로 인트라넷 릴리스 그리드를 만든 인프라.

댓글

아직 댓글이 없습니다.

현재 이 글의 댓글은 닫혀 있습니다.