← 목록으로
2026-02-25plans

title: content-orchestration 파이프라인 설계서 (L1) date: 2026-02-25T17:20:00+09:00 type: design layer: L1 status: in-review task_id: "dc1d0d04-5bca-4d69-abc7-e0b4f3e40c79" tags: [content-orchestration, pipeline-design, L1] author: pipeline-design-pl project: content-orchestration reviewed_by: "jarvis" reviewed_at: "2026-02-25T17:35:00+09:00" approved_by: "" approved_at: ""

content-orchestration 파이프라인 설계서 (L1)

Task ID: dc1d0d04-5bca-4d69-abc7-e0b4f3e40c79 작성일: 2026-02-25 작성자: pipeline-design-pl 근거 문서:

  • L0: content-orchestration-biz-v4.md (비즈 기획서)
  • L1: content-orchestration-design-db.md (DB 설계서, 확정)

1. 현황 분석

1-1. 현재 파이프라인 구조

content-pipeline 프로젝트(projects/content-pipeline/src/pipeline/)에 구현된 파이프라인은 다음 8개 파일로 구성된다:

파일역할실행 방식
collect.tsRSS 17개 소스 수집, 중복 제거, 1단계 필라 키워드 필터링CLI / import
generate.ts뉴스레터 생성 (Gemini Flash), 관련성 점수 기반 뉴스 선별CLI / import
generate-blog.ts블로그 포스트 생성, 5대 필라별 프롬프트, 8항목 QA 검증CLI / import
publish.ts뉴스레터 발행 (Brevo + 블로그 + SNS getlate.dev)CLI / import
publish-blog.ts블로그 단독 발행 (Turso blog_posts 테이블에 INSERT)CLI / import
publish-sns.tsSNS 멀티플랫폼 발행 (getlate.dev API, 플랫폼별 콘텐츠 변환)CLI / import
run.ts뉴스레터 파이프라인 오케스트레이터 (수집 -> 생성 -> 이메일 -> 블로그 -> SNS)CLI
run-blog-pipeline.ts블로그 파이프라인 오케스트레이터 (수집 -> 주제선정 -> 리서치 -> 생성 -> QA -> 게시)CLI

현재 데이터 흐름:

RSS 17개 피드 ─→ collect.ts ─→ collected_news (178건)
                                    │
                    ┌───────────────┤
                    ↓               ↓
              generate.ts     generate-blog.ts
              (뉴스레터)        (블로그 포스트)
                    │               │
                    ↓               ↓
              newsletters       blog_posts
              (content-os DB)   (apppro-kr DB)
                    │               │
          ┌────────┼────────┐      ↓
          ↓        ↓        ↓  publish-blog.ts
    Brevo API  blog_posts  getlate API
    (publish)  (publish)   (publish-sns)

1-2. 현재 파이프라인의 강점

  1. 1단계 경량 필터링 구현: filterByPillar() — 필라 키워드 매칭 + 등급(S/A/B) 기반 통과 판단. AI 토큰 소비 0
  2. 5대 필라 체계: 요일별 콘텐츠 필라 자동 배정 (월~금)
  3. 8항목 QA 검증: 본문 길이, H2 구조, 메타 설명, 제목 길이, 마크다운 유효성, 요약, 카테고리 등
  4. 관련성 점수 기반 뉴스 선별: 비즈니스 키워드 가중치 + 최신성 보너스 + 소스 다양성
  5. SNS 플랫폼별 콘텐츠 변환: 트위터(280자), 링크드인(3000자) 등 플랫폼 특성 반영
  6. 중복 제거: URL 정규화 + 제목 유사도(70% 단어 겹침) 기반 이중 중복 검사
  7. QA 실패 시 재시도: run-blog-pipeline.ts에서 최대 2회 재생성

1-3. 현재 한계점

#한계상세DB 설계서 연계
1파이프라인 실행 기록 없음pipeline_logs 0건. 언제 무엇이 실행됐는지 추적 불가pipeline_logs 확장 (trigger_type, error_log_id)
2콘텐츠 큐 미사용content_queue 0건. 생성→발행이 단일 프로세스로 직렬 실행content_queue 확장 (승인 플로우 5컬럼)
3승인 플로우 부재AI 생성 후 즉시 발행. CEO 검수 단계 없음content_queue.status: draft→reviewing→approved
4채널 하드코딩Brevo, getlate, blog DB 연결 정보가 코드에 내장channels 테이블로 동적 관리
5에러 추적 산재각 함수의 console.error/warn만 존재. 통합 에러 로그 없음error_logs 테이블
6재시도 제한적블로그 QA 실패 시 2회만 재시도. Brevo/SNS는 재시도 없음error_logs.auto_fix_attempted + content_distributions.retry_count
72-Layer 상태 추적 불가내부 DB 상태와 외부 플랫폼 등록 상태가 분리되지 않음content_distributions 테이블
8멀티 프로젝트 미지원apppro 단일 프로젝트 전용. 프로젝트별 설정 분리 없음channels.project, content_queue.project
9content-pipeline과 orchestration 분리파이프라인 코드는 ai-blog 레포, 대시보드는 content-orchestration 레포. 연동 포인트 없음pipeline_logs로 실행 결과 기록

