← 목록으로
2026-02-26plans

title: content-orchestration 성과 분석 대시보드 구현 플랜 date: 2026-02-26 type: implementation-plan project: content-orchestration phase: 3-analytics status: draft

content-orchestration 성과 분석 대시보드 구현 플랜

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: content-orchestration 대시보드에 성과 분석 탭을 추가하여 콘텐츠 발행 현황, 파이프라인 효율, 채널 분포, 에러 추이를 시각적으로 파악할 수 있게 한다.

Architecture: 기존 content-orchestration Next.js 15 앱에 /[project]/analytics 라우트를 추가. DB 집계 쿼리를 content-db.ts에 확장하고, 순수 Tailwind CSS 기반 차트 컴포넌트(bar/line/donut)를 자체 구현한다. 외부 차트 라이브러리(recharts 등) 의존성 없이 SVG + Tailwind로 경량 구현.

Tech Stack: Next.js 15 (App Router, RSC), Turso (LibSQL), Tailwind CSS 4, SVG 차트 (자체 구현)


코드베이스 분석 요약

기존 구조 (재사용 가능)

  • src/lib/content-db.ts: Turso 클라이언트 + 7개 조회 함수 (newsletters, queue, logs, pipeline_logs, rss_stats, news_stats, plf_schedule)
  • src/lib/projects.ts: 프로젝트 설정 (apppro/richbukae/ai-architect)
  • src/app/[project]/layout.tsx: 네비게이션 바 (개요/캘린더/RSS/로그) -- 여기에 "성과 분석" 탭 추가
  • UI 패턴: StatCard, StatusBadge, EmptyState, 테이블 패턴 일관 사용
  • 스타일: Tailwind CSS만 사용 (shadcn/ui 미사용, 외부 UI 라이브러리 없음)

DB 테이블 (analytics 데이터 소스)

테이블주요 데이터analytics 활용
collected_news수집일, 소스, 등급, 사용여부일별 수집량, 소스별 분포
newsletters생성일, 상태, 발송일발행 추이, 상태 분포
content_queue타입, 필라, 상태, 우선순위, 프로젝트, 채널타입별/필라별 분포, 파이프라인 상태
content_logs타입, 플랫폼, 상태, metrics(JSON), 발행일채널별 성과, 일별 발행량
pipeline_logs파이프라인명, 상태, 소요시간, 처리건수, metadata(JSON)파이프라인 효율, 실패율
content_distributions채널ID, 상태, 발행URL채널별 배포 추적
error_logs컴포넌트, 에러타입, 자동교정, 해결여부에러 추이, 자동교정 성공률
channels채널명, 타입, 플랫폼, 활성여부채널 목록 참조

의존성 제약

  • package.json에 차트 라이브러리 없음 (recharts 등 미설치)
  • 결정: SVG + Tailwind로 경량 차트 자체 구현 -- 의존성 추가 없이 빌드 사이즈 최소화
  • 대안: 데이터가 복잡해지면 recharts 추가 가능하지만 MVP에서는 불필요

Task 1: DB 집계 쿼리 함수 추가 (content-db.ts 확장)

파일: src/lib/content-db.ts

추가할 함수 6개:

// 1. 일별 콘텐츠 발행 수 (최근 30일)
export async function getDailyPublishStats(days: number = 30): Promise<DailyPublishStat[]>
// SQL: SELECT date(published_at/1000, 'unixepoch') as day, content_type, COUNT(*) as count
//      FROM content_logs WHERE published_at >= ? GROUP BY day, content_type ORDER BY day

// 2. 필라별 콘텐츠 분포
export async function getPillarDistribution(): Promise<PillarStat[]>
// SQL: SELECT pillar, COUNT(*) as count, status FROM content_queue
//      WHERE pillar IS NOT NULL GROUP BY pillar, status

