Social Dashboard API
External Analysis API + Insight API (외부 LLM/MCP 연동용 읽기 전용 API)
변경 이력
| 버전 | 날짜 | 변경 사항 |
|---|---|---|
| v1.4 | 2026-04-22 |
|
| v1.3 | 2026-04-20 |
|
| v1.2 | 2026-04-13 |
|
| v1.1 | 2026-04-12 |
|
| v1.0 | 2026-03-20 | 최초 릴리즈 — External pending/analysis API, 이미지·릴스 분석 파이프라인, 빠른 테스트 UI |
데이터 흐름
인증
모든 /api/external/* 엔드포인트는 X-Api-Key 헤더로 인증합니다.
| 헤더 | 값 | 비고 |
|---|---|---|
X-Api-Key |
EXTERNAL_API_KEY 환경변수 값 |
환경변수 미설정 시 인증 스킵 (개발 모드) |
curl -H "X-Api-Key: your-secret-key" https://threads.pyrocnc.co.kr/api/external/pending
GET /api/external/pending
캡처 완료(capture_status='done') 상태이면서 아직 분석되지 않은 포스트 목록을 반환합니다.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
platform | string? | null | instagram 또는 threads. 미지정 시 양쪽 모두 조회 |
analysis_type | string | reel | reel 또는 image. 어떤 분석 기준으로 미분석을 판별할지 |
limit | int | 200 | 최대 반환 수 (1~1000) |
Response
{
"success": true,
"data": [
{
"id": 42,
"platform": "instagram",
"post_id": "Cxyz123abc",
"author": "user_handle",
"text": "게시물 텍스트 (최대 200자)...",
"post_type": "Reels",
"screenshot_paths": "instagram/user_handle/screenshot_1.jpg",
"video_path": "instagram/user_handle/video.mp4",
"media_url": "https://...",
"likes": 1234,
"comments": 56,
"views": 78900,
"posted_at": "2025-03-15T12:00:00",
"captured_at": "2025-03-16T03:22:10",
"transcript": "[0.0s-2.5s] 오늘 소개할 레시피는...\n[2.5s-5.0s] 재료를 준비해주세요",
"video_duration": 28.5,
"scene_count": 7,
"cuts_per_second": 0.25,
"frame_paths": [
"instagram/Cxyz123abc/frames/frame_000.0s.jpg",
"instagram/Cxyz123abc/frames/frame_002.0s.jpg",
"instagram/Cxyz123abc/frames/frame_004.0s.jpg"
]
}
],
"message": "15 pending posts"
}
Response 필드
| 필드 | 타입 | 설명 |
|---|---|---|
id | int | DB 기본키 (분석 업로드 시 path parameter로 사용) |
platform | string | instagram 또는 threads |
post_id | string | 외부 포스트 ID (분석 업로드 시 post_ext_id로 교차 검증) |
author | string | 게시물 작성자 핸들 |
text | string | 게시물 텍스트 (최대 200자) |
post_type | string? | 게시물 유형 (Reels, Post, Carousel 등) |
screenshot_paths | string? | 스크린샷 경로 (JSON 배열 문자열) |
video_path | string? | 영상 파일 경로 |
media_url | string? | 원본 미디어 URL |
likes | int | 좋아요 수 |
comments | int | 댓글 수 |
views | int | 조회수 |
posted_at | string? | 게시 시각 (ISO 8601) |
captured_at | string? | 캡처 완료 시각 (ISO 8601) |
transcript |
string? | [v1.1] 서버 Whisper STT 전사 결과. 타임스탬프 형식 [0.0s-2.5s] 텍스트. 전사 미수행 시 null. 있으면 외부 분석기에서 별도 STT 불필요 |
video_duration |
float? | [v1.2] 영상 길이(초). ffprobe로 추출. 이미지 포스트는 null |
scene_count |
int? | [v1.2] 씬 전환 횟수 (ffmpeg scene=0.3 기준). 이미지 포스트는 null |
cuts_per_second |
float? | [v1.2] 초당 컷 수 (scene_count / video_duration). 영상 페이스 지표 |
frame_paths |
string[]? | [v1.2] 타임스탬프 프레임 경로 배열. 파일명 형식 frame_002.0s.jpg. GET /captures/{path}로 다운로드. 프레임 미추출 시 null. 0.5fps, 384px, 최대 30장 |
cURL 예시
# 릴스 미분석 포스트 (Instagram만) curl "https://threads.pyrocnc.co.kr/api/external/pending?platform=instagram&analysis_type=reel&limit=50" # 이미지 미분석 포스트 (전체) curl "https://threads.pyrocnc.co.kr/api/external/pending?analysis_type=image"
GET /captures/{path}
캡처된 스크린샷/영상 파일을 다운로드합니다. 기존 StaticFiles 마운트를 그대로 사용합니다.
pending 응답의 screenshot_paths 또는 video_path 값을 경로로 사용합니다.
cURL 예시
# 스크린샷 다운로드 curl -O "https://threads.pyrocnc.co.kr/captures/instagram/user_handle/screenshot_1.jpg" # 영상 다운로드 curl -O "https://threads.pyrocnc.co.kr/captures/instagram/user_handle/video.mp4"
POST /api/actions/backfill-transcripts v1.1
캡처 완료된 영상 중 transcript가 없는 건을 일괄 Whisper 전사합니다. 비동기 실행되며 상태는 별도 엔드포인트에서 폴링합니다.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
platform | string | instagram | instagram 또는 threads |
Response
{
"success": true,
"data": null,
"message": "스크립트 백필이 시작되었습니다"
}
cURL 예시
curl -X POST "https://threads.pyrocnc.co.kr/api/actions/backfill-transcripts?platform=instagram"
GET /api/actions/backfill-transcripts-status v1.1
백필 작업의 진행 상황을 조회합니다. 프론트엔드에서 2초 간격으로 폴링합니다.
Response
{
"success": true,
"data": {
"running": true,
"total": 45,
"done": 12,
"current_post_id": 1234,
"errors": []
},
"message": ""
}
Response 필드
| 필드 | 타입 | 설명 |
|---|---|---|
running | bool | 현재 실행 중 여부 |
total | int | 전사 대상 총 건수 |
done | int | 완료된 건수 |
current_post_id | int? | 현재 처리 중인 포스트 DB ID |
errors | string[] | 오류 메시지 목록 |
POST /api/actions/backfill-frames v1.2
캡처 완료된 영상 중 프레임이 추출되지 않은 건을 일괄 처리합니다. 0.5fps로 384px JPEG 프레임을 추출하고 비디오 메타데이터(duration, scene_count, cuts_per_second)도 함께 저장합니다.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
platform | string | instagram | instagram 또는 threads |
Response
{
"success": true,
"data": null,
"message": "프레임 백필이 시작되었습니다"
}
cURL 예시
curl -X POST "https://threads.pyrocnc.co.kr/api/actions/backfill-frames?platform=instagram"
GET /api/actions/backfill-frames-status v1.2
프레임 백필 작업의 진행 상황을 조회합니다. 프론트엔드에서 2초 간격으로 폴링합니다.
Response
{
"success": true,
"data": {
"running": true,
"total": 30,
"done": 8,
"current_post_id": 567,
"errors": []
},
"message": ""
}
Response 필드
| 필드 | 타입 | 설명 |
|---|---|---|
running | bool | 현재 실행 중 여부 |
total | int | 프레임 추출 대상 총 건수 |
done | int | 완료된 건수 |
current_post_id | int? | 현재 처리 중인 포스트 DB ID |
errors | string[] | 오류 메시지 목록 |
POST /api/external/analysis/{post_db_id}
분석 결과를 DB에 저장합니다. post_db_id는 pending 응답의 id 필드입니다.
Path Parameters
| 파라미터 | 타입 | 설명 |
|---|---|---|
post_db_id | int | DB 기본키 (pending 응답의 id) |
Request Body
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
analysis_type | string | Y | "reel" 또는 "image" |
reel | object | 조건부 | analysis_type="reel" 시 필수 |
image | object | 조건부 | analysis_type="image" 시 필수 |
reel 객체 필드
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
post_ext_id | string | Y | 외부 포스트 ID (pending의 post_id와 일치해야 함) |
caption | string? | 캡션 텍스트 | |
transcript | string? | 음성 트랜스크립트 | |
duration_seconds | float? | 영상 길이 (초) | |
scene_count | int | 장면 수 (기본 0) | |
cuts_per_second | float | 초당 컷 수 (기본 0.0) | |
hook_analysis | string? | 훅 분석 결과 | |
structure_analysis | string? | 구조 분석 결과 | |
visual_pace_analysis | string? | 시각적 페이스 분석 | |
audio_trend_analysis | string? | 오디오 트렌드 분석 | |
cta_analysis | string? | CTA 분석 결과 | |
category_analysis | string? | 카테고리 분석 (JSON 문자열) | |
overall_score | float? | 종합 점수 (0~10) | |
overall_summary | string? | 종합 요약 | |
raw_ai_response | string? | 원본 AI 응답 전문 |
image 객체 필드
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
post_ext_id | string | Y | 외부 포스트 ID (pending의 post_id와 일치해야 함) |
platform | string | 플랫폼 (기본 "instagram") | |
caption | string? | 캡션 텍스트 | |
image_count | int | 이미지 수 (기본 0) | |
content_summary | string? | 콘텐츠 요약 | |
extracted_text | string? | 이미지에서 추출한 텍스트 | |
visual_composition | string? | 시각적 구성 분석 | |
category_analysis | string? | 카테고리 분석 (JSON 문자열) | |
overall_score | float? | 종합 점수 (0~10) | |
overall_summary | string? | 종합 요약 | |
raw_ai_response | string? | 원본 AI 응답 전문 |
Response
{
"success": true,
"data": { "post_db_id": 42, "analysis_type": "image" },
"message": "Analysis saved successfully"
}
에러 응답
| 코드 | 상황 | 예시 |
|---|---|---|
401 | API 키 불일치 | {"detail": "Invalid or missing API key"} |
404 | post_db_id가 DB에 없음 | {"detail": "Post with id=999 not found"} |
400 | post_ext_id 불일치 | {"detail": "post_ext_id mismatch: sent 'abc', DB has 'xyz'"} |
422 | 필수 필드 누락 / 잘못된 analysis_type | {"detail": "reel field is required when analysis_type='reel'"} |
cURL 예시
curl -X POST "https://threads.pyrocnc.co.kr/api/external/analysis/42" \
-H "Content-Type: application/json" \
-H "X-Api-Key: your-secret-key" \
-d '{
"analysis_type": "reel",
"reel": {
"post_ext_id": "Cxyz123abc",
"hook_analysis": "첫 1초에 강렬한 비주얼 훅...",
"structure_analysis": "도입-전개-CTA 3단 구성...",
"visual_pace_analysis": "빠른 컷 전환, 평균 0.8초...",
"audio_trend_analysis": "트렌딩 BGM 사용...",
"cta_analysis": "팔로우 유도 CTA 명확...",
"category_analysis": "{\"primary_category\": \"뷰티/패션\"}",
"overall_score": 8.5,
"overall_summary": "높은 완성도의 뷰티 릴스..."
}
}'
curl -X POST "https://threads.pyrocnc.co.kr/api/external/analysis/42" \
-H "Content-Type: application/json" \
-H "X-Api-Key: your-secret-key" \
-d '{
"analysis_type": "image",
"image": {
"post_ext_id": "Cxyz123abc",
"platform": "instagram",
"content_summary": "제품 리뷰 카드뉴스 형식...",
"extracted_text": "BEST 5 추천 아이템...",
"visual_composition": "깔끔한 그리드 레이아웃...",
"category_analysis": "{\"primary_category\": \"뷰티/패션\"}",
"overall_score": 7.8,
"overall_summary": "정보성 높은 카드뉴스..."
}
}'
POST /api/actions/analyze-carousels v1.3
캐러셀 게시물에 대해 Claude Vision 심층 분석을 시작합니다. 비동기 실행되며 상태는 별도 엔드포인트에서 폴링합니다. 5개 영역 분석: 슬라이드별 OCR, 서사 구조, 훅, 디자인, 종합 평가.
Request Body
| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
post_ids | int[]? | null | 분석할 포스트 DB ID 목록. null이면 캡처된 전체 캐러셀 대상 (최대 200건) |
platform | string | "instagram" | instagram 또는 threads |
Response
{
"success": true,
"data": null,
"message": "캐러셀 분석이 시작되었습니다"
}
cURL 예시
# 특정 포스트 분석
curl -X POST "https://threads.pyrocnc.co.kr/api/actions/analyze-carousels" \
-H "Content-Type: application/json" \
-d '{"post_ids": [101, 102, 103], "platform": "instagram"}'
# 전체 캐러셀 일괄 분석
curl -X POST "https://threads.pyrocnc.co.kr/api/actions/analyze-carousels" \
-H "Content-Type: application/json" \
-d '{"platform": "instagram"}'
GET /api/actions/analyze-carousels-status v1.3
캐러셀 분석 작업의 진행 상황을 조회합니다. 프론트엔드에서 2초 간격으로 폴링합니다.
Response
{
"success": true,
"data": {
"running": true,
"total": 15,
"done": 5,
"current_post_id": 101,
"current_step": "analyzing",
"errors": []
},
"message": ""
}
Response 필드
| 필드 | 타입 | 설명 |
|---|---|---|
running | bool | 현재 실행 중 여부 |
total | int | 분석 대상 총 건수 |
done | int | 완료된 건수 |
current_post_id | int? | 현재 처리 중인 포스트 DB ID |
current_step | string | 현재 단계 (preparing, loading_images, analyzing) |
errors | string[] | 오류 메시지 목록 |
GET /api/data/carousel-ranking v1.3
Instagram + Threads 캐러셀 게시물의 통합 우선순위 랭킹을 반환합니다. virality(50%) + recency(50%) 복합 점수, 5분 인메모리 캐시.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
limit | int | 100 | 최대 반환 수 (1~5000) |
Response
{
"success": true,
"data": [
{
"id": 42,
"post_id": "Cxyz123abc",
"author": "user_handle",
"text": "캐러셀 캡션 텍스트 (최대 200자)...",
"likes": 1234,
"comments": 56,
"views": 78900,
"reposts": 12,
"posted_at": "2026-04-15T12:00:00",
"platform": "instagram",
"post_url": "https://www.instagram.com/p/Cxyz123abc/",
"priority_score": 72.5,
"grade": "A",
"virality_score": 65.0,
"recency_score": 80.0,
"days_ago": 5.2,
"slide_count": 8,
"thumb_url": "https://...",
"capture_status": "done"
}
],
"message": ""
}
Response 필드
| 필드 | 타입 | 설명 |
|---|---|---|
priority_score | float | 복합 점수 (0-100). virality 50% + recency 50% |
grade | string | 등급: A(≥70), B(≥55), C(≥40), D(<40) |
virality_score | float | 바이럴리티 점수 (percentile 기반 0-100) |
recency_score | float | 최신성 점수 (지수 감소, 14일 반감기) |
days_ago | float | 게시 후 경과일 |
slide_count | int | 캐러셀 슬라이드 수 |
thumb_url | string | 첫 번째 이미지 URL (썸네일용) |
capture_status | string? | 캡처 상태 (done/null) |
스코어링 알고리즘
| 요소 | 비중 | 산출 방식 |
|---|---|---|
| Virality | 50% | Instagram: views / (likes + comments), Threads: reposts*5 + comments*3 + likes → percentile 변환 |
| Recency | 50% | 100 * exp(-0.693 * days / 14) (14일 반감기 지수 감소) |
cURL 예시
curl "https://threads.pyrocnc.co.kr/api/data/carousel-ranking?limit=50"
GET /api/data/carousel-detail/{post_id} v1.3
개별 캐러셀 게시물의 전체 media_url과 text를 반환합니다. 상세 모달에서 사용합니다.
Path Parameters
| 파라미터 | 타입 | 설명 |
|---|---|---|
post_id | string | 외부 포스트 ID (예: Cxyz123abc) |
Response
{
"success": true,
"data": {
"media_url": "[\"https://...\", \"https://...\", ...]",
"text": "전체 캡션 텍스트..."
},
"message": ""
}
cURL 예시
curl "https://threads.pyrocnc.co.kr/api/data/carousel-detail/Cxyz123abc"
GET /api/data/carousel-analyses v1.3
완료된 캐러셀 분석 결과 목록을 게시물 메타데이터와 함께 반환합니다.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
platform | string? | null | instagram 또는 threads. 미지정 시 양쪽 모두 |
limit | int | 500 | 최대 반환 수 (1~2000) |
Response
{
"success": true,
"data": {
"analyses": [
{
"id": 1,
"post_id": 42,
"post_ext_id": "Cxyz123abc",
"platform": "instagram",
"caption": "...",
"slide_count": 8,
"slide_ocr": "[{\"slide\":1, \"texts\":[...], \"role\":\"표지\"}]",
"narrative_analysis": "{\"score\":8, \"type\":\"문제→해결\", ...}",
"hook_analysis": "{\"score\":7, \"hook_type\":\"질문형\", ...}",
"design_analysis": "{\"score\":9, \"color_palette\":\"...\", ...}",
"category_analysis": "{\"primary_category\":\"교육\", ...}",
"overall_score": 8.0,
"overall_summary": "{\"score\":8, \"strengths\":[...], ...}",
"analysis_status": "done",
"created_at": "2026-04-20T15:59:00",
"author": "user_handle",
"likes": 1234,
"comments": 56,
"views": 78900,
"post_url": "https://..."
}
],
"total": 25
},
"message": ""
}
cURL 예시
# 전체 캐러셀 분석 결과 curl "https://threads.pyrocnc.co.kr/api/data/carousel-analyses?limit=100" # Instagram만 curl "https://threads.pyrocnc.co.kr/api/data/carousel-analyses?platform=instagram"
GET /api/data/carousel-analysis/{post_db_id} v1.3
특정 게시물의 캐러셀 분석 상세 결과를 반환합니다.
Path Parameters
| 파라미터 | 타입 | 설명 |
|---|---|---|
post_db_id | int | 포스트 DB 기본키 |
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
platform | string? | null | 플랫폼 필터 (미지정 시 전체 검색) |
Response
{
"success": true,
"data": {
"id": 1,
"post_id": 42,
"post_ext_id": "Cxyz123abc",
"platform": "instagram",
"slide_count": 8,
"slide_ocr": "[...]",
"narrative_analysis": "{\"score\":8, ...}",
"hook_analysis": "{\"score\":7, ...}",
"design_analysis": "{\"score\":9, ...}",
"category_analysis": "{...}",
"overall_score": 8.0,
"overall_summary": "{...}",
"raw_ai_response": "{...}",
"analysis_status": "done",
"created_at": "2026-04-20T15:59:00"
},
"message": ""
}
에러 응답
{"success": false, "data": null, "message": "분석 결과가 없습니다"}
cURL 예시
curl "https://threads.pyrocnc.co.kr/api/data/carousel-analysis/42?platform=instagram"
DELETE /api/data/carousel-analyses v1.3
지정한 포스트의 캐러셀 분석 결과를 삭제합니다.
Request Body
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
ids | int[] | Y | 삭제할 포스트 DB ID 목록 |
platform | string | 플랫폼 (기본 "instagram", 현재 미사용) |
Response
{
"success": true,
"data": {"deleted": 3},
"message": "3개 캐러셀 분석 결과 삭제"
}
cURL 예시
curl -X DELETE "https://threads.pyrocnc.co.kr/api/data/carousel-analyses" \
-H "Content-Type: application/json" \
-d '{"ids": [42, 43, 44]}'
빠른 테스트
Insight API 개요
외부 LLM이 소셜 대시보드의 릴스, 캐러셀, 이미지 분석 데이터를 참조하여 콘텐츠 기획에 활용할 수 있도록 하는 읽기 전용 API입니다.
두 인터페이스 모두 동일한 데이터를 제공합니다. REST API는 범용 클라이언트용, MCP는 LLM 도구 연동용입니다.
인증
/api/insights/* 엔드포인트는 External API와 동일하게 X-Api-Key 헤더로 인증합니다.
| 헤더 | 값 | 비고 |
|---|---|---|
X-Api-Key |
EXTERNAL_API_KEY 환경변수 값 |
환경변수 미설정 시 인증 스킵 (개발 모드) |
curl -H "X-Api-Key: your-secret-key" https://your-host/api/insights/summary
공통 응답 형식
모든 엔드포인트는 동일한 ApiResponse 래퍼를 사용합니다. 대용량 필드 (raw_json, raw_ai_response)는 자동 제거됩니다.
{
"success": true,
"data": { ... },
"message": ""
}
GET /api/insights/summary
대시보드 전체 현황 요약. 수집 건수, 활성 키워드, 분석 건수(릴스/이미지/캐러셀) 등.
Query Parameters
없음
Response
{
"success": true,
"data": {
"total_posts": 12039,
"instagram_posts": 10771,
"threads_posts": 1268,
"active_keywords": 37,
"today_collected": 0,
"reel_analyses": 933,
"image_analyses": 407,
"carousel_analyses": 144
}
}
GET /api/insights/posts
수집된 Instagram/Threads 포스트를 검색합니다.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
platform | string | instagram | instagram 또는 threads |
keyword | string? | null | 수집 키워드로 필터 |
search | string? | null | 텍스트, 작성자, 키워드, 해시태그에서 검색 |
limit | int | 50 | 최대 반환 수 (1~500) |
offset | int | 0 | 페이지네이션 오프셋 |
Response 주요 필드
| 필드 | 타입 | 설명 |
|---|---|---|
id | int | DB 기본키 |
platform | string | instagram / threads |
author | string | 작성자 핸들 |
text | string | 게시물 텍스트 |
likes, comments, views | int | 참여 지표 |
post_type | string? | Post / Reels / Carousel |
hashtags | string? | 해시태그 목록 |
posted_at | string | 게시 시각 |
GET /api/insights/reels-ranking
Instagram 릴스를 참여도 종합 점수로 랭킹. virality(45%) + recency(35%) + richness(20%) 가중 합산.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
limit | int | 100 | 최대 반환 수 (1~1000) |
min_grade | string? | null | A, B, C, D — 최소 등급 필터 |
Response 주요 필드
| 필드 | 타입 | 설명 |
|---|---|---|
priority_score | float | 종합 점수 (0~100) |
grade | string | A (80+) / B (60+) / C (40+) / D |
virality_score | float | 바이럴 점수 (조회 대비 반응률) |
recency_score | float | 최신성 점수 (지수 감쇠) |
richness_score | float | 콘텐츠 풍부도 (해시태그, 음악 등) |
GET /api/insights/threads-ranking
Threads 포스트 참여도 랭킹. virality(60%) + recency(40%) 가중 합산.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
limit | int | 100 | 최대 반환 수 (1~1000) |
min_grade | string? | null | 최소 등급 필터 |
응답 형식은 reels-ranking과 동일 (virality_score, recency_score, priority_score, grade)
GET /api/insights/carousel-ranking
Instagram + Threads 캐러셀/카드뉴스 통합 랭킹. virality(50%) + recency(50%) 합산 후 양 플랫폼 합산 정렬.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
limit | int | 100 | 최대 반환 수 (1~1000) |
min_grade | string? | null | 최소 등급 필터 |
응답에 platform (instagram/threads), slide_count 추가 포함
GET /api/insights/reel-analyses
완료된 릴스 AI 분석 결과. 훅, 구조, 시각 페이스, CTA, 카테고리 분석과 종합 점수 포함.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
topic | string? | null | 캡션/분석 텍스트에서 키워드 검색 |
category | string? | null | 카테고리 필터 (예: 푸드/요리) |
min_grade | string? | null | 최소 등급 (A=8+, B=7+, C=5+) |
limit | int | 100 | 최대 반환 수 (1~500) |
Response 주요 필드
| 필드 | 타입 | 설명 |
|---|---|---|
hook_analysis | JSON string | 첫 3초 훅 분석 (점수, 유형, 시각적 훅) |
structure_analysis | JSON string | 영상 구조 분석 (포맷, 페이싱, 서사 흐름) |
visual_pace_analysis | JSON string | 시각 페이스 분석 (장면 전환, 리듬, 구도) |
cta_analysis | JSON string | CTA 분석 (유형, 배치, 효과) |
category_analysis | JSON string | 카테고리 분류 (주/부 카테고리, 타겟) |
overall_score | float | 종합 점수 (0~10) |
overall_summary | string | 종합 요약문 |
transcript | string? | 음성 전사 텍스트 |
GET /api/insights/image-analyses
완료된 이미지 포스트 AI 분석 결과.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
platform | string? | null | instagram / threads 필터 |
topic | string? | null | 키워드 검색 |
category | string? | null | 카테고리 필터 |
min_grade | string? | null | 최소 등급 |
limit | int | 100 | 최대 반환 수 (1~500) |
Response 주요 필드
| 필드 | 타입 | 설명 |
|---|---|---|
content_summary | string | 콘텐츠 요약 |
extracted_text | string? | 이미지 내 텍스트 추출 |
visual_composition | string? | 시각 구도 분석 |
category_analysis | JSON string | 카테고리 분류 |
overall_score | float | 종합 점수 (0~10) |
overall_summary | string | 종합 요약문 |
GET /api/insights/carousel-analyses
완료된 캐러셀/카드뉴스 AI 분석 결과.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
platform | string? | null | instagram / threads 필터 |
limit | int | 100 | 최대 반환 수 (1~500) |
Response 주요 필드
| 필드 | 타입 | 설명 |
|---|---|---|
slide_count | int | 슬라이드 수 |
slide_ocr | string? | 슬라이드별 OCR 텍스트 |
narrative_analysis | string? | 서사 구조 분석 |
hook_analysis | string? | 훅 분석 |
design_analysis | string? | 디자인 분석 |
overall_score | float | 종합 점수 (0~10) |
overall_summary | string | 종합 요약문 |
GET /api/insights/categories
분석에서 감지된 콘텐츠 카테고리 목록. 분석 결과 필터링에 활용.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
content_type | string | reel | reel 또는 image |
Response
{
"success": true,
"data": ["가족/육아", "건강/피트니스", "게임", "교육", "뉴스/시사", "라이프스타일", "마케팅", "뷰티/패션", "여행/관광", "유머/엔터테인먼트", "음악/K-POP", "직장/비즈니스", "테크/개발", "푸드/요리", ...]
}
GET /api/insights/hashtag-stats
해시태그 성과 통계. 사용 횟수, 평균 참여 지표, 랭크 점수.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
platform | string | instagram | instagram / threads |
limit | int | 50 | 최대 반환 수 (1~200) |
Response 주요 필드
| 필드 | 타입 | 설명 |
|---|---|---|
tag | string | 해시태그 |
count | int | 사용 횟수 |
avg_likes | float | 평균 좋아요 |
avg_comments | float | 평균 댓글 |
avg_views | float | 평균 조회수 |
rank_score | float | 종합 랭크 점수 |
GET /api/insights/content-filter-history
콘텐츠 필터 워크플로우 이력. 검색 조건, 패턴 분석, 브랜드 파라미터, 생성된 제안 포함.
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
limit | int | 30 | 최대 반환 수 (1~100) |
history_id | int? | null | 특정 이력 상세 조회 (지정 시 단건 반환) |
Response (목록)
{
"success": true,
"data": [
{
"id": 15,
"platform": "instagram",
"search_topic": "맛집",
"search_category": "푸드/요리",
"brand_name": "MyBrand",
"search_post_count": 8,
"created_at": "2026-04-20 14:30:00"
}
]
}
Response (상세 — history_id 지정 시)
추가 필드: search_posts_json, analysis_json, proposals_json (JSON 파싱된 객체), target_audience, key_message, content_purpose, tone, duration, slide_count, notes
MCP Server (포트 8004)
FastMCP 기반 SSE 서버로, Claude Code 등 MCP 클라이언트에서 tool로 직접 호출할 수 있습니다.
연결 설정
{
"mcpServers": {
"social-dashboard": {
"url": "http://YOUR_HOST:8004/sse"
}
}
}
제공 Tools (12개)
| Tool | Parameters | 설명 |
|---|---|---|
get_dashboard_summary | — | 전체 현황 요약 |
search_posts | platform, keyword, search, limit | 포스트 검색 |
get_reels_ranking | limit, min_grade | 릴스 랭킹 |
get_threads_ranking | limit, min_grade | 스레드 랭킹 |
get_carousel_ranking | limit, min_grade | 캐러셀 랭킹 |
get_reel_analyses | topic, category, min_grade, limit | 릴스 분석 결과 |
get_image_analyses | platform, topic, category, min_grade, limit | 이미지 분석 결과 |
get_carousel_analyses | platform, limit | 캐러셀 분석 결과 |
get_categories | content_type | 카테고리 목록 |
get_hashtag_stats | platform, limit | 해시태그 통계 |
get_content_filter_history | limit, history_id | 콘텐츠 필터 이력 |
query_db | sql, db_name | 읽기 전용 SQL (SELECT만) |
Live Test
분석 시스템 개요
Social Dashboard는 Claude Sonnet (claude-sonnet-4-20250514) 모델을 사용하여 소셜 미디어 콘텐츠를 자동 분석합니다. 이미지 분석, 캐러셀 분석, 릴스(동영상) 분석 세 가지 파이프라인으로 나뉩니다.
이미지 분석 파이프라인
처리 흐름
이미지 전처리 설정
| 설정 | 값 | 설명 |
|---|---|---|
MAX_IMAGE_WIDTH | 768px | 이미지 최대 가로 폭 (비율 유지 리사이즈) |
JPEG_QUALITY | 85% | JPEG 압축 품질 |
MAX_IMAGES | 20장 | 게시물당 최대 이미지 수 |
| 색상 모드 | RGB | RGBA/P 모드는 RGB로 자동 변환 |
| 인코딩 | Base64 | Claude API 전송을 위한 Base64 인코딩 |
Claude에 전달되는 컨텍스트
| 데이터 | 형식 | 설명 |
|---|---|---|
| 작성자 | 텍스트 | @username 형식의 작성자 핸들 |
| 이미지 수 | 숫자 | 게시물에 포함된 이미지 장수 |
| 좋아요/댓글/조회수 | 숫자 | 게시물 인게이지먼트 지표 |
| 캡션 | 텍스트 | 게시물 캡션 전문 |
| 해시태그 | 텍스트 | 게시물에 포함된 해시태그 |
| 이미지 | Base64 이미지 | 슬라이드 번호와 함께 순차적으로 전송 |
분석 항목
System Prompt (이미지 분석)
당신은 소셜 미디어 이미지 콘텐츠 분석 전문가입니다. 이 메시지에는 게시물의 이미지가 포함되어 있습니다. 캐러셀 게시물의 경우 여러 슬라이드가 순서대로 전달됩니다. 각 이미지를 직접 보고 다음을 분석하세요: - 이미지에 포함된 텍스트 (OCR) - 화면 구성 및 레이아웃 - 색감, 톤, 브랜드 일관성 - 텍스트 오버레이 디자인 - 시각적 계층 구조 - 콘텐츠 카테고리 및 타겟 오디언스 **캐러셀 게시물(2장 이상)의 경우 슬라이드 간 관계를 반드시 분석하세요:** - 내러티브/정보 흐름: 슬라이드가 어떤 순서와 논리로 연결되는지 - 디자인 일관성: 폰트, 색상, 레이아웃의 통일성 - 각 슬라이드의 역할: 표지/본문/요약/CTA 등
응답 JSON 구조
{
"content_summary": "이미지 콘텐츠에 대한 상세 요약 (2-3문장)",
"extracted_text": [
{
"slide": 1,
"text": "이미지 내 텍스트",
"position": "상단/중앙/하단",
"style": "제목/본문/CTA"
}
],
"visual_composition": {
"score": "1-10",
"layout": "레이아웃 분석",
"color_palette": "색감/톤 분석",
"brand_consistency": "브랜드 일관성",
"text_overlay_design": "텍스트 오버레이 디자인 품질",
"visual_hierarchy": "시각적 계층 구조",
"carousel_flow": "슬라이드 간 내러티브/정보 흐름",
"slide_relationship": "연결 방식 (순차적 스토리/주제별 분류/단계적 설명/대비 등)",
"design_consistency": "슬라이드 간 디자인 통일성",
"summary": "비주얼 구성 종합 평가"
},
"category": {
"primary_category": "주요 카테고리",
"sub_category": "세부 카테고리",
"target_audience": "타겟 오디언스",
"content_type": "정보형/감성형/홍보형/교육형/엔터테인먼트"
},
"overall": {
"score": "1-10",
"strengths": ["강점1", "강점2"],
"improvements": ["개선점1", "개선점2"],
"summary": "종합 분석 요약"
}
}
캐러셀 분석 파이프라인 v1.3
처리 흐름
이미지 전처리 설정
| 설정 | 값 | 설명 |
|---|---|---|
MAX_IMAGE_WIDTH | 768px | 슬라이드 최대 가로 폭 (비율 유지 리사이즈) |
JPEG_QUALITY | 85% | JPEG 압축 품질 |
MAX_SLIDES | 20장 | 캐러셀당 최대 슬라이드 수 |
| 색상 모드 | RGB | RGBA/P 모드는 RGB로 자동 변환 |
max_tokens | 6144 | Claude 응답 최대 토큰 (이미지 분석 4096보다 큼) |
분석 항목 (5개 영역)
System Prompt (캐러셀 분석)
당신은 소셜 미디어 캐러셀 콘텐츠 분석 전문가입니다. 이 메시지에는 캐러셀 게시물의 슬라이드 이미지가 순서대로 포함되어 있습니다. 각 슬라이드를 직접 보고 다음 5개 영역을 심층 분석하세요: 1. **슬라이드별 OCR**: 각 슬라이드의 텍스트, 위치, 스타일, 역할을 추출 2. **서사 구조**: 슬라이드 간 논리적 흐름, 서사 유형, 진행 구조 3. **훅 분석**: 첫 슬라이드의 스크롤 멈춤 효과, 훅 유형과 전략 4. **디자인 분석**: 색상, 타이포, 레이아웃, 일관성, 가독성 5. **종합 평가**: 전체 점수, 강점, 개선점
응답 JSON 구조
{
"slide_ocr": [
{
"slide": 1,
"texts": [
{"text": "추출된 텍스트", "position": "상단/중앙/하단", "style": "제목/본문/CTA/강조", "font_size": "대/중/소"}
],
"role": "표지/도입/전개/클라이맥스/요약/CTA"
}
],
"narrative": {
"score": "1-10",
"type": "문제→해결/순차정보/비교대조/Q&A/스토리텔링/튜토리얼",
"flow": "슬라이드 간 서사 흐름 상세 설명",
"progression": "도입→전개→마무리 구조 분석",
"coherence": "논리적 연결성 평가",
"summary": "서사 구조 종합 평가"
},
"hook": {
"score": "1-10",
"hook_type": "질문형/충격형/공감형/호기심유발/통계형/도발형",
"first_slide_analysis": "첫 슬라이드 상세 분석",
"visual_hook": "시각적 후킹 요소",
"text_hook": "텍스트 후킹 요소",
"scroll_stop_power": "스크롤 멈춤 효과 평가",
"summary": "훅 종합 평가"
},
"design": {
"score": "1-10",
"color_palette": "색상 팔레트 분석",
"typography": "타이포그래피 분석",
"layout": "레이아웃 패턴 분석",
"consistency": "슬라이드 간 디자인 일관성",
"visual_hierarchy": "시각적 계층 구조",
"readability": "가독성 평가",
"summary": "디자인 종합 평가"
},
"category": {
"primary_category": "주요 카테고리",
"sub_category": "세부 카테고리",
"target_audience": "타겟 오디언스",
"content_type": "정보형/감성형/홍보형/교육형/엔터테인먼트"
},
"overall": {
"score": "1-10",
"strengths": ["강점1", "강점2"],
"improvements": ["개선점1", "개선점2"],
"key_takeaway": "이 캐러셀의 핵심 전략",
"summary": "종합 분석 요약"
}
}
DB 스키마 (carousel_analyses 테이블)
| 컬럼 | 타입 | 설명 |
|---|---|---|
id | INTEGER PK | 자동 증가 기본키 |
post_id | INTEGER | 포스트 DB 기본키 |
post_ext_id | TEXT | 외부 포스트 ID |
platform | TEXT | instagram / threads |
caption | TEXT | 캡션 텍스트 |
slide_count | INTEGER | 슬라이드 수 |
slide_ocr | TEXT (JSON) | 슬라이드별 OCR 결과 |
narrative_analysis | TEXT (JSON) | 서사 구조 분석 |
hook_analysis | TEXT (JSON) | 훅 분석 |
design_analysis | TEXT (JSON) | 디자인 분석 |
category_analysis | TEXT (JSON) | 카테고리 분류 |
overall_score | REAL | 종합 점수 (0-10) |
overall_summary | TEXT (JSON) | 종합 평가 |
raw_ai_response | TEXT (JSON) | 원본 AI 응답 전문 |
analysis_status | TEXT | pending / processing / done / failed |
error | TEXT | 에러 메시지 (실패 시) |
created_at | TEXT | 생성 시각 |
updated_at | TEXT | 수정 시각 |
릴스(동영상) 분석 파이프라인
처리 흐름
Step 1: 오디오 추출 & 씬 감지 (ffmpeg)
| 처리 | 설정 | 설명 |
|---|---|---|
| 영상 길이 측정 | ffprobe | 영상 duration 추출 (timeout 30초) |
| 오디오 추출 | PCM 16kHz mono | ffmpeg -vn -acodec pcm_s16le -ar 16000 -ac 1 → WAV 파일 |
| 씬 감지 | threshold 0.3 | select='gt(scene,0.3)',showinfo 필터로 장면 전환점 검출 |
| 컷 속도 계산 | 자동 | scene_count / duration = cuts_per_second |
Step 2: 프레임 추출 설정
| 설정 | 값 | 설명 |
|---|---|---|
FRAMES_PER_SECOND | 0.5 fps | 2초당 1장 추출 (30초 영상 = 15장) |
FRAME_MAX_WIDTH | 384px | 프레임 최대 가로 폭 (비율 유지) |
FRAME_JPEG_QUALITY | 5 (ffmpeg q:v) | JPEG 품질 (2=최고, 31=최저, 5=분석용 적정) |
MAX_FRAMES | 30장 | 긴 영상도 최대 30장으로 제한 |
| 인코딩 | Base64 | Claude API 전송을 위한 Base64 인코딩 |
Step 3: 음성 전사 (STT)
| 설정 | 값 | 설명 |
|---|---|---|
| 모델 | faster-whisper base | 속도와 정확도의 균형. 싱글턴(transcriber.py)으로 로드 |
| 디바이스 | CPU | compute_type="int8" 양자화로 메모리 절감 |
| 빔 서치 | beam_size=5 | 전사 정확도 향상을 위한 빔 크기 |
| 출력 형식 | 타임스탬프 포함 | [0.0s-2.5s] 텍스트... 형식으로 출력 |
| 캡처 시 자동 전사 | v1.1 | 릴스 캡처 시 자동으로 STT 수행 → collected_posts.transcript에 저장 |
| 분석 시 재사용 | v1.1 | DB에 이미 transcript가 있으면 Whisper 스킵 (로그: "Using existing transcript") |
Step 4: Claude에 전달되는 컨텍스트
| 데이터 | 형식 | 설명 |
|---|---|---|
| 작성자 | 텍스트 | @username 형식의 작성자 핸들 |
| 영상 메타데이터 | 숫자 | 길이(초), 프레임 수, 씬 전환 수, 초당 컷 |
| 좋아요/댓글/조회수 | 숫자 | 게시물 인게이지먼트 지표 |
| 캡션 | 텍스트 | 릴스 하단 캡션 텍스트 |
| 해시태그 | 텍스트 | 게시물에 포함된 해시태그 |
| 음악 정보 | 텍스트 | 사용된 음악 제목/아티스트 |
| STT 전사 | 텍스트 | 타임스탬프 포함 음성 전사 결과 |
| 프레임 이미지 | Base64 이미지 | 타임스탬프별 프레임 (e.g. [0초], [2초], ...) |
분석 항목 (7개 영역)
System Prompt (릴스 분석)
당신은 Instagram Reels 콘텐츠 전략 분석 전문가입니다. 이 메시지에는 릴스 영상의 프레임 이미지(1초당 1장)가 포함되어 있습니다. 프레임 이미지를 직접 보고 다음을 반드시 분석하세요: - 화면 구성 및 레이아웃 (인물 위치, 배경, 구도) - 후킹 자막/텍스트 오버레이 (위치, 크기, 내용, 등장 타이밍) - 등장인물의 표정, 제스처, 시선 방향 - 컷 전환 패턴과 시각적 리듬 - 제품/브랜드 노출 요소 - 첫 1-3초(첫 3장 프레임)의 시각적 후킹 요소 텍스트 데이터(캡션, STT 전사, 메타데이터)와 프레임 이미지를 종합하여 분석하세요.
응답 JSON 구조
{
"hook": {
"score": "1-10",
"first_3_seconds": "첫 3초 화면 구성 + 음성/자막 분석",
"hook_type": "질문형/충격형/공감형/정보형/스토리형",
"text_overlay": "후킹 자막/텍스트 내용과 위치",
"visual_hook": "시각적 후킹 요소",
"attention_grab": "종합적 시선 끌기 요소",
"summary": "Hook 평가 요약"
},
"structure": {
"score": "1-10",
"format": "정보 전달/스토리텔링/튜토리얼/브이로그/챌린지/리뷰",
"pacing": "빠름/보통/느림",
"narrative_flow": "프레임 기반 서사 흐름 (도입-전개-마무리)",
"summary": "구조 평가 요약"
},
"visual_pace": {
"score": "1-10",
"scene_transitions": "씬 전환 패턴",
"rhythm": "빠른 컷/적절한 컷/느린 컷",
"composition": "화면 구도 (중앙/삼등분/대각선 등)",
"text_graphics": "자막, 그래픽, 이모지 활용",
"visual_style": "전체적 시각 스타일",
"summary": "비주얼 페이스 평가 요약"
},
"audio_trend": {
"score": "1-10",
"music_fit": "음악과 콘텐츠 적합도",
"trend_relevance": "트렌드 반영도",
"audio_quality": "오디오 품질",
"summary": "오디오/트렌드 평가 요약"
},
"cta": {
"score": "1-10",
"cta_type": "직접적/간접적/없음",
"cta_placement": "CTA 배치 위치",
"cta_visual": "CTA 텍스트/그래픽",
"effectiveness": "CTA 효과 평가",
"summary": "CTA 평가 요약"
},
"category": {
"score": "1-10",
"primary_category": "주요 카테고리",
"sub_category": "세부 카테고리",
"target_audience": "타겟 오디언스",
"summary": "카테고리 평가 요약"
},
"overall": {
"score": "1-10",
"strengths": ["강점1", "강점2", "강점3"],
"improvements": ["개선점1", "개선점2", "개선점3"],
"summary": "종합 분석 요약 (시각 + 음성 + 텍스트 종합)"
}
}
기술 스택 요약
| 구성 요소 | 기술 | 용도 |
|---|---|---|
| AI 모델 | claude-sonnet-4-20250514 | 이미지/영상 멀티모달 분석 |
| 이미지 처리 | Pillow (PIL) | 리사이즈, JPEG 변환, Base64 인코딩 |
| 영상 처리 | ffmpeg / ffprobe | 프레임 추출, 오디오 분리, 씬 감지 |
| 음성 인식 | faster-whisper (base, int8) | 음성 → 텍스트 (타임스탬프 포함) |
| API 클라이언트 | anthropic Python SDK | Claude API 호출 (sync client in thread) |
| 비동기 처리 | asyncio.to_thread() | 동기 작업(ffmpeg, Whisper, Claude)을 비동기로 래핑 |
| DB | aiosqlite | 분석 결과 저장 (reel_analyses, image_analyses, carousel_analyses 테이블) |