2. 파이프라인 설계 (5단계)

2-0. 전체 아키텍처 개요

┌──────────────────────────────────────────────────────────────────┐
│                     content-orchestration                        │
│                     (대시보드 + API + 스케줄러)                     │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Stage 1         Stage 2         Stage 3        Stage 4         │
│  ┌──────┐       ┌──────┐       ┌──────┐       ┌──────┐        │
│  │ 수집  │──→──│ 생성  │──→──│ 승인  │──→──│ 배포  │        │
│  │Collect│      │Generate│      │Approve│      │Publish│        │
│  └──┬───┘       └──┬───┘       └──┬───┘       └──┬───┘        │
│     │              │              │              │              │
│     ↓              ↓              ↓              ↓              │
│  collected      content_queue  content_queue  content_          │
│  _news          (draft)        (approved)     distributions    │
│                                                                  │
│  ────────────── Stage 5: 결과 추적 ──────────────────────────    │
│  pipeline_logs  │  error_logs  │  optimization_history          │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

핵심 설계 원칙:

  • 큐 기반 비동기: 각 단계는 DB 상태를 통해 연결. 단계 간 직접 호출 최소화
  • 멱등성: 동일 입력에 대해 중복 처리 방지 (URL 정규화, slug 중복 체크)
  • 관찰가능성: 모든 실행은 pipeline_logs에 기록, 에러는 error_logs에 수집
  • 점진적 확장: Phase 1은 기존 코드 재활용, Phase 2~3에서 고도화

2-1. Stage 1: RSS 수집 (Collect)

목적: 외부 소스에서 뉴스를 수집하여 collected_news 테이블에 저장한다.

입력

항목상세
소스RSS 17개 피드 (collect.ts의 RSS_FEEDS 배열)
트리거Vercel Cron 스케줄 (매일 06:00 KST) 또는 수동 API 호출
설정 참조현재: 코드 하드코딩 / Phase 2: channels 테이블의 RSS 소스 설정

처리 로직

1. RSS 피드 순회 파싱 (rss-parser, timeout 10초)
   │
2. 피드별 최신 10건 추출
   │
3. 중복 제거 (2단계)
   ├─ URL 정규화 (utm 파라미터 제거, trailing slash 제거)
   └─ 제목 유사도 검사 (70% 단어 겹침 = 중복)
   │
4. 1단계 경량 필터링 (토큰 소비 0)
   ├─ 광고성/무관 키워드 즉시 제거
   ├─ S등급: 무조건 통과
   ├─ A등급: 필라 키워드 1개 이상 매칭 시 통과
   └─ B등급: 필라 키워드 2개 이상 매칭 시만 통과
   │
5. collected_news INSERT (ON CONFLICT url DO NOTHING)

출력

항목테이블상세
수집 기사collected_news중복 제거 + 필터링 통과한 기사. lang/grade/category 포함
실행 로그pipeline_logspipeline_name='collect', status, duration_ms, items_processed, metadata(feeds_ok/feeds_fail)
에러 로그error_logs피드 수집 실패 시: component='rss_collector', error_type='timeout'/'api_error'

에러 처리

에러 유형현재 처리설계 후 처리
피드 파싱 실패 (HTTP 4xx/5xx)console.warn 후 skiperror_logs 기록 + 나머지 피드 계속 처리
피드 timeout (10초)rss-parser 자체 timeouterror_logs 기록 + skip
DB 저장 실패try-catch 무시error_logs 기록 + pipeline_logs에 부분 실패 기록
모든 피드 실패빈 배열 반환pipeline_logs status='failed' + error_logs escalated=1

트리거 조건

트리거조건Phase
스케줄 (Cron)매일 06:00 KSTPhase 1
수동 (API)POST /api/pipeline/collectPhase 1
수동 (대시보드)대시보드 "수집 실행" 버튼Phase 2

2-2. Stage 2: AI 콘텐츠 생성 (Generate)

목적: 수집된 뉴스를 기반으로 AI가 콘텐츠를 생성하여 content_queue에 draft 상태로 저장한다.

입력