// 3. 파이프라인 효율 통계 (최근 30일)
export async function getPipelineEfficiency(days: number = 30): Promise<PipelineEfficiency[]>
// SQL: SELECT pipeline_name, COUNT(*) as runs,
//      SUM(CASE WHEN status='completed' THEN 1 ELSE 0 END) as success,
//      SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) as failed,
//      AVG(duration_ms) as avg_duration, SUM(items_processed) as total_items
//      FROM pipeline_logs WHERE created_at >= ? GROUP BY pipeline_name

// 4. 에러 추이 (최근 30일, 컴포넌트별)
export async function getErrorTrends(days: number = 30): Promise<ErrorTrend[]>
// SQL: SELECT component, error_type, COUNT(*) as count,
//      SUM(CASE WHEN auto_fix_result='success' THEN 1 ELSE 0 END) as auto_fixed
//      FROM error_logs WHERE occurred_at >= ? GROUP BY component, error_type

// 5. 채널별 배포 현황 (content_distributions 기반)
export async function getChannelPerformance(): Promise<ChannelPerformance[]>
// SQL: SELECT cd.channel_id, c.name, c.platform, cd.platform_status, COUNT(*) as count
//      FROM content_distributions cd LEFT JOIN channels c ON cd.channel_id = c.id
//      GROUP BY cd.channel_id, cd.platform_status

// 6. 주간 요약 (이번 주 vs 지난 주)
export async function getWeeklySummary(): Promise<WeeklySummary>
// SQL: 복합 쿼리 — 이번 주/지난 주 발행수, 수집수, 에러수 비교

인터페이스 정의:

export interface DailyPublishStat { day: string; content_type: string; count: number; }
export interface PillarStat { pillar: string; status: string; count: number; }
export interface PipelineEfficiency { pipeline_name: string; runs: number; success: number; failed: number; avg_duration: number; total_items: number; }
export interface ErrorTrend { component: string; error_type: string; count: number; auto_fixed: number; }
export interface ChannelPerformance { channel_id: string; name: string; platform: string; status: string; count: number; }
export interface WeeklySummary { this_week: { published: number; collected: number; errors: number }; last_week: { published: number; collected: number; errors: number }; }

완료 기준: npm run build 타입 에러 없음.


Task 2: SVG 차트 컴포넌트 구현

파일: src/components/charts.tsx (신규)

구현할 차트 3종:

2-1. BarChart (수직 막대 차트)

  • Props: { data: {label: string; value: number; color?: string}[]; height?: number; showValues?: boolean }
  • 순수 SVG + Tailwind 클래스
  • 반응형: 컨테이너 너비 100%
  • 용도: 일별 발행량, 필라별 분포

2-2. MiniLineChart (간단 라인 차트)

  • Props: { data: number[]; labels?: string[]; height?: number; color?: string }
  • SVG polyline으로 구현
  • 용도: 일별 수집 추이, 에러 추이

2-3. DonutChart (도넛 차트)

  • Props: { data: {label: string; value: number; color: string}[]; size?: number }
  • SVG circle + stroke-dasharray로 구현
  • 중앙에 총계 표시
  • 용도: 채널별 분포, 상태 분포

스타일 규칙:

  • 기존 코드의 Tailwind 컬러 팔레트 사용 (blue-500, green-500, orange-500, purple-500, red-500)
  • 빈 데이터 시 EmptyState 패턴과 동일한 fallback 표시
  • 서버 컴포넌트 호환 (RSC 렌더링 가능)

완료 기준: 각 차트가 빈 데이터, 1개 데이터, 다수 데이터에서 정상 렌더링. npm run build 성공.


Task 3: 네비게이션 업데이트 + 라우트 생성

파일 수정: src/app/[project]/layout.tsx

변경 내용:

// NAV_ITEMS에 추가
{ href: '/analytics', label: '성과 분석', icon: '◆' },

파일 생성: src/app/[project]/analytics/page.tsx

페이지 구조:

/[project]/analytics
├── 주간 요약 카드 (이번 주 vs 지난 주, 증감 표시)
├── 일별 발행 추이 (BarChart, 최근 14일)
├── 필라별 콘텐츠 분포 (DonutChart)
├── 파이프라인 효율 (테이블 + 성공률 바)
├── 채널별 배포 현황 (BarChart)
├── 에러 추이 (MiniLineChart + 자동교정 성공률)
└── 수집 효율 (수집/사용 비율 바)

