o2o-infinith-demo/docs/DB_SCHEMA_V3.md

10 KiB

INFINITH SaaS Database Schema V3

작성일: 2026-04-05 마이그레이션 파일: supabase/migrations/20260405_saas_schema_v3.sql

설계 원칙

  1. CLINIC-CENTRIC: 병원 1개 = 1행. URL이 달라도 같은 병원이면 같은 행
  2. TIME-SERIES: 채널 메트릭은 INSERT-only 스냅샷 (시계열 쿼리)
  3. SEPARATION: 원시 데이터 / 분석 리포트 / 콘텐츠 전략 분리
  4. LOOP-READY: 매 분석이 이전 데이터를 참조해 전략 자동 조정
  5. MULTI-TENANT: user_id 기반 접근 제어 (미래 auth)

ERD (Entity Relationship)

clinics (병원 마스터)
 ├─ analysis_runs (분석 실행 히스토리) ← 매주 1행씩 쌓임
 │    ├─ channel_snapshots (채널별 시계열 메트릭) ← INSERT-only
 │    ├─ screenshots (스크린샷 증거)
 │    └─ performance_metrics (성과 메트릭)
 │         └─ strategy_adjustments (전략 조정 근거)
 ├─ channel_configs (사용자 수동 채널 연결)
 ├─ content_plans (콘텐츠 기획 — 활성 1개)
 │    └─ content_performance (개별 콘텐츠 성과)
 └─ (marketing_reports) ← 레거시 호환

테이블 상세

1. clinics — 병원 마스터

컬럼 타입 설명
id UUID PK
user_id UUID 소유자 (미래 auth)
url TEXT UNIQUE 대표 URL
name TEXT 한국어 병원명
name_en TEXT 영문 병원명
domain TEXT 도메인
address, phone TEXT 기본 정보
established_year INT 개원 연도
services TEXT[] 시술 목록
branding JSONB 컬러, 폰트, 로고, 태그라인
social_handles JSONB 검증된 소셜 핸들
verified_channels JSONB Phase 1 결과 캐시
analysis_frequency TEXT 'manual' / 'daily' / 'weekly' / 'monthly'
last_analyzed_at TIMESTAMPTZ 마지막 분석 시간

2. analysis_runs — 분석 실행 히스토리

컬럼 타입 설명
id UUID PK
clinic_id UUID FK → clinics
status TEXT pending/discovering/collecting/generating/complete/partial/error
scrape_data JSONB Firecrawl 원시 데이터
raw_channel_data JSONB API 수집 원시 데이터
analysis_data JSONB 시장 분석
vision_analysis JSONB Vision 분석 결과
report JSONB AI 리포트
channel_errors JSONB 채널별 에러 기록
trigger TEXT 'manual' / 'scheduled' / 'webhook'

3. channel_snapshots — 채널별 시계열 핵심

컬럼 타입 설명
id UUID PK
clinic_id UUID FK → clinics
run_id UUID FK → analysis_runs
channel TEXT youtube/instagram/facebook/gangnamUnni/...
handle TEXT @handle 또는 URL
followers INT 구독자/팔로워
posts INT 게시물/영상 수
total_views BIGINT 총 조회수
rating NUMERIC(3,1) 평점
reviews INT 리뷰 수
health_score INT 0-100
details JSONB 상세 (top videos, latest posts 등)
screenshot_url TEXT 채널 랜딩 스크린샷
captured_at TIMESTAMPTZ 캡처 시간

핵심: INSERT-only. 절대 UPDATE 하지 않음. captured_at 기준 시계열 쿼리.

4. screenshots — 스크린샷 증거

컬럼 타입 설명
id UUID PK
clinic_id, run_id UUID FK
channel TEXT 어떤 채널
page_type TEXT main/doctors/surgery/landing
url TEXT 이미지 URL (Supabase Storage)
source_url TEXT 원본 페이지 URL
vision_data JSONB Gemini Vision 추출 데이터

5. content_plans — 콘텐츠 기획

컬럼 타입 설명
id UUID PK
clinic_id UUID FK → clinics
run_id UUID FK → analysis_runs 어떤 분석 기반
brand_guide JSONB 브랜딩 가이드
channel_strategies JSONB 채널별 전략
content_strategy JSONB 필라, 타입, 워크플로우
calendar JSONB 4주 캘린더
is_active BOOLEAN 현재 활성 기획

6. channel_configs — 사용자 수동 채널 연결

컬럼 타입 설명
id UUID PK
clinic_id UUID FK
channel TEXT 채널 종류
handle TEXT @handle
is_verified BOOLEAN 사용자가 직접 확인
access_token TEXT OAuth token (미래)

7. performance_metrics — 성과 메트릭 (루프 핵심)

컬럼 타입 설명
id UUID PK
clinic_id, run_id UUID FK
prev_run_id UUID FK 이전 분석 (비교 기준)
channel_deltas JSONB 채널별 변화량
kpi_progress JSONB KPI 달성률
top_performing_content JSONB 성과 좋은 콘텐츠
underperforming_channels TEXT[] 성과 미달 채널
strategy_suggestions JSONB AI 전략 조정 제안

8. content_performance — 개별 콘텐츠 성과

컬럼 타입 설명
id UUID PK
clinic_id UUID FK
plan_id UUID FK 어떤 기획의 콘텐츠
channel, content_type TEXT 채널 + 유형
views, likes, comments, shares INT 반응 지표
engagement_rate NUMERIC 참여율
performance_score INT 0-100