항목상세
소스 데이터collected_news에서 used_in_newsletter=0인 미사용 기사
AI 모델Gemini 2.0 Flash (현재) / Phase 2: content_generation_config 테이블 참조
프롬프트필라별 프롬프트 템플릿 (prompts/pillars/*.md)
트리거Stage 1 완료 후 자동 / 수동 API 호출

처리 로직

1. 콘텐츠 유형 결정
   ├─ 블로그: 요일 기반 필라 자동 배정 (getTodayPillar)
   └─ 뉴스레터: 주 1회 (월요일) 주간 브리핑
   │
2. 뉴스 선별 (관련성 점수 기반)
   ├─ 비즈니스 키워드 고가중치 (+3)
   ├─ AI 일반 토픽 중가중치 (+1)
   ├─ 연구/기술 저가중치 (-1)
   ├─ 최신성 보너스 (2일 이내 +3, 5일 이내 +1)
   └─ 소스 다양성 (동일 소스 최대 3건)
   │
3. AI 콘텐츠 생성 (Gemini Flash API)
   ├─ 시스템 프롬프트: BASE_SYSTEM_PROMPT + 필라별 추가 지침
   ├─ 뉴스 컨텍스트: 최근 5건 뉴스 요약
   └─ 출력: JSON (title, slug, content, excerpt, meta_description, category, tags)
   │
4. QA 검증 (8항목)
   ├─ 본문 길이 (1,500~4,000자)
   ├─ H2 헤딩 (최소 3개)
   ├─ AI 디렉토리 링크 (최소 2개, 프로덕션 도메인 설정 시)
   ├─ 메타 설명 (최대 160자)
   ├─ 제목 길이 (최대 45자)
   ├─ 마크다운 유효성 (빈 링크 없음)
   ├─ 요약 길이 (최대 200자)
   └─ 카테고리 유효성 (5대 필라 중 하나)
   │
   ├─ 6/8 이상 통과: PASS → content_queue에 draft INSERT
   └─ 미달: 재생성 (최대 2회)
   │
5. content_queue INSERT (status='draft')
   ├─ title, content_body, type, pillar, topic, project
   └─ collected_news.used_in_newsletter = 1 (사용된 뉴스 마킹)

출력

항목테이블상세
콘텐츠 초안content_queuestatus='draft', type='blog'/'newsletter', content_body에 마크다운/HTML 본문
뉴스레터newsletterscontent_queue_id 연계, HTML+텍스트 본문
실행 로그pipeline_logspipeline_name='generate', items_processed, metadata(pillar, qa_score)
에러 로그error_logsAI 생성 실패: component='ai_generator', error_type='api_error'/'quality_fail'

에러 처리

에러 유형처리
Gemini API 타임아웃/에러1회 재시도 → 실패 시 error_logs 기록 + mock 생성(개발 모드만)
JSON 파싱 실패이스케이프 보정 → 필드별 추출 시도 → 실패 시 error_logs
QA 미달 (6/8 미만)최대 2회 재생성 → 3회 실패 시 error_logs (quality_fail) + 에스컬레이션
API 키 미설정mock 생성 + pipeline_logs metadata에 mock=true 기록

2-3. Stage 3: 승인 플로우 (Approve)

목적: CEO가 콘텐츠를 검수하고 승인/거부한다. 승인된 콘텐츠만 배포 단계로 진행한다.

상태 전이 (content_queue.status)

draft ──→ reviewing ──→ approved ──→ scheduled ──→ published
  │           │
  │           └──→ draft (거부: rejected_reason에 사유 기록)
  │
  └──→ failed (생성 단계 실패)

처리 로직

1. draft → reviewing 전환
   ├─ 트리거: 자동 (AI 생성 완료 즉시) 또는 수동 (대시보드에서 검수 요청)
   └─ 액션: 텔레그램 알림 발송 (CEO에게 검수 요청)
   │
2. CEO 검수
   ├─ 대시보드: /[project]/content/[id] 페이지에서 미리보기 + 승인/거부 버튼
   ├─ 텔레그램: 인라인 버튼으로 간편 승인 (Phase 2)
   └─ 자동 승인 타이머: N시간 무응답 시 자동 approved (Phase 3, 설정 가능)
   │
3. reviewing → approved
   ├─ approved_by: 'ceo' (수동) / 'auto' (자동 승인 타이머)
   ├─ approved_at: 현재 시각 (ms epoch)
   └─ 후속 액션: Stage 4 (배포) 자동 트리거
   │
4. reviewing → draft (거부)
   ├─ rejected_reason: CEO 피드백 텍스트
   └─ 후속: AI 재생성 또는 수동 수정 후 다시 reviewing

입력/출력

항목테이블상세
입력content_queuestatus='draft' 또는 'reviewing'인 콘텐츠
출력content_queuestatus='approved', approved_by, approved_at 업데이트
알림텔레그램reviewing 전환 시 CEO에게 검수 요청 메시지
로그pipeline_logspipeline_name='approve', metadata(approved_by, content_id)

트리거 조건

트리거조건Phase
자동 (생성 완료)Stage 2에서 QA 통과 후 자동 reviewing 전환Phase 1
수동 (대시보드)CEO가 승인/거부 버튼 클릭Phase 1
수동 (API)POST /api/content/[id]/approve 또는 /rejectPhase 1
텔레그램 인라인텔레그램 메시지 내 승인 버튼Phase 2
자동 승인 타이머reviewing 후 N시간 무응답Phase 3

2-4. Stage 4: 채널 배포 (Publish)

목적: 승인된 콘텐츠를 각 채널에 예약 등록하고, 발행 상태를 추적한다.

2-Layer 상태 관리

내부 상태 (content_queue.status)      외부 상태 (content_distributions.platform_status)
─────────────────────────────         ───────────────────────────────────────────────
approved                              → pending (배포 대상 등록)
                                      → registered (외부 플랫폼에 예약 등록 완료)
scheduled (모든 채널 등록 완료)         → published (실제 발행 완료)
published (모든 채널 발행 완료)         → failed (등록/발행 실패)

처리 로직

1. 배포 대상 채널 결정
   ├─ channels 테이블에서 is_active=1인 채널 조회
   ├─ 콘텐츠 유형(blog/newsletter/sns)에 맞는 채널 필터링
   └─ content_distributions에 채널별 레코드 INSERT (platform_status='pending')
   │
2. 채널별 예약 등록
   │
   ├─ 블로그 (apppro.kr)
   │   ├─ apppro-kr Turso DB의 blog_posts에 INSERT (published=0, scheduled_at 설정)
   │   ├─ 기존 Vercel Cron (/api/cron/publish, 6h 주기)이 scheduled_at 도래 시 published=1 전환
   │   └─ content_distributions UPDATE: platform_status='registered', platform_id=post_id
   │
   ├─ Brevo 뉴스레터
   │   ├─ Brevo API: 캠페인 생성 + scheduledAt 파라미터로 예약 발송
   │   └─ content_distributions UPDATE: platform_status='registered', platform_id=campaign_id
   │
   └─ SNS (getlate.dev)
       ├─ Late API: 플랫폼별 콘텐츠 변환 → 예약 포스트 등록
       │   ├─ Twitter/X: 280자 제한 (제목 + URL + 해시태그)
       │   ├─ LinkedIn: 3000자 (제목 + 요약 + URL + 해시태그)
       │   └─ Threads: 500자 (제목 + 요약 + URL)
       └─ content_distributions UPDATE: platform_status='registered', platform_id=post_id
   │
3. 상태 갱신
   ├─ 모든 채널 registered → content_queue.status = 'scheduled'
   └─ 모든 채널 published → content_queue.status = 'published'

채널별 발행 메커니즘

채널API/방법예약 발행Phase
블로그 (apppro.kr)Turso DB INSERT + Vercel Cronscheduled_at 필드 기반, 기존 Cron이 자동 전환Phase 1
Brevo 뉴스레터@getbrevo/brevo SDK, sendCampaign()scheduledAt 파라미터Phase 1
Twitter/Xgetlate.dev API /postsscheduledFor 파라미터Phase 1
LinkedIngetlate.dev API /postsscheduledFor 파라미터Phase 1~2
Threadsgetlate.dev API /postsscheduledFor 파라미터Phase 2
티스토리Tistory Open API (OAuth)직접 예약 발행Phase 3
미디엄Medium API (POST /users/{id}/posts)publishStatus='draft'로 등록Phase 3

입력/출력

항목테이블상세
입력content_queuestatus='approved'인 콘텐츠
입력channelsis_active=1인 활성 채널 목록
출력content_distributions채널별 배포 레코드 (platform_status, platform_id, platform_url)
출력content_queuestatus → 'scheduled' → 'published' 순차 전환
출력content_logs발행 완료 시 불변 이벤트 기록 (append-only)
로그pipeline_logspipeline_name='publish', metadata(channels_ok/channels_fail)
에러error_logs등록/발행 실패: component='publisher'/'brevo'/'sns_publisher'

에러 처리

에러 유형처리
블로그 DB 저장 실패1회 재시도 → content_distributions.platform_status='failed' + error_logs
Brevo API 인증 실패토큰 갱신 시도 → 재발송 → 실패 시 error_logs (escalated=1, CEO 갱신 필요)
Brevo rate limit발송 시간 변경(피크 회피) → 재시도
getlate API 에러1회 재시도 → 실패 시 error_logs + 해당 플랫폼만 skip
getlate 계정 미연결error_logs (error_type='auth_fail') + 나머지 플랫폼 계속
일부 채널만 실패성공 채널은 registered, 실패 채널은 failed. content_queue는 부분 상태 유지

2-5. Stage 5: 결과 추적 (Track)

목적: 파이프라인 실행 이력, 에러 로그, 최적화 파라미터 변경을 기록하고 대시보드에 표시한다.

기록 대상

테이블기록 시점기록 내용
pipeline_logs각 단계(collect/generate/approve/publish) 시작/완료 시pipeline_name, status, duration_ms, items_processed, trigger_type, metadata
error_logs에러 발생 시 (모든 단계)component, error_type, error_message, auto_fix 시도/결과, escalated
content_logs콘텐츠 발행 완료 시content_type, content_id, platform, status, published_at (불변 감사 로그)
content_distributions배포 상태 변경 시platform_status UPDATE (pending→registered→published/failed)
optimization_historyAI 파라미터 변경 시 (Phase 2+)parameter, old_value, new_value, reason, impact

pipeline_logs 기록 상세

각 파이프라인 단계 실행 시 아래 형식으로 기록한다:

시작 시: INSERT pipeline_logs (pipeline_name, status='started', trigger_type, created_at)
완료 시: UPDATE pipeline_logs SET status='completed', duration_ms, items_processed, metadata
실패 시: UPDATE pipeline_logs SET status='failed', error_message, error_log_id

metadata JSON 구조 (단계별):

단계metadata 예시
collect{"feeds_ok":15, "feeds_fail":2, "raw_count":120, "dedup_count":95, "filter_pass":78}
generate{"pillar":"AI도구리뷰", "qa_score":7, "content_type":"blog", "model":"gemini-2.0-flash"}
approve{"approved_by":"ceo", "content_id":"cq-001", "wait_hours":2.5}
publish{"channels_ok":3, "channels_fail":0, "channels":["apppro-blog","brevo","twitter"]}

error_logs 기록 상세

에러 발생 시: INSERT error_logs (
  component,         -- rss_collector / ai_generator / publisher / qa_checker / brevo / sns_publisher
  error_type,        -- timeout / auth_fail / quality_fail / api_error / rate_limit / validation_fail
  error_message,     -- 원본 에러 메시지
  content_id,        -- 관련 콘텐츠 ID (nullable)
  channel_id,        -- 관련 채널 ID (nullable)
  auto_fix_attempted -- 0/1
)

자동 교정 시도 후: UPDATE error_logs SET
  auto_fix_attempted = 1,
  auto_fix_result = 'success'/'failed'/'skipped',
  auto_fix_action = '교정 액션 설명'

해결 시: UPDATE error_logs SET
  resolved_at = 현재시각,
  resolution_type = 'auto_fixed'/'manual_fixed'/'ignored'

에스컬레이션 시: UPDATE error_logs SET escalated = 1

3. 자체교정(Self-Healing) 연계

3-1. 교정 등급별 설계

기획서 7-4의 자체교정 등급(Autonomy Level)을 파이프라인 단계별로 매핑한다.

등급교정 행동파이프라인 적용Phase
L1: 자동 재시도동일 작업 재실행RSS 수집 재시도, API 호출 재시도, DB 저장 재시도Phase 1
L2: 파라미터 조정설정값 변경 후 재실행QA 미달 시 프롬프트 온도 조정, 발행 시간 변경Phase 2
L3: 대체 경로fallback 우회Gemini 실패 시 Claude 전환, RSS URL 자동 교체Phase 2
L4: 구조 변경코드/설정 수정RSS 파서 로직 수정, 새 API 연동Phase 3
L5: 전략 변경콘텐츠 전략 수정토픽 필라 변경, 채널 추가/삭제CEO 승인 필수

3-2. 단계별 자체교정 시나리오

Stage 1 (수집) 교정

에러감지L1 재시도L2 조정L3 대체에스컬레이션
RSS 피드 HTTP 4xx/5xxfetch 에러 코드30초 후 1회 재시도대체 URL 탐색3회 연속 실패 시
RSS 피드 timeout10초 초과timeout 15초로 확장 후 재시도연속 5회 실패 시
파싱 에러 (XML 깨짐)rss-parser 예외content_snippet만 추출 시도즉시 error_logs

Stage 2 (생성) 교정

에러감지L1 재시도L2 조정L3 대체에스컬레이션
AI API 타임아웃fetch timeout1회 재시도2회 연속 실패 시
AI API 인증 실패HTTP 401/403즉시 (CEO 키 갱신 필요)
QA 미달 (score < 6/8)validateQuality최대 2회 재생성프롬프트 온도 0.7→0.53회 재생성 후에도 미달 시
JSON 파싱 실패JSON.parse 예외이스케이프 보정 → 필드별 추출모든 파싱 방법 실패 시

Stage 4 (배포) 교정

에러감지L1 재시도L2 조정L3 대체에스컬레이션
블로그 DB INSERT 실패SQL 예외1회 재시도2회 실패 시
Brevo API 인증 만료HTTP 401즉시 (CEO 토큰 갱신)
Brevo rate limitHTTP 42960초 대기 후 재시도발송 시간 변경3회 재시도 실패
getlate 계정 미연결'NO_ACCOUNTS'해당 플랫폼 skiperror_logs 기록
slug 중복 (블로그)ON CONFLICTslug에 날짜/순번 자동 추가

3-3. error_logs → 자동 진단 → 교정 플로우

에러 발생
    │
    ↓
error_logs INSERT (component, error_type, error_message)
    │
    ↓
교정 등급 판단 (L1~L5)
    │
    ├─ L1~L3 (자동 교정 가능)
    │   ├─ auto_fix_attempted = 1
    │   ├─ 교정 실행
    │   ├─ auto_fix_result = 'success'/'failed'
    │   └─ 성공 시: resolved_at 기록, resolution_type = 'auto_fixed'
    │
    └─ L4~L5 또는 자동 교정 실패
        ├─ escalated = 1
        ├─ 텔레그램 알림 (자비스/CEO)
        └─ resolution_type = 'manual_fixed' (수동 해결 후)

4. Phase별 구현 범위

4-1. Phase 1 MVP (1주): 스케줄 수집 → AI 생성 → 수동 승인 → 블로그 발행

목표: "대시보드에서 CEO가 콘텐츠를 승인하면 실제 블로그에 발행되는 흐름 1건 이상 동작"

구현 범위:

단계구현 내용기존 코드 활용
Stage 1Vercel Cron 트리거 → collect.ts 실행 → collected_news 저장 + pipeline_logs 기록collect.ts 그대로, pipeline_logs INSERT 추가
Stage 2collect 완료 후 → generate-blog.ts 실행 → content_queue(draft) 저장generate-blog.ts 리팩토링: 결과를 content_queue에 저장
Stage 3draft → reviewing 자동 전환 + 대시보드 승인/거부 API신규: /api/content/[id]/approve, /api/content/[id]/reject
Stage 4approved → 블로그 DB INSERT → content_distributions 기록publish-blog.ts 리팩토링: content_distributions 연계
Stage 5pipeline_logs 기록 (모든 단계), error_logs 기록 (에러 시)신규: 로깅 유틸리티
자체교정L1 자동 재시도만 (RSS 재시도, API 재시도, QA 재생성)기존 재시도 로직 유지 + error_logs 기록 추가

Phase 1에서 하지 않는 것:

  • Brevo 뉴스레터 자동 발송 (수동으로 Brevo 대시보드에서 발행)
  • SNS 자동 배포 (수동으로 getlate 대시보드에서 발행)
  • 텔레그램 알림 연동
  • 자동 승인 타이머
  • 멀티 프로젝트 (apppro만)

4-2. Phase 2 (2주): 다중 채널 배포 + 알림 + 자동 교정

구현 범위:

기능상세
Brevo 자동 발송approved → Brevo API 캠페인 생성 + 예약 → content_distributions
SNS 자동 배포approved → getlate API 예약 포스트 → content_distributions
텔레그램 알림reviewing 전환 시 CEO에게 검수 요청 자동 알림
AI 파라미터 관리content_generation_config 테이블 활용, 대시보드에서 설정 변경
L2 파라미터 조정QA 미달 시 프롬프트 온도/길이 자동 조정
L3 대체 경로AI 모델 fallback, RSS URL 자동 교체
성과 수집 (D+3)발행 후 3일 뒤 조회수/오픈율 수집 → content_distributions.metrics
pipeline_logs 확장error_log_id, trigger_type, parent_log_id 컬럼 활용
optimization_history파라미터 변경 이력 기록
프로젝트별 RSS 분리channels 테이블 기반 프로젝트별 소스 관리

4-3. Phase 3 (3주+): 자체교정 고도화 + 성과 기반 최적화

구현 범위:

기능상세
자동 승인 타이머reviewing 후 N시간 무응답 시 자동 approved
L4 구조 변경RSS 파서 로직 자동 수정, 새 API 자동 연동 (자비스 검수 후)
성과 기반 자동 최적화발행 시간, 제목 패턴, 채널 배분 자동 조정 → optimization_history 기록
외부 플랫폼 연동티스토리(Open API), 미디엄(API), EO(API 확인 후)
DAG 시각화파이프라인 전 과정을 대시보드에서 시각적으로 표시
AI 콘텐츠 자동 트리거큐 기반 자동 실행 (content_queue.status = 'pending' 감지 → 생성 시작)
크로스포스팅1개 콘텐츠 → 블로그 + 뉴스레터 + SNS 동시 배포
자체교정 정확도 추적자동 교정 성공률 대시보드 표시 (목표: 80%+)

5. 파이프라인 API 엔드포인트 (Phase 1)

content-orchestration Next.js App Router에 구현할 API 라우트:

메서드경로용도Phase
POST/api/pipeline/collect수동 RSS 수집 트리거1
POST/api/pipeline/generate수동 콘텐츠 생성 트리거1
POST/api/content/[id]/approve콘텐츠 승인1
POST/api/content/[id]/reject콘텐츠 거부 (body: { reason })1
POST/api/pipeline/publish/[id]수동 배포 트리거 (승인된 콘텐츠)1
GET/api/pipeline/logs파이프라인 실행 로그 조회1
GET/api/pipeline/errors에러 로그 조회 (미해결 우선)1
GET/api/cron/pipelineVercel Cron: 전체 파이프라인 자동 실행1

Cron 스케줄 설계:

// vercel.json
{
  "crons": [
    {
      "path": "/api/cron/pipeline",
      "schedule": "0 21 * * 1-5"
    }
  ]
}
  • 0 21 * * 1-5 = UTC 21:00 = KST 06:00, 월~금
  • Cron 핸들러가 Stage 1(수집) → Stage 2(생성) 순차 실행
  • Stage 3(승인)은 CEO 액션 대기, Stage 4(배포)는 승인 후 자동

6. 데이터 흐름 종합 (DB 테이블 연계)

┌──────────────────┐
│  RSS 17개 피드     │
└────────┬─────────┘
         │ Stage 1: collect
         ↓
┌──────────────────┐     ┌──────────────────┐
│  collected_news   │     │  pipeline_logs    │
│  (178+ 건)        │     │  name='collect'   │
└────────┬─────────┘     └──────────────────┘
         │ Stage 2: generate
         ↓
┌──────────────────┐     ┌──────────────────────────────┐
│  content_queue    │     │  content_generation_config    │
│  status='draft'   │────→│  (AI 모델/프롬프트/QA 설정)    │
│  + title          │     └──────────────────────────────┘
│  + content_body   │
│  + type/pillar    │
└────────┬─────────┘
         │ Stage 3: approve
         ↓
┌──────────────────┐     ┌──────────────────┐
│  content_queue    │     │  텔레그램 알림     │
│  status='approved'│     │  (Phase 2)        │
│  + approved_by/at │     └──────────────────┘
└────────┬─────────┘
         │ Stage 4: publish
         ↓
┌──────────────────┐     ┌──────────────────┐
│  content_         │────→│  channels         │
│  distributions    │     │  (활성 채널 목록)   │
│  platform_status  │     └──────────────────┘
│  platform_id/url  │
│  scheduled_at     │     ┌──────────────────┐
│  published_at     │     │  content_logs     │
└────────┬─────────┘     │  (발행 감사 로그)   │
         │               └──────────────────┘
         │ Stage 5: track
         ↓
┌──────────────────┐     ┌──────────────────────────┐
│  pipeline_logs    │     │  optimization_history      │
│  (전 단계 실행 이력)│     │  (파라미터 변경 이력, P2+)  │
└──────────────────┘     └──────────────────────────┘
         │
┌──────────────────┐
│  error_logs       │
│  (에러 + 자동교정)  │
└──────────────────┘

7. 기존 코드 → 신규 파이프라인 전환 전략

7-1. Phase 1 코드 변경 최소화 원칙

기존 content-pipeline 코드를 최대한 재활용하되, content-orchestration 레포에서 호출하는 구조로 전환한다.

기존 코드변경 사항변경 이유
collect.tscollectNews() 결과를 pipeline_logs에 기록하는 래퍼 추가관찰가능성
generate-blog.ts결과를 blog_posts 대신 content_queue에 저장하도록 분기승인 플로우 도입
publish-blog.tscontent_queue에서 읽어 blog_posts에 저장 + content_distributions 기록2-Layer 상태
publish.tssendViaBrevo, publishToSnsViaGetlate에 content_distributions 연계 추가2-Layer 상태
run.ts / run-blog-pipeline.tscontent-orchestration API에서 호출하는 구조로 전환대시보드 통합

7-2. 모듈 구조 (content-orchestration 레포 내)

content-orchestration/
├── src/
│   ├── app/
│   │   ├── api/
│   │   │   ├── pipeline/
│   │   │   │   ├── collect/route.ts      ← Stage 1 트리거
│   │   │   │   ├── generate/route.ts     ← Stage 2 트리거
│   │   │   │   ├── publish/[id]/route.ts ← Stage 4 트리거
│   │   │   │   ├── logs/route.ts         ← 실행 로그 조회
│   │   │   │   └── errors/route.ts       ← 에러 로그 조회
│   │   │   ├── content/
│   │   │   │   └── [id]/
│   │   │   │       ├── approve/route.ts  ← 승인
│   │   │   │       └── reject/route.ts   ← 거부
│   │   │   └── cron/
│   │   │       └── pipeline/route.ts     ← Vercel Cron 핸들러
│   │   └── [project]/
│   │       └── content/
│   │           └── [id]/
│   │               └── page.tsx          ← 콘텐츠 미리보기 + 승인 UI
│   └── lib/
│       ├── pipeline/
│       │   ├── collect.ts    ← collect.ts 래퍼 (pipeline_logs 연계)
│       │   ├── generate.ts   ← generate-blog.ts 래퍼 (content_queue 연계)
│       │   ├── publish.ts    ← publish 래퍼 (content_distributions 연계)
│       │   └── logger.ts     ← pipeline_logs / error_logs 유틸리티
│       └── db/
│           └── content-os.ts ← Turso 클라이언트 (기존)

8. 전체 타임라인 요약

Phase 1 (1주)
├─ Day 1-2: DB 마이그레이션 (channels, content_queue 확장, content_distributions, error_logs)
├─ Day 2-3: pipeline logger 유틸리티 + collect/generate 래퍼
├─ Day 3-4: 승인 API + 대시보드 UI (approve/reject)
├─ Day 4-5: publish 래퍼 (블로그만) + Cron 설정 + E2E 테스트
└─ Day 5: QA + 1건 실제 플로우 동작 확인

Phase 2 (2주)
├─ Brevo 자동 발송 연동
├─ getlate SNS 자동 배포 연동
├─ 텔레그램 검수 알림
├─ content_generation_config 대시보드 관리
├─ L2/L3 자동 교정
└─ 성과 수집 (D+3)

Phase 3 (3주+)
├─ 자동 승인 타이머
├─ 외부 플랫폼 연동 (티스토리, 미디엄)
├─ 성과 기반 자동 최적화
├─ DAG 시각화
└─ 크로스포스팅

리뷰 로그

[pipeline-design-pl 초안 작성] 2026-02-25 17:20

  • 기존 8개 파이프라인 파일 분석 완료 (collect, generate, generate-blog, publish, publish-blog, publish-sns, run, run-blog-pipeline)
  • 현재 파이프라인 강점 7건 + 한계점 9건 정리
  • 5단계 파이프라인 설계: 수집(Collect) → 생성(Generate) → 승인(Approve) → 배포(Publish) → 추적(Track)
  • 각 단계별 입력/출력/트리거/에러처리 상세 명세
  • 자체교정(Self-Healing) 등급별 설계: L1~L5, 단계별 시나리오 매핑
  • DB 설계서(확정) 기준 테이블 연계: collected_news, content_queue, channels, content_distributions, error_logs, pipeline_logs, content_logs, optimization_history, content_generation_config
  • Phase별 구현 범위: Phase 1 MVP(1주) / Phase 2(2주) / Phase 3(3주+)
  • 기존 코드 전환 전략: 래퍼 패턴으로 최소 변경

[자비스 1차 검수] 2026-02-25 17:35

  • ✅ frontmatter 완비 (ISO 8601 시간 포함, 전 필드 존재)
  • ✅ 비즈 기획서 v4 요구사항 전체 커버 (5단계 파이프라인, 승인 플로우, 2-Layer 상태, 자체교정 L1~L5)
  • ✅ DB 설계서(확정) 기준 9개 테이블 전부 활용 시점 명세
  • ✅ Phase 1 MVP 1주 범위 명확 + 하지 않는 것 명시
  • ✅ 기존 코드 래퍼 패턴으로 변경 최소화 전략 우수
  • ✅ 자체교정 시나리오별 L1~L3 단계 구체적 명세
  • ✅ planning-rules.md 준수 (한글, 리뷰 로그, L1 설계서 규격)
  • 결과: 승인 (approved) — 수정 없이 VP 2차 검수 요청
plans/2026/02/25/content-orchestration-design-pipeline.md