props 데이터: 모든 데이터는 Task 1에서 만든 집계 함수로 서버사이드 fetch (RSC).

완료 기준: /apppro/analytics 라우트 접속 시 페이지 렌더링, 네비게이션에서 "성과 분석" 링크 활성.


Task 4: 성과 분석 대시보드 페이지 구현

파일: src/app/[project]/analytics/page.tsx

세부 섹션 구현:

4-1. 주간 요약 카드 (Hero Section)

  • 4개 StatCard: 이번 주 발행수, 이번 주 수집수, 미해결 에러, 파이프라인 성공률
  • 각 카드에 지난 주 대비 증감 표시 (화살표 + 퍼센트)
  • 기존 StatCard 컴포넌트 확장 (delta prop 추가)

4-2. 일별 발행 추이

  • BarChart: X축=날짜(14일), Y축=발행수, 색상=content_type별
  • 날짜 포맷: "2/26" 형식 (한국어)

4-3. 필라별 콘텐츠 분포

  • DonutChart: 5대 필라별 비중
  • 옆에 범례 + 건수 표시

4-4. 파이프라인 효율

  • 테이블: 파이프라인명, 실행 횟수, 성공률(%), 평균 소요시간, 총 처리건수
  • 성공률은 가로 바로 시각화 (기존 ChannelDistribution 패턴 재사용)

4-5. 채널별 배포 현황

  • BarChart: 채널별 배포 수
  • 상태별 색상 구분 (published=green, pending=orange, failed=red)

4-6. 에러 추이

  • MiniLineChart: 일별 에러 발생 수
  • 하단에 컴포넌트별 에러 수 + 자동교정 성공률 표시

4-7. 수집 효율

  • 진행 바: 수집/사용 비율 (기존 RSS 페이지 패턴 재사용)
  • 소스별 수집량 상위 5개

완료 기준: 모든 섹션이 실데이터(Turso DB)로 렌더링. 빈 테이블에서도 에러 없이 빈 상태 표시. npm run build 성공.


Task 5: 빌드 검증 + 통합 테스트

검증 항목:

  1. npm run build 성공 (타입 에러/린트 에러 없음)
  2. /apppro/analytics 페이지 접속 확인
  3. 네비게이션 바에 "성과 분석" 탭 표시 확인
  4. 빈 데이터 상태에서 모든 차트/테이블이 graceful하게 빈 상태 표시
  5. 기존 페이지들 (/, /apppro, /apppro/calendar, /apppro/rss, /apppro/logs) 깨지지 않음 확인

수정 사항:

  • 빌드 에러 발견 시 즉시 수정
  • 타입 불일치 수정
  • 린트 경고 처리

완료 기준: npm run build 클린 성공 + 기존 페이지 영향 없음.


구현 순서 의존성

Task 1 (DB 쿼리) → Task 2 (차트 컴포넌트) → Task 3 (라우트 생성) → Task 4 (페이지 구현) → Task 5 (빌드 검증)

Task 1, 2는 독립적이므로 병렬 가능. Task 3은 Task 1+2 완료 후. Task 4는 Task 3 완료 후. Task 5는 최종.


CEO 블로킹: 없음

  • DB 스키마 변경 없음 (기존 테이블의 SELECT 쿼리만 추가)
  • 환경변수 추가 없음 (기존 CONTENT_OS_DB_URL/TOKEN 사용)
  • 외부 의존성 추가 없음 (SVG 차트 자체 구현)
  • Vercel 배포는 GitHub push 시 자동 (기존 설정 유지)

예상 구현 시간

  • Task 1: ~3분 (DB 함수 6개)
  • Task 2: ~5분 (SVG 차트 3종)
  • Task 3: ~2분 (레이아웃 수정 + 빈 페이지)
  • Task 4: ~8분 (7개 섹션 구현)
  • Task 5: ~2분 (빌드 + 검증)
  • 총: ~20분
plans/2026/02/26/content-orchestration-analytics.md