9. strategy_adjustments — 전략 조정 히스토리

컬럼 타입 설명
id UUID PK
clinic_id, plan_id UUID FK
performance_id UUID FK 어떤 성과 분석 기반
adjustment_type TEXT frequency_change/pillar_shift/channel_add/...
description TEXT "YouTube Shorts 주 3회 → 5회로 증가"
reason TEXT "Shorts 조회수가 Long-form 대비 300% 높음"
before_value, after_value JSONB 변경 전/후 값

Views (트렌드 쿼리용)

channel_latest

각 채널의 최신 스냅샷만 반환. DISTINCT ON + ORDER BY captured_at DESC.

channel_weekly_delta

현재 vs 7일 전 스냅샷을 LATERAL JOIN으로 비교. 팔로워 변화율(%) 자동 계산.


성과 → 기획 루프 플로우

Week N:
  1. analysis_run 생성
  2. channel_snapshots INSERT (현재 메트릭)
  3. performance_metrics 생성 (이전 run과 비교)
     → channel_deltas: {youtube: +2%, instagram: +7.1%}
     → kpi_progress: [{metric: "YouTube 구독자", progress: 97.4%}]
     → strategy_suggestions: ["Instagram Reels 빈도 증가 권장"]
  4. strategy_adjustments INSERT (조정 내역)
  5. content_plans UPDATE (조정 반영)
     → Content Director가 strategy_suggestions를 기반으로 캘린더 자동 조정
  6. report 생성 (이전 분석 대비 변화 포함)

Week N+1:
  → 2번부터 반복. channel_snapshots에 데이터가 쌓이면서 트렌드 정확도 ↑

기존 호환성

기존 테이블 상태 이관 계획
marketing_reports 유지 (deprecated) 기존 API 호환 유지, 새 데이터는 새 테이블에만 저장
scrape_results 유지 캐시용

구현 체크리스트

Phase 1: DB 마이그레이션 (테이블 생성)

  • Supabase Dashboard에서 20260405_saas_schema_v3.sql 실행
  • 테이블 9개 생성 확인: clinics, analysis_runs, channel_snapshots, screenshots, content_plans, channel_configs, performance_metrics, content_performance, strategy_adjustments
  • View 2개 생성 확인: channel_latest, channel_weekly_delta
  • RLS 정책 적용 확인
  • 인덱스 생성 확인

Phase 2: Edge Function 전환 — discover-channels

  • discover-channels/index.ts: clinics 테이블에 UPSERT (url 기준)
  • discover-channels/index.ts: analysis_runs 테이블에 INSERT (status: 'discovering')
  • discover-channels/index.ts: clinic.verified_channels 업데이트
  • 기존 marketing_reports에도 병행 쓰기 유지 (호환성)
  • 검증: 분석 실행 → clinics + analysis_runs 행 생성 확인

Phase 3: Edge Function 전환 — collect-channel-data

  • collect-channel-data/index.ts: channel_snapshots에 채널별 INSERT
  • collect-channel-data/index.ts: screenshots에 스크린샷 INSERT
  • collect-channel-data/index.ts: analysis_runs.raw_channel_data 업데이트
  • collect-channel-data/index.ts: analysis_runs.vision_analysis 업데이트
  • 기존 marketing_reports.channel_data에도 병행 쓰기 유지
  • 검증: channel_snapshots에 채널별 행 생성 확인

Phase 4: Edge Function 전환 — generate-report

  • generate-report/index.ts: analysis_runs.report 업데이트
  • generate-report/index.ts: analysis_runs.status = 'complete'
  • generate-report/index.ts: clinics.last_analyzed_at 업데이트
  • content_plans 자동 생성 (transformPlan 로직 서버사이드 이동)
  • 기존 marketing_reports.report에도 병행 쓰기 유지
  • 검증: analysis_runs.status = 'complete' + report JSONB 생성 확인

Phase 5: 성과 분석 루프 구현

  • 반복 분석 시 performance_metrics 자동 생성 (이전 run 대비)
  • channel_weekly_delta View 쿼리 테스트
  • strategy_suggestions 생성 로직 (Content Director 연동)
  • content_plans 자동 업데이트 (전략 조정 반영)
  • strategy_adjustments 히스토리 기록
  • 검증: 2회 연속 분석 → performance_metrics에 변화량 기록 확인

Phase 6: Frontend 전환

  • useReport 훅: analysis_runs + channel_snapshots에서 읽기
  • useMarketingPlan 훅: content_plans에서 읽기
  • ReportPage: channel_snapshots 시계열 데이터로 트렌드 차트 추가
  • KPIDashboard: performance_metrics의 달성률 표시
  • 검증: 기존 리포트 URL이 새 테이블에서도 정상 렌더링

Phase 7: 스케줄링

  • clinics.analysis_frequency 기반 자동 분석 트리거
  • Supabase Cron 또는 external scheduler 연동
  • analysis_runs.trigger = 'scheduled' 기록
  • 검증: 주간 자동 분석 실행 확인

Phase 8: 기존 데이터 이관

  • marketing_reportsclinics + analysis_runs 이관 스크립트
  • marketing_reports.channel_datachannel_snapshots 변환
  • marketing_reports.reportanalysis_runs.report 복사
  • 이관 후 기존 리포트 URL 정상 작동 확인
  • marketing_reports deprecated 마킹