← 목록으로
2026-02-25plans

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.xmlRSS 피드활성
/ogOpen 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/pipelineVercel 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_newsCOUNT(*) WHERE created_at >= 오늘 00:00 KST
검수 대기content_queueCOUNT(*) WHERE status IN ('draft', 'reviewing')
오늘 발행content_queueCOUNT(*) WHERE status = 'published' AND updated_at >= 오늘 00:00
미해결 에러error_logsCOUNT(*) 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/contentcontent_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_logsCOUNT(*) WHERE created_at >= 기간
성공률pipeline_logsCOUNT(status='completed') / COUNT(*) * 100
평균 소요pipeline_logsAVG(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 필드
collectfeeds_ok/feeds_fail, filter_pass
generatepillar, qa_score, content_type
approveapproved_by, content_id
publishchannels_ok/channels_fail
self-healingfixed/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_namestringnull단계 필터 (collect/generate/...)
statusstringnull상태 필터 (completed/failed/started)
daysnumber7기간 필터 (최근 N일)
pagenumber1페이지 번호
limitnumber20페이지 당 건수

상태 표시

상태화면 표시
로딩테이블 영역 스켈레톤
에러"실행 이력을 불러올 수 없습니다" + 재시도
빈 화면"아직 파이프라인이 실행되지 않았습니다. 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_logsCOUNT(*) WHERE resolved_at IS NULL
에스컬레이션error_logsCOUNT(*) WHERE escalated = 1 AND resolved_at IS NULL
자동교정 성공률error_logsCOUNT(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 쿼리 파라미터:

파라미터타입기본값설명
resolvedstring'unresolved''unresolved' / 'resolved' / 'all'
componentstringnull컴포넌트 필터
error_typestringnull에러 유형 필터
pagenumber1페이지 번호
limitnumber20페이지 당 건수

상태 표시

상태화면 표시
로딩테이블 영역 스켈레톤
에러 (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

#메서드경로화면역할
1GET/api/pipeline/content/[id]승인 화면개별 콘텐츠 상세 (content_body 포함)
2GET/api/pipeline/stats요약 통계 (수집/검수대기/발행/에러 집계)
3GET/api/pipeline/logs이력 화면실행 로그 목록 (필터, 페이지네이션)
4GET/api/pipeline/errors에러 화면에러 로그 목록 (필터, 페이지네이션)
5POST/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.tsxstatus 문자열을 받아 색상 배지로 렌더링. content_queue, pipeline_logs 공용승인, 이력
content-list.tsxcontent_queue 목록 렌더링. 선택 상태 관리. 필터 탭 포함승인
content-preview.tsx메타데이터 + content_body 마크다운 렌더링. 읽기 전용승인
approve-actions.tsx승인/거부 버튼. 거부 시 사유 입력 모달. API 호출 + 결과 처리승인
log-table.tsxpipeline_logs 테이블 렌더링. 행 클릭 시 상세 모달이력
error-table.tsxerror_logs 테이블 렌더링. 자동교정 결과 배지 포함에러
error-escalated.tsx에스컬레이션 에러 카드. 빨간 배경. 권장 조치 문구 포함에러
filter-bar.tsx탭 형태 필터. 값 변경 시 콜백 호출승인, 이력, 에러
pagination.tsxpage, 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 + 기본 대시보드

필수 구현:

#구현 항목상세
1app/pipeline/layout.tsx파이프라인 전용 레이아웃 (사이드바 포함)
2app/pipeline/page.tsx홈 — 요약 카드 4개 + 최근 실행 5건 + 검수 대기 미리보기
3app/pipeline/review/page.tsx콘텐츠 승인 — 목록 + 미리보기 + 승인/거부 버튼 (핵심)
4app/pipeline/logs/page.tsx실행 이력 — 로그 테이블 + 필터 + 페이지네이션
5app/pipeline/errors/page.tsx에러 현황 — 에스컬레이션 우선 표시 + 에러 테이블
6api/pipeline/content/[id]/route.ts개별 콘텐츠 상세 조회 API
7api/pipeline/stats/route.ts요약 통계 API
8api/pipeline/logs/route.ts실행 로그 목록 API
9api/pipeline/errors/route.ts에러 로그 목록 API
10api/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 테이블 기반, 채널별 배포 상태
2Optimistic Update승인/거부 시 즉시 UI 반영 (SWR mutation)
3자동 새로고침30초 간격 polling으로 최신 데이터 자동 반영
4파이프라인 수동 트리거"수집 실행" / "생성 실행" 버튼 (대시보드에서 직접 API 호출)
5채널 관리 화면 (/pipeline/channels)channels 테이블 CRUD (활성/비활성 전환)
6자체교정 대시보드최근 7일 교정 성공률, 등급별 분포
7모바일 반응형사이드바 -> 하단 탭바, 승인 화면 1컬럼
8Telegram 알림 연동검수 요청 시 CEO 텔레그램 알림 (review 화면에서 알림 상태 표시)

Phase 3 (3주+): 고급 기능

#구현 항목상세
1DAG 시각화파이프라인 전 과정을 시각적 플로우 차트로 표시
2성과 대시보드발행 후 조회수/오픈율/클릭 통계 (content_distributions.metrics)
3AI 파라미터 관리 UIcontent_generation_config 편집 (모델, 온도, 프롬프트)
4자동 승인 타이머 설정N시간 무응답 시 자동 승인 (설정 화면)
5인증/권한관리자 로그인, 역할 기반 접근 제어
6다크 모드시스템 설정 연동
7optimization_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 구현 플랜 착수 권장
plans/2026/02/25/content-orchestration-design-uiux.md