title: content-orchestration 대시보드 UI/UX 설계서 (L1) date: 2026-02-25T20:15:00+09:00 type: design layer: L1 status: in-review tags: [content-orchestration, uiux, dashboard, L1, phase1] author: uiux-design-pl project: content-orchestration reviewed_by: "jarvis" reviewed_at: "2026-02-25T21:30:00+09:00" approved_by: "" approved_at: ""
content-orchestration 대시보드 UI/UX 설계서 (L1)
Task ID: dc1d0d04-5bca-4d69-abc7-e0b4f3e40c79
작성일: 2026-02-25
작성자: uiux-design-pl
근거 문서:
- L1:
content-orchestration-design-pipeline.md(파이프라인 설계서, 승인) - L1:
content-orchestration-design-db.md(DB 설계서, 검수 완료) - L1:
content-orchestration-design-external.md(외부 연동 설계서, 승인) - L1:
content-orchestration-design-self-healing.md(자체교정 설계서, 승인)
1. 화면 구성 개요
1-1. 현재 대시보드 현황
content-pipeline 프로젝트(projects/content-pipeline/src/app/)의 현재 화면 구성:
| 경로 | 역할 | 상태 |
|---|---|---|
/ (page.tsx) | 블로그 홈 — 게시된 포스트 목록, 카테고리 탭, 페이지네이션 | 활성 (34개 포스트) |
/posts/[slug] | 개별 블로그 포스트 페이지 (SSG/ISR) | 활성 |
/feed.xml | RSS 피드 | 활성 |
/og | Open Graph 이미지 생성 | 활성 |
기존 API 라우트:
| 메서드 | 경로 | 역할 | 상태 |
|---|---|---|---|
GET | /api/pipeline/content?status= | content_queue 조회 (status 필터) | 구현 완료 |
POST | /api/pipeline/approve | 콘텐츠 승인 (draft/reviewing -> approved) | 구현 완료 |
POST | /api/pipeline/reject | 콘텐츠 거부 (reviewing -> draft, rejected_reason 기록) | 구현 완료 |
GET | /api/cron/pipeline | Vercel Cron: Stage 1(수집) -> Stage 2(생성) | 구현 완료 |
GET | /api/cron/publish | 블로그 예약 발행 (6시간 주기) | 구현 완료 |
POST | /api/subscribe | 뉴스레터 구독 | 구현 완료 |
POST | /api/track | 페이지 트래킹 | 구현 완료 |
핵심 발견: 승인/거부 API는 이미 구현되어 있으나, 이를 호출하는 프론트엔드 UI가 없다. CEO가 API를 직접 호출해야 하는 상태이다. Phase 1 MVP의 핵심은 이 API를 호출하는 대시보드 UI를 만드는 것이다.
1-2. Phase 1 MVP 필요 화면 목록
| # | 화면 | 경로 | 신규/수정 | 우선순위 |
|---|---|---|---|---|
| 1 | 파이프라인 홈 | /pipeline | 신규 | P1 |
| 2 | 콘텐츠 승인 화면 | /pipeline/review | 신규 | P1 (핵심) |
| 3 | 파이프라인 이력 | /pipeline/logs | 신규 | P1 |
| 4 | 에러 현황 | /pipeline/errors | 신규 | P1 |
| 5 | 블로그 홈 | / | 유지 (변경 없음) | — |
| 6 | 포스트 페이지 | /posts/[slug] | 유지 (변경 없음) | — |
1-3. 네비게이션 구조
기존 블로그 네비게이션과 분리된 관리자 네비게이션을 사용한다. /pipeline 하위 경로는 내부 관리 화면이므로 별도 레이아웃을 적용한다.
[블로그 영역 — 외부 공개]
/ ← 블로그 홈 (기존 유지)
/posts/[slug] ← 포스트 (기존 유지)
[파이프라인 관리 영역 — 내부 전용]
/pipeline ← 파이프라인 홈 (요약 대시보드)
/pipeline/review ← 콘텐츠 승인/거부
/pipeline/logs ← 실행 이력
/pipeline/errors ← 에러 현황
파이프라인 영역 사이드바 네비게이션:
Pipeline
├── 홈 /pipeline
├── 콘텐츠 검수 /pipeline/review
├── 실행 이력 /pipeline/logs
└── 에러 현황 /pipeline/errors
2. 핵심 화면 상세 설계
2-1. 파이프라인 홈 (/pipeline)
화면 목적
CEO/관리자가 한눈에 파이프라인 전체 현황을 파악한다. 오늘의 수집/생성/배포 건수, 검수 대기 콘텐츠 수, 미해결 에러 수를 요약 카드로 표시한다.
URL 경로
/pipeline
주요 컴포넌트
┌──────────────────────────────────────────────────────────────┐
│ Pipeline Dashboard [블로그로 이동]│
├──────────┬───────────────────────────────────────────────────┤
│ │ │
│ 사이드바 │ [요약 카드 4개 — 1행] │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────┐│
│ 홈 ● │ │ 오늘 수집 │ │ 검수 대기 │ │ 오늘 발행 │ │ 에러 ││
│ 콘텐츠 │ │ 78건 │ │ 2건 │ │ 1건 │ │ 0건 ││
│ 실행이력 │ └──────────┘ └──────────┘ └──────────┘ └──────┘│
│ 에러 │ │
│ │ [최근 파이프라인 실행 — 5건] │
│ │ ┌───────────────────────────────────────────────┐│
│ │ │ 2/25 06:00 | collect | 완료 | 78건 | 1.2초 ││
│ │ │ 2/25 06:01 | generate | 완료 | 1건 | 8.5초 ││
│ │ │ 2/24 06:00 | collect | 완료 | 65건 | 1.1초 ││
│ │ │ ... ││
│ │ └───────────────────────────────────────────────┘│
│ │ │
│ │ [검수 대기 콘텐츠 미리보기] │
│ │ ┌───────────────────────────────────────────────┐│
│ │ │ "AI 도구 비교: Claude vs GPT-5" | reviewing ││
│ │ │ AI도구리뷰 | 2/25 06:01 | [검수하기 →] ││
│ │ └───────────────────────────────────────────────┘│
│ │ │
└──────────┴───────────────────────────────────────────────────┘
데이터 표시 영역
요약 카드 (4개):
| 카드 | 데이터 소스 | 쿼리 |
|---|---|---|
| 오늘 수집 | collected_news | COUNT(*) WHERE created_at >= 오늘 00:00 KST |
| 검수 대기 | content_queue | COUNT(*) WHERE status IN ('draft', 'reviewing') |
| 오늘 발행 | content_queue | COUNT(*) WHERE status = 'published' AND updated_at >= 오늘 00:00 |
| 미해결 에러 | error_logs | COUNT(*) WHERE resolved_at IS NULL |
최근 실행 이력 (5건):
pipeline_logs에서ORDER BY created_at DESC LIMIT 5- 표시: 시각, pipeline_name, status(색상 배지), items_processed, duration_ms
검수 대기 콘텐츠 미리보기 (최대 3건):
content_queue에서status IN ('draft', 'reviewing') ORDER BY created_at DESC LIMIT 3- 표시: title, pillar, created_at, [검수하기] 버튼 ->
/pipeline/review이동
사용자 액션
| 액션 | 동작 |
|---|---|
| 요약 카드 클릭 | 해당 상세 페이지로 이동 (검수 대기 -> /pipeline/review, 에러 -> /pipeline/errors) |
| "검수하기" 버튼 | /pipeline/review 페이지로 이동 |
| "전체 보기" 링크 | /pipeline/logs 페이지로 이동 |
| 사이드바 네비게이션 | 각 하위 페이지로 이동 |
API 연계
| API | 용도 |
|---|---|
GET /api/pipeline/content?status=draft + ?status=reviewing | 검수 대기 콘텐츠 목록 |
신규 GET /api/pipeline/stats | 요약 카드 데이터 (수집/발행/에러 집계) |
신규 GET /api/pipeline/logs?limit=5 | 최근 실행 이력 |
상태 표시
| 상태 | 화면 표시 |
|---|---|
| 로딩 | 스켈레톤 UI (카드 영역 회색 펄스) |
| 에러 | "데이터를 불러올 수 없습니다" + 재시도 버튼 |
| 빈 화면 (검수 대기 0건) | "검수 대기 중인 콘텐츠가 없습니다" 안내 메시지 |
| 빈 화면 (실행 이력 0건) | "아직 파이프라인이 실행되지 않았습니다" 안내 메시지 |
2-2. 콘텐츠 승인 화면 (/pipeline/review) — 가장 중요
화면 목적
CEO가 AI 생성 콘텐츠를 검수하고 승인 또는 거부한다. Phase 1 MVP의 핵심 화면. 이 화면이 없으면 CEO가 API를 직접 호출해야 하므로, 이 화면의 완성이 Phase 1의 완료 기준이다.
URL 경로
/pipeline/review
주요 컴포넌트
┌──────────────────────────────────────────────────────────────┐
│ 콘텐츠 검수 [블로그로 이동]│
├──────────┬───────────────────────────────────────────────────┤
│ │ │
│ 사이드바 │ [필터 탭] │
│ │ [전체(5)] [검수대기(2)] [승인됨(2)] [거부됨(1)] │
│ │ │
│ │ [콘텐츠 목록 — 좌측 패널] [미리보기 — 우측 패널] │
│ │ ┌──────────────────┐ ┌───────────────────────┐ │
│ │ │ ● AI 도구 비교: │ │ 제목: AI 도구 비교: │ │
│ │ │ Claude vs GPT │ │ Claude vs GPT-5 │ │
│ │ │ reviewing │ │ │ │
│ │ │ AI도구리뷰 │ │ 필라: AI도구리뷰 │ │
│ │ │ 2/25 06:01 │ │ 유형: blog │ │
│ │ ├──────────────────┤ │ 생성일: 2/25 06:01 │ │
│ │ │ 다음 콘텐츠... │ │ 상태: reviewing │ │
│ │ │ │ │ │ │
│ │ │ │ │ ─── 본문 미리보기 ─── │ │
│ │ │ │ │ │ │
│ │ │ │ │ # AI 도구 비교: │ │
│ │ │ │ │ Claude vs GPT-5 │ │
│ │ │ │ │ │ │
│ │ │ │ │ ## 1. 개요 │ │
│ │ │ │ │ 최근 AI 도구 시장에서 │ │
│ │ │ │ │ ... │ │
│ │ │ │ │ │ │
│ │ │ │ │ ─── 액션 영역 ─── │ │
│ │ │ │ │ │ │
│ │ │ │ │ [승인] [거부] │ │
│ │ │ │ │ │ │
│ │ │ │ │ (거부 시 사유 입력란) │ │
│ │ └──────────────────┘ └───────────────────────┘ │
│ │ │
└──────────┴───────────────────────────────────────────────────┘
데이터 표시 영역
좌측: 콘텐츠 목록
content_queue에서ORDER BY created_at DESC LIMIT 50- 필터 탭으로 status 필터링: 전체 / draft+reviewing / approved / draft(rejected_reason IS NOT NULL)
- 각 항목 표시: title (2줄 말줄임), status 배지 (색상 구분), pillar, created_at
- 선택된 항목 하이라이트
우측: 콘텐츠 미리보기
- 메타데이터 영역: title, pillar, type, status, created_at, approved_by, approved_at
- 본문 미리보기:
content_body를 마크다운 렌더링 (H2 구조, 본문 내용 확인 가능) - 액션 영역: 승인/거부 버튼 + 거부 사유 입력란
상태 배지 색상:
| status | 배지 색상 | 텍스트 |
|---|---|---|
draft | 회색 (gray) | 초안 |
reviewing | 노란색 (yellow) | 검수중 |
approved | 녹색 (green) | 승인됨 |
scheduled | 파란색 (blue) | 예약됨 |
published | 남색 (indigo) | 발행됨 |
failed | 빨간색 (red) | 실패 |
사용자 액션
| # | 액션 | UI 요소 | API 호출 | 결과 |
|---|---|---|---|---|
| 1 | 콘텐츠 승인 | 녹색 "승인" 버튼 | POST /api/pipeline/approve { contentId, approvedBy: 'ceo' } | status -> approved, 자동 발행 트리거 |
| 2 | 콘텐츠 거부 | 빨간색 "거부" 버튼 -> 사유 모달 | POST /api/pipeline/reject { contentId, reason } | status -> draft, rejected_reason 기록 |
| 3 | 필터 전환 | 탭 버튼 클릭 | 클라이언트 사이드 필터링 (또는 GET /api/pipeline/content?status=) | 목록 갱신 |
| 4 | 콘텐츠 선택 | 목록 항목 클릭 | — | 우측 미리보기 패널 갱신 |
| 5 | 목록 새로고침 | 새로고침 아이콘 | GET /api/pipeline/content 재호출 | 최신 데이터로 목록 갱신 |
승인/거부 인터랙션 상세
승인 플로우:
1. CEO가 "승인" 버튼 클릭
2. 확인 다이얼로그: "이 콘텐츠를 승인하시겠습니까? 승인 시 자동으로 블로그에 발행됩니다."
3. CEO "확인" 클릭
4. POST /api/pipeline/approve 호출
5. 로딩 스피너 표시 (버튼 비활성화)
6. 성공 시:
- 토스트 알림: "승인 완료! 블로그 발행이 시작되었습니다."
- 상태 배지 approved로 변경 (optimistic update)
- 자동 발행 결과 표시 (성공/실패)
7. 실패 시:
- 토스트 알림: "승인 처리 중 오류가 발생했습니다."
- 재시도 안내
거부 플로우:
1. CEO가 "거부" 버튼 클릭
2. 모달 표시: "거부 사유를 입력해주세요" + textarea
3. CEO 사유 입력 + "거부 확인" 클릭
4. POST /api/pipeline/reject 호출
5. 로딩 스피너 표시
6. 성공 시:
- 토스트 알림: "콘텐츠가 거부되었습니다. AI가 사유를 반영하여 재생성합니다."
- 상태 배지 draft로 변경
- 이전 거부 사유 표시 (rejected_reason)
7. 실패 시:
- 토스트 알림: "거부 처리 중 오류가 발생했습니다."
API 연계
| API | 용도 | 기존/신규 |
|---|---|---|
GET /api/pipeline/content | 콘텐츠 목록 (전체/status별) | 기존 구현 |
GET /api/pipeline/content?status=reviewing | 검수 대기 목록 | 기존 구현 |
POST /api/pipeline/approve | 콘텐츠 승인 | 기존 구현 |
POST /api/pipeline/reject | 콘텐츠 거부 | 기존 구현 |
신규 GET /api/pipeline/content/[id] | 개별 콘텐츠 상세 (content_body 포함) | 신규 필요 |
참고: 기존
GET /api/pipeline/content는content_body를 조회하지 않는다. 목록에서는 title만 표시하고, 개별 콘텐츠 선택 시content_body를 포함한 상세 조회 API가 필요하다. 이를 위해GET /api/pipeline/content/[id]엔드포인트를 신규로 추가한다.
상태 표시
| 상태 | 화면 표시 |
|---|---|
| 로딩 (목록) | 스켈레톤 UI (카드 목록 영역) |
| 로딩 (미리보기) | 본문 영역 스켈레톤 |
| 로딩 (승인/거부 처리 중) | 버튼 비활성화 + 스피너 |
| 에러 (API 실패) | "데이터를 불러올 수 없습니다" + 재시도 버튼 |
| 빈 화면 (콘텐츠 0건) | "검수할 콘텐츠가 없습니다. 파이프라인이 실행되면 여기에 표시됩니다." |
| 콘텐츠 미선택 | 우측 패널에 "좌측에서 콘텐츠를 선택하세요" 안내 |
2-3. 파이프라인 이력 화면 (/pipeline/logs)
화면 목적
파이프라인 실행 이력을 시간순으로 조회한다. 각 단계(collect, generate, approve, publish, self-healing)의 성공/실패 현황, 소요 시간, 처리 건수를 확인한다.
URL 경로
/pipeline/logs
주요 컴포넌트
┌──────────────────────────────────────────────────────────────┐
│ 실행 이력 [블로그로 이동]│
├──────────┬───────────────────────────────────────────────────┤
│ │ │
│ 사이드바 │ [요약 통계 카드 — 1행] │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ │ 총 실행 │ │ 성공률 │ │ 평균 소요 │ │
│ │ │ 45건 │ │ 91% │ │ 12.3초 │ │
│ │ └───────────┘ └───────────┘ └───────────┘ │
│ │ │
│ │ [필터 바] │
│ │ 단계: [전체|수집|생성|승인|배포|자체교정] │
│ │ 상태: [전체|완료|실패|진행중] │
│ │ 기간: [최근7일|최근30일|전체] │
│ │ │
│ │ [로그 테이블] │
│ │ ┌──────┬───────┬─────┬──────┬──────┬──────────┐ │
│ │ │ 시각 │ 단계 │상태 │ 건수 │ 소요 │ 메타데이터│ │
│ │ ├──────┼───────┼─────┼──────┼──────┼──────────┤ │
│ │ │2/25 │collect│ ✅ │ 78건 │1.2초 │feeds:15/17│ │
│ │ │06:00 │ │ │ │ │ │ │
│ │ ├──────┼───────┼─────┼──────┼──────┼──────────┤ │
│ │ │2/25 │generate│ ✅ │ 1건 │8.5초 │pillar:AI │ │
│ │ │06:01 │ │ │ │ │qa:7/8 │ │
│ │ ├──────┼───────┼─────┼──────┼──────┼──────────┤ │
│ │ │2/24 │collect│ ⚠️ │ 65건 │2.1초 │feeds:14/17│ │
│ │ │06:00 │ │부분 │ │ │fail:3 │ │
│ │ └──────┴───────┴─────┴──────┴──────┴──────────┘ │
│ │ │
│ │ [페이지네이션: < 1 2 3 ... >] │
│ │ │
└──────────┴───────────────────────────────────────────────────┘
데이터 표시 영역
요약 통계 카드 (3개):
| 카드 | 데이터 소스 | 계산 |
|---|---|---|
| 총 실행 | pipeline_logs | COUNT(*) WHERE created_at >= 기간 |
| 성공률 | pipeline_logs | COUNT(status='completed') / COUNT(*) * 100 |
| 평균 소요 | pipeline_logs | AVG(duration_ms) WHERE status='completed' |
로그 테이블:
pipeline_logs에서ORDER BY created_at DESC- 페이지네이션: 20건/페이지
- 컬럼: 시각(created_at), 단계(pipeline_name), 상태(status 배지), 처리 건수(items_processed), 소요 시간(duration_ms), 메타데이터(metadata JSON에서 핵심 필드 추출)
status 배지:
| status | 배지 | 아이콘 |
|---|---|---|
started | 파란색 | 회전 스피너 |
completed | 녹색 | 체크마크 |
failed | 빨간색 | X 마크 |
pipeline_name 표시명:
| pipeline_name | 한글 표시 |
|---|---|
collect | 수집 |
generate | 생성 |
approve | 승인 |
publish | 배포 |
self-healing | 자체교정 |
metadata 핵심 필드 표시:
| pipeline_name | 표시할 metadata 필드 |
|---|---|
collect | feeds_ok/feeds_fail, filter_pass |
generate | pillar, qa_score, content_type |
approve | approved_by, content_id |
publish | channels_ok/channels_fail |
self-healing | fixed/escalated/total_errors |
사용자 액션
| 액션 | 동작 |
|---|---|
| 필터 탭 클릭 | pipeline_name 또는 status로 필터링 |
| 기간 선택 | created_at 범위 필터링 |
| 로그 행 클릭 | 상세 모달 표시 (metadata JSON 전체, error_message, error_log_id) |
| 페이지네이션 | 다음/이전 20건 |
| 새로고침 | 최신 데이터 재조회 |
API 연계
| API | 용도 | 기존/신규 |
|---|---|---|
신규 GET /api/pipeline/logs | 실행 로그 목록 (필터, 페이지네이션) | 신규 필요 |
신규 GET /api/pipeline/stats | 요약 통계 (총 실행, 성공률, 평균 소요) | 신규 필요 |
GET /api/pipeline/logs 쿼리 파라미터:
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
pipeline_name | string | null | 단계 필터 (collect/generate/...) |
status | string | null | 상태 필터 (completed/failed/started) |
days | number | 7 | 기간 필터 (최근 N일) |
page | number | 1 | 페이지 번호 |
limit | number | 20 | 페이지 당 건수 |
상태 표시
| 상태 | 화면 표시 |
|---|---|
| 로딩 | 테이블 영역 스켈레톤 |
| 에러 | "실행 이력을 불러올 수 없습니다" + 재시도 |
| 빈 화면 | "아직 파이프라인이 실행되지 않았습니다. Cron이 매일 06:00(KST)에 자동 실행합니다." |
2-4. 에러 현황 화면 (/pipeline/errors)
화면 목적
파이프라인 에러를 통합 조회한다. 에스컬레이션(escalated=1) 에러를 최상단에 우선 표시하여, CEO/VP가 즉시 대응해야 할 에러를 빠르게 식별한다. 자체교정 시도 결과(성공/실패)도 함께 표시한다.
URL 경로
/pipeline/errors
주요 컴포넌트
┌──────────────────────────────────────────────────────────────┐
│ 에러 현황 [블로그로 이동]│
├──────────┬───────────────────────────────────────────────────┤
│ │ │
│ 사이드바 │ [요약 카드 — 1행] │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ │미해결 에러 │ │에스컬레이션│ │자동교정성공│ │
│ │ │ 3건 │ │ 1건 │ │ 78% │ │
│ │ └───────────┘ └───────────┘ └───────────┘ │
│ │ │
│ │ [필터 바] │
│ │ 상태: [미해결|전체|해결됨] │
│ │ 컴포넌트: [전체|rss|ai|brevo|sns|...] │
│ │ 에러유형: [전체|timeout|auth|quality|api|...] │
│ │ │
│ │ [에스컬레이션 섹션 — 빨간 배경] │
│ │ ┌───────────────────────────────────────────────┐│
│ │ │ ⚠ ESCALATED: Brevo 인증 실패 ││
│ │ │ component: brevo | type: auth_fail ││
│ │ │ 2/25 06:05 | HTTP 401 Unauthorized ││
│ │ │ 자동교정: 시도 안함 (에스컬레이션 대상) ││
│ │ │ 조치필요: CEO Brevo API 키 갱신 필요 ││
│ │ └───────────────────────────────────────────────┘│
│ │ │
│ │ [일반 에러 목록] │
│ │ ┌──────┬──────────┬────────┬──────┬───────────┐ │
│ │ │시각 │컴포넌트 │유형 │교정 │메시지 │ │
│ │ ├──────┼──────────┼────────┼──────┼───────────┤ │
│ │ │2/25 │rss │timeout │✅성공 │Feed X │ │
│ │ │06:00 │_collector│ │L1재시도│timeout │ │
│ │ ├──────┼──────────┼────────┼──────┼───────────┤ │
│ │ │2/24 │ai │quality │✅성공 │QA 5/8 │ │
│ │ │06:02 │_generator│_fail │L2재생성│→재생성7/8│ │
│ │ └──────┴──────────┴────────┴──────┴───────────┘ │
│ │ │
│ │ [페이지네이션: < 1 2 3 ... >] │
│ │ │
└──────────┴───────────────────────────────────────────────────┘
데이터 표시 영역
요약 카드 (3개):
| 카드 | 데이터 소스 | 쿼리 |
|---|---|---|
| 미해결 에러 | error_logs | COUNT(*) WHERE resolved_at IS NULL |
| 에스컬레이션 | error_logs | COUNT(*) WHERE escalated = 1 AND resolved_at IS NULL |
| 자동교정 성공률 | error_logs | COUNT(auto_fix_result='success') / COUNT(auto_fix_attempted=1) * 100 (최근 7일) |
에스컬레이션 섹션:
error_logs WHERE escalated = 1 AND resolved_at IS NULL ORDER BY occurred_at DESC- 빨간색 배경/보더로 시각적 강조
- 각 항목: component, error_type, error_message, occurred_at, 권장 조치 문구
일반 에러 테이블:
error_logs WHERE escalated = 0 ORDER BY occurred_at DESC- 컬럼: 시각(occurred_at), 컴포넌트(component), 유형(error_type), 자동교정 결과(auto_fix_result + auto_fix_action), 에러 메시지(error_message)
자동교정 결과 배지:
| auto_fix_result | 배지 |
|---|---|
success | 녹색 "자동교정 성공" + auto_fix_action |
failed | 주황색 "자동교정 실패" |
skipped | 회색 "교정 미시도" |
null (auto_fix_attempted=0) | 회색 "대기중" |
에스컬레이션 권장 조치 (error_type별):
| error_type | 권장 조치 문구 |
|---|---|
auth_fail | "API 키 갱신이 필요합니다. 환경 변수를 확인하세요." |
quality_fail (3회) | "프롬프트 검토가 필요합니다." |
timeout (3회 연속) | "서비스 상태를 확인하세요." |
api_error (3회 연속) | "외부 서비스 장애가 의심됩니다." |
사용자 액션
| 액션 | 동작 |
|---|---|
| 필터 전환 | 미해결/전체/해결됨, 컴포넌트별, 유형별 필터 |
| 에러 행 클릭 | 상세 모달 (전체 error_message, auto_fix_action, content_id, channel_id) |
| "해결됨 처리" 버튼 | error_logs UPDATE resolved_at + resolution_type='manual_fixed' |
| 새로고침 | 최신 데이터 재조회 |
API 연계
| API | 용도 | 기존/신규 |
|---|---|---|
신규 GET /api/pipeline/errors | 에러 로그 목록 (필터, 페이지네이션) | 신규 필요 |
신규 POST /api/pipeline/errors/[id]/resolve | 에러 수동 해결 처리 | 신규 필요 |
GET /api/pipeline/errors 쿼리 파라미터:
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
resolved | string | 'unresolved' | 'unresolved' / 'resolved' / 'all' |
component | string | null | 컴포넌트 필터 |
error_type | string | null | 에러 유형 필터 |
page | number | 1 | 페이지 번호 |
limit | number | 20 | 페이지 당 건수 |
상태 표시
| 상태 | 화면 표시 |
|---|---|
| 로딩 | 테이블 영역 스켈레톤 |
| 에러 (API 실패) | "에러 현황을 불러올 수 없습니다" + 재시도 |
| 빈 화면 (에러 0건) | 녹색 배경 안내: "에러가 없습니다. 파이프라인이 정상 동작 중입니다." |
| 에스컬레이션 0건 | 에스컬레이션 섹션 숨김 |
3. 콘텐츠 승인 화면 상세 (가장 중요)
3-1. 콘텐츠 목록 표시
content_queue에서 조회한 항목을 좌측 패널에 목록으로 표시한다.
목록 항목 구조:
┌─────────────────────────────────────┐
│ [status 배지] [pillar 태그] │
│ │
│ 제목: AI 도구 비교 가이드 — Claude │
│ vs GPT-5, 무엇을 선택해야 할까? │
│ │
│ 2026-02-25 06:01 │
│ (거부 이력: "본문이 너무 짧습니다") │
└─────────────────────────────────────┘
- status 배지: 위 2-2에서 정의한 색상 규칙
- pillar 태그: 연한 배경 태그 (
AI도구리뷰,업종별AI가이드등) - 제목: title 2줄 말줄임 (
line-clamp-2) - 생성일: created_at (한국 날짜 형식)
- 거부 이력: rejected_reason이 있으면 작은 글씨로 표시 (이전 거부 사유 확인용)
3-2. 미리보기 (content_body)
우측 패널에 콘텐츠 본문을 마크다운 형식으로 렌더링한다.
미리보기 구성:
┌──────────────────────────────────────┐
│ 메타데이터 영역 │
│ ─────────────────────────────────── │
│ 제목: AI 도구 비교 가이드 │
│ 유형: blog | 필라: AI도구리뷰 │
│ 상태: reviewing | 생성일: 2/25 06:01 │
│ │
│ QA 점수: 7/8 ●●●●●●●○ │
│ │
│ ═══════════════════════════════════ │
│ │
│ 본문 미리보기 (마크다운 렌더링) │
│ │
│ # AI 도구 비교: Claude vs GPT-5 │
│ │
│ ## 1. 개요 │
│ 최근 AI 도구 시장에서 가장 주목받는... │
│ │
│ ## 2. 기능 비교 │
│ | 항목 | Claude | GPT-5 | │
│ |------|--------|-------| │
│ | ... | ... | ... | │
│ │
│ (스크롤 가능) │
│ │
│ ═══════════════════════════════════ │
│ │
│ 액션 영역 │
│ ─────────────────────────────────── │
│ │
│ [✅ 승인] [❌ 거부] │
│ │
│ (approved/published 상태일 때는 │
│ 버튼 비활성화 + "이미 승인됨" 표시) │
│ │
└──────────────────────────────────────┘
마크다운 렌더링 라이브러리: react-markdown (이미 Next.js에서 사용 가능) 또는 @next/mdx
QA 점수 시각화: 파이프라인 설계서 기준 8항목 점수를 점(dot) 또는 바 형태로 표시. content_queue에 qa_score가 없으면 metadata에서 추출하거나 표시 생략.
3-3. 승인/거부 API 연계 상세
기존 구현된 API와의 연계:
승인 API (POST /api/pipeline/approve):
- 요청:
{ contentId: string, approvedBy: 'ceo' } - 응답:
{ success, contentId, status, approvedBy, approvedAt, autoPublish: { triggered, success, blogPostId } } - 승인 완료 시 자동으로
stage-publish모듈이 호출되어 블로그 발행까지 진행 - UI에서 autoPublish.success를 확인하여 발행 결과를 토스트로 표시
거부 API (POST /api/pipeline/reject):
- 요청:
{ contentId: string, reason: string } - 응답:
{ success, contentId, status, reason } - 거부 사유(reason)는 필수. 빈 문자열 전송 시 400 에러
상세 조회 API (GET /api/pipeline/content/[id]) — 신규 구현 필요:
- 응답:
{ id, type, pillar, topic, status, title, content_body, approved_by, approved_at, rejected_reason, created_at, updated_at } - 기존 목록 API와 다른 점:
content_body컬럼을 포함하여 본문 미리보기 가능
3-4. Optimistic Update vs Revalidation
Phase 1에서는 Revalidation (서버 재조회) 방식을 사용한다.
1. 승인/거부 API 호출
2. API 응답 수신
3. 성공 시: 콘텐츠 목록 API 재호출 (revalidation)
4. 목록 갱신 완료
이유:
- Phase 1 MVP에서는 구현 단순성 우선
- 승인 시 자동 발행(stage-publish)까지 포함되므로, 서버 상태가 여러 테이블에 걸쳐 변경됨
- Phase 2에서 optimistic update 또는 SWR mutation으로 개선 가능
4. API 목록 (화면별 필요 API)
4-1. 기존 구현 API (변경 불필요)
| 메서드 | 경로 | 화면 | 역할 |
|---|---|---|---|
GET | /api/pipeline/content | 승인 화면 | 콘텐츠 목록 (status 필터) |
POST | /api/pipeline/approve | 승인 화면 | 콘텐츠 승인 + 자동 발행 |
POST | /api/pipeline/reject | 승인 화면 | 콘텐츠 거부 |
GET | /api/cron/pipeline | (Cron) | 수집+생성 자동 실행 |
4-2. 신규 구현 필요 API
| # | 메서드 | 경로 | 화면 | 역할 |
|---|---|---|---|---|
| 1 | GET | /api/pipeline/content/[id] | 승인 화면 | 개별 콘텐츠 상세 (content_body 포함) |
| 2 | GET | /api/pipeline/stats | 홈 | 요약 통계 (수집/검수대기/발행/에러 집계) |
| 3 | GET | /api/pipeline/logs | 이력 화면 | 실행 로그 목록 (필터, 페이지네이션) |
| 4 | GET | /api/pipeline/errors | 에러 화면 | 에러 로그 목록 (필터, 페이지네이션) |
| 5 | POST | /api/pipeline/errors/[id]/resolve | 에러 화면 | 에러 수동 해결 처리 |
4-3. 신규 API 입출력 명세
GET /api/pipeline/content/[id]
입력: URL 파라미터 id
출력:
{
"item": {
"id": "cq-001",
"type": "blog",
"pillar": "AI도구리뷰",
"topic": "Claude vs GPT-5",
"status": "reviewing",
"title": "AI 도구 비교: Claude vs GPT-5",
"content_body": "# AI 도구 비교...\n\n## 1. 개요\n...",
"approved_by": null,
"approved_at": null,
"rejected_reason": null,
"created_at": 1740441660000,
"updated_at": 1740441660000
}
}
GET /api/pipeline/stats
입력: 없음
출력:
{
"collected_today": 78,
"pending_review": 2,
"published_today": 1,
"unresolved_errors": 0,
"recent_logs": [
{
"id": "log-001",
"pipeline_name": "collect",
"status": "completed",
"items_processed": 78,
"duration_ms": 1200,
"created_at": 1740441600000
}
]
}
GET /api/pipeline/logs
입력: 쿼리 파라미터 (pipeline_name, status, days, page, limit)
출력:
{
"items": [
{
"id": "log-001",
"pipeline_name": "collect",
"status": "completed",
"duration_ms": 1200,
"items_processed": 78,
"error_message": null,
"metadata": "{\"feeds_ok\":15,\"feeds_fail\":2}",
"trigger_type": "scheduled",
"created_at": 1740441600000
}
],
"total": 45,
"page": 1,
"limit": 20,
"stats": {
"total_runs": 45,
"success_rate": 91,
"avg_duration_ms": 12300
}
}
GET /api/pipeline/errors
입력: 쿼리 파라미터 (resolved, component, error_type, page, limit)
출력:
{
"escalated": [
{
"id": "err-001",
"occurred_at": 1740441900000,
"component": "brevo",
"error_type": "auth_fail",
"error_message": "HTTP 401 Unauthorized",
"escalated": 1,
"auto_fix_attempted": 0,
"auto_fix_result": null,
"resolved_at": null
}
],
"items": [
{
"id": "err-002",
"occurred_at": 1740441600000,
"component": "rss_collector",
"error_type": "timeout",
"error_message": "Feed timeout: 10s exceeded",
"auto_fix_attempted": 1,
"auto_fix_result": "success",
"auto_fix_action": "L1 즉시 재시도 성공",
"resolved_at": 1740441605000,
"resolution_type": "auto_fixed"
}
],
"total": 12,
"page": 1,
"limit": 20,
"stats": {
"unresolved": 3,
"escalated_count": 1,
"auto_fix_success_rate": 78
}
}
POST /api/pipeline/errors/[id]/resolve
입력:
{
"resolution_type": "manual_fixed"
}
출력:
{
"success": true,
"id": "err-001",
"resolved_at": 1740442000000,
"resolution_type": "manual_fixed"
}
5. 컴포넌트 구조
5-1. 파일 구조
app/pipeline/
├── layout.tsx ← 파이프라인 전용 레이아웃 (사이드바 네비게이션)
├── page.tsx ← 파이프라인 홈 (요약 대시보드)
├── review/
│ └── page.tsx ← 콘텐츠 승인/거부 (핵심 화면)
├── logs/
│ └── page.tsx ← 실행 이력
└── errors/
└── page.tsx ← 에러 현황
components/pipeline/
├── sidebar.tsx ← 사이드바 네비게이션 (4개 메뉴)
├── stat-card.tsx ← 요약 통계 카드 (재사용)
├── status-badge.tsx ← 상태 배지 (content_queue, pipeline_logs 공용)
├── content-list.tsx ← 콘텐츠 목록 (승인 화면 좌측)
├── content-preview.tsx ← 콘텐츠 미리보기 (승인 화면 우측)
├── approve-actions.tsx ← 승인/거부 버튼 + 거부 모달
├── log-table.tsx ← 파이프라인 로그 테이블
├── error-table.tsx ← 에러 로그 테이블
├── error-escalated.tsx ← 에스컬레이션 에러 카드
├── filter-bar.tsx ← 필터 바 (탭 + 드롭다운)
├── pagination.tsx ← 페이지네이션 (재사용)
└── toast.tsx ← 토스트 알림 (승인/거부 결과)
api/pipeline/
├── content/
│ └── [id]/
│ └── route.ts ← 개별 콘텐츠 상세 조회 (신규)
├── stats/
│ └── route.ts ← 요약 통계 (신규)
├── logs/
│ └── route.ts ← 실행 로그 목록 (신규)
└── errors/
├── route.ts ← 에러 로그 목록 (신규)
└── [id]/
└── resolve/
└── route.ts ← 에러 수동 해결 (신규)
5-2. 컴포넌트별 역할
| 컴포넌트 | 역할 | 사용 화면 |
|---|---|---|
sidebar.tsx | 좌측 사이드바 네비게이션. 현재 경로 하이라이트. 미해결 에러 수 배지 표시 | 모든 /pipeline 하위 |
stat-card.tsx | 숫자 + 라벨 + 색상을 받아 카드로 표시. 클릭 시 상세 페이지 이동 | 홈, 이력, 에러 |
status-badge.tsx | status 문자열을 받아 색상 배지로 렌더링. content_queue, pipeline_logs 공용 | 승인, 이력 |
content-list.tsx | content_queue 목록 렌더링. 선택 상태 관리. 필터 탭 포함 | 승인 |
content-preview.tsx | 메타데이터 + content_body 마크다운 렌더링. 읽기 전용 | 승인 |
approve-actions.tsx | 승인/거부 버튼. 거부 시 사유 입력 모달. API 호출 + 결과 처리 | 승인 |
log-table.tsx | pipeline_logs 테이블 렌더링. 행 클릭 시 상세 모달 | 이력 |
error-table.tsx | error_logs 테이블 렌더링. 자동교정 결과 배지 포함 | 에러 |
error-escalated.tsx | 에스컬레이션 에러 카드. 빨간 배경. 권장 조치 문구 포함 | 에러 |
filter-bar.tsx | 탭 형태 필터. 값 변경 시 콜백 호출 | 승인, 이력, 에러 |
pagination.tsx | page, totalPages를 받아 페이지네이션 UI 렌더링 | 이력, 에러 |
toast.tsx | 승인/거부/에러 결과를 화면 우상단에 토스트 알림으로 표시 | 승인, 에러 |
5-3. 파이프라인 레이아웃 (pipeline/layout.tsx)
/pipeline 하위 페이지는 블로그 레이아웃과 별도의 관리자 레이아웃을 사용한다.
구성:
- 상단 헤더: "Pipeline Dashboard" + 블로그로 이동 링크
- 좌측: 사이드바 (sidebar.tsx)
- 우측: 콘텐츠 영역 (children)
블로그 레이아웃과의 관계:
app/layout.tsx(루트 레이아웃)은 유지 — HTML, body, 글로벌 스타일app/pipeline/layout.tsx(파이프라인 레이아웃)은 루트 레이아웃 내부에 중첩- 블로그 헤더/푸터는
/pipeline하위에서 표시하지 않음 (관리 화면이므로)
구현 방안:
app/pipeline/layout.tsx에서 사이드바 + 콘텐츠 영역 2컬럼 그리드 적용- 블로그 헤더(
layout.tsx의<header>)는 유지하되,/pipeline경로에서는 다른 네비게이션 표시 (조건부 렌더링 또는 별도 레이아웃)
6. Phase별 구현 범위
Phase 1 MVP (1주): 승인 UI + 기본 대시보드
필수 구현:
| # | 구현 항목 | 상세 |
|---|---|---|
| 1 | app/pipeline/layout.tsx | 파이프라인 전용 레이아웃 (사이드바 포함) |
| 2 | app/pipeline/page.tsx | 홈 — 요약 카드 4개 + 최근 실행 5건 + 검수 대기 미리보기 |
| 3 | app/pipeline/review/page.tsx | 콘텐츠 승인 — 목록 + 미리보기 + 승인/거부 버튼 (핵심) |
| 4 | app/pipeline/logs/page.tsx | 실행 이력 — 로그 테이블 + 필터 + 페이지네이션 |
| 5 | app/pipeline/errors/page.tsx | 에러 현황 — 에스컬레이션 우선 표시 + 에러 테이블 |
| 6 | api/pipeline/content/[id]/route.ts | 개별 콘텐츠 상세 조회 API |
| 7 | api/pipeline/stats/route.ts | 요약 통계 API |
| 8 | api/pipeline/logs/route.ts | 실행 로그 목록 API |
| 9 | api/pipeline/errors/route.ts | 에러 로그 목록 API |
| 10 | api/pipeline/errors/[id]/resolve/route.ts | 에러 수동 해결 API |
| 11 | 공용 컴포넌트 7개 | sidebar, stat-card, status-badge, content-list, content-preview, approve-actions, toast |
Phase 1 제한 (하지 않는 것):
- 인증/권한 관리 (Phase 1은 URL 접근만으로 사용, 보안은 Vercel Preview URL로 한정)
- Optimistic Update (서버 재조회 방식 사용)
- 실시간 업데이트 (polling이나 WebSocket 없음, 수동 새로고침)
- 모바일 최적화 (데스크톱 우선, 기본 반응형만)
- 다크 모드
- content_distributions 화면 (배포 상태 조회는 Phase 2)
- 파이프라인 수동 트리거 UI (API 직접 호출로 대체)
Phase 2 (2주): 고도화 + 배포 현황
| # | 구현 항목 | 상세 |
|---|---|---|
| 1 | 배포 현황 화면 (/pipeline/distributions) | content_distributions 테이블 기반, 채널별 배포 상태 |
| 2 | Optimistic Update | 승인/거부 시 즉시 UI 반영 (SWR mutation) |
| 3 | 자동 새로고침 | 30초 간격 polling으로 최신 데이터 자동 반영 |
| 4 | 파이프라인 수동 트리거 | "수집 실행" / "생성 실행" 버튼 (대시보드에서 직접 API 호출) |
| 5 | 채널 관리 화면 (/pipeline/channels) | channels 테이블 CRUD (활성/비활성 전환) |
| 6 | 자체교정 대시보드 | 최근 7일 교정 성공률, 등급별 분포 |
| 7 | 모바일 반응형 | 사이드바 -> 하단 탭바, 승인 화면 1컬럼 |
| 8 | Telegram 알림 연동 | 검수 요청 시 CEO 텔레그램 알림 (review 화면에서 알림 상태 표시) |
Phase 3 (3주+): 고급 기능
| # | 구현 항목 | 상세 |
|---|---|---|
| 1 | DAG 시각화 | 파이프라인 전 과정을 시각적 플로우 차트로 표시 |
| 2 | 성과 대시보드 | 발행 후 조회수/오픈율/클릭 통계 (content_distributions.metrics) |
| 3 | AI 파라미터 관리 UI | content_generation_config 편집 (모델, 온도, 프롬프트) |
| 4 | 자동 승인 타이머 설정 | N시간 무응답 시 자동 승인 (설정 화면) |
| 5 | 인증/권한 | 관리자 로그인, 역할 기반 접근 제어 |
| 6 | 다크 모드 | 시스템 설정 연동 |
| 7 | optimization_history 시각화 | 파라미터 변경 이력 + 효과 그래프 |
7. 디자인 가이드라인
7-1. 색상 체계
기존 블로그의 CSS 변수(var(--color-*))를 최대한 재사용하되, 관리 화면에 필요한 추가 색상을 정의한다.
기존 색상 (블로그):
--color-primary: 파란색 (기본 강조)--color-text: 진한 회색 (본문)--color-text-muted: 연한 회색 (보조)--color-border: 테두리--color-primary-light: 연한 파란 배경
관리 화면 추가 색상:
- 성공(green):
#10B981(배지, 카드) - 경고(yellow):
#F59E0B(reviewing 배지) - 에러(red):
#EF4444(failed 배지, 에스컬레이션) - 정보(blue):
#3B82F6(scheduled 배지) - 남색(indigo):
#6366F1(published 배지)
7-2. 타이포그래피
기존 블로그의 Pretendard 폰트를 그대로 사용한다.
| 요소 | 크기 | 두께 |
|---|---|---|
| 페이지 제목 | text-2xl (24px) | font-bold |
| 섹션 제목 | text-lg (18px) | font-semibold |
| 카드 숫자 | text-3xl (30px) | font-bold |
| 테이블 헤더 | text-sm (14px) | font-semibold |
| 테이블 본문 | text-sm (14px) | font-normal |
| 배지 텍스트 | text-xs (12px) | font-medium |
7-3. 레이아웃 원칙
- 사이드바: 240px 고정폭, 배경
#F9FAFB - 콘텐츠 영역: 나머지 공간
flex-1 - 카드 그리드:
grid-cols-2 sm:grid-cols-4(요약 카드) - 승인 화면: 좌측 목록
w-[360px]+ 우측 미리보기flex-1(2컬럼 분할) - 간격:
gap-4(카드 간격),p-6(컨텐츠 패딩)
리뷰 로그
[uiux-design-pl 초안 작성] 2026-02-25 20:15
- 기존 대시보드 현황 분석 완료: 블로그 홈 + 포스트 페이지 + API 7개 (approve/reject/content 포함)
- Phase 1 MVP 필수 화면 4개 설계: 파이프라인 홈(/pipeline), 콘텐츠 승인(/pipeline/review), 실행 이력(/pipeline/logs), 에러 현황(/pipeline/errors)
- 콘텐츠 승인 화면 상세 설계 (가장 중요): 목록+미리보기 2컬럼 레이아웃, 승인/거부 인터랙션 플로우, 상태 배지 6종 정의
- 신규 API 5개 명세: content/[id] 상세, stats 통계, logs 목록, errors 목록, errors/[id]/resolve
- 기존 API 4개 연계 확인: approve/reject는 이미 구현 완료 (프론트엔드 UI만 필요)
- 컴포넌트 구조: pipeline/ 디렉토리 기반 4페이지 + pipeline/components 12개
- Phase별 구현 범위: Phase 1(MVP 승인UI), Phase 2(배포현황+자동새로고침+수동트리거), Phase 3(DAG시각화+성과+인증)
- 디자인 가이드라인: 기존 블로그 CSS 변수 재사용 + 관리 화면 추가 색상 5종
- 자비스 1차 검수 요청
[자비스 검수] 2026-02-25T21:30:00+09:00
- ✅ 기존 API 4개 재사용 확인: approve/reject/content/cron 구현 완료, 프론트엔드 UI만 없는 상태 명확히 진단
- ✅ 신규 API 5개 명세 완비: content/[id] 상세, stats 통계, logs 목록, errors 목록, errors/[id]/resolve — 입출력 JSON 명세까지 포함
- ✅ Phase 1 MVP 제한사항 명확: 인증 없음(Vercel Preview URL만), optimistic update 없음(재조회 방식), 실시간 없음(수동 새로고침) — MVP 원칙 준수
- ✅ 승인 화면 핵심 설계 완료: 2컬럼(목록+미리보기), 거부 모달(사유 입력), 자동 발행 연계(autoPublish 응답 확인), 상태 배지 6종 정의
- ✅ 컴포넌트 12개 역할 분리: sidebar/stat-card/status-badge/content-list/content-preview/approve-actions/log-table/error-table/error-escalated/filter-bar/pagination/toast
- ✅ 기존 CSS 변수 재사용: 블로그 디자인 일관성 유지, 추가 색상 5종(성공/경고/에러/정보/배포) 명시
- ✅ 에스컬레이션 우선 표시: error_logs.escalated=1을 최상단 빨간 배경 섹션으로 강조 — CEO 즉시 식별 가능
- ✅ Phase 2/3 로드맵: 배포현황/자동새로고침/수동트리거/채널관리 → DAG시각화/성과대시보드/인증 단계적 확장 명시
- ✅ DB 테이블 매핑 명확: content_queue + pipeline_logs + error_logs 세 테이블 연계 일관성 확인
- 검수 결론: 승인 요청. VP 최종 승인 후 Phase 2 UI/UX L2 구현 플랜 착수 권장