o2o-infinith-demo/doc/PIPELINE_IMPROVEMENT_PLAN.md

12 KiB
Raw Blame History

검색/분석 파이프라인 종합 개선 계획

작성일: 2026-04-04 최종 수정: 2026-04-04 (Vision Analysis 추가) 상태: Sprint 0 + Sprint 1 구현 중

Context

현재 파이프라인의 3-phase 아키텍처(discover → collect → generate)는 구조적으로는 괜찮으나, 실행 신뢰성정보 수집 범위에 심각한 문제가 있음.

핵심 문제

  • Vision 분석 부재: 텍스트만 수집 → 이미지 속 정보(개원 연도, 의료진 사진, 인증 마크, 시술 전후) 100% 누락. 전체 정보의 약 40%를 놓침
  • Silent failure 16건: 모든 catch 블록이 에러를 삼킴
  • 데이터 품질 8건: 잘못된 URL, 동명이인 병원, 평점 스케일 혼동
  • 에러 복구 0건: 재시도 로직 없음, 새로고침 시 데이터 유실
  • API 불안정 6건: 타임아웃, 쿼터 소진, 인증 실패 미감지

목표: Vision 분석 추가 + 검색 정확도 + 에러 회복력 + UX 투명성 대폭 개선


진행 상태 체크리스트

Sprint 0: Vision Analysis 추가 최우선 (~2h)

성형외과 홈페이지의 핵심 정보가 이미지로 제공됨 (배너, 의료진 사진, 인증 마크 등). Firecrawl screenshot + Gemini Vision으로 이미지 속 정보를 추출.

  • WP-V1. 멀티페이지 스크린샷 캡처 + 저장 (45min)

    • 파일: collect-channel-data/index.ts에 추가 (Phase 2 — 검증된 URL들이 있는 시점)
    • 캡처 대상 (6+ 페이지):
      1. 병원 메인 페이지 — 배너, 소셜 아이콘, 카카오톡 버튼
      2. 의료진 페이지 — siteMap에서 /doctor, /team, /staff URL 자동 탐지
      3. 시술 안내 페이지/surgery, /service, /procedure 탐지
      4. YouTube 채널 랜딩youtube.com/@{handle} (구독자 수, 고정 영상)
      5. Instagram 프로필instagram.com/{handle} (팔로워 수, 피드 미리보기)
      6. 강남언니 페이지 — verified URL (평점, 리뷰 수, 의료진)
    • API: Firecrawl formats: ["screenshot"] + screenshotOptions: { fullPage: false, quality: 80, viewport: { width: 1280, height: 800 } }
    • 저장: Supabase Storage screenshots/{reportId}/{channel}.png → signed URL
    • DB: channel_data.screenshots[]ScreenshotEvidence 형태로 저장
    • 프론트엔드: 이미 구현된 ScreenshotProviderEvidenceGalleryEvidenceLightbox 에 바로 연결
  • WP-V2. Gemini Vision 분석 (1h)

    • 파일: 새 _shared/visionAnalysis.ts
    • API: Google Gemini gemini-2.0-flash (GEMINI_API_KEY 이미 .env에 있음)
    • 각 스크린샷별 분석 프롬프트:
      • 메인 페이지: 개원 연도, 소셜 아이콘, 카카오톡 버튼, 브랜드 컬러, 슬로건, 인증 마크
      • 의료진 페이지: 의사 이름 + 전문 분야 + 약력 (이미지 내 텍스트 OCR)
      • 시술 페이지: 시술 카테고리, 가격 정보, 전후 사진 유무
      • YouTube 랜딩: 구독자 수, 영상 수, 최근 업로드 제목, 고정 영상
      • Instagram 프로필: 팔로워 수, 게시물 수, 최근 피드 미리보기, 바이오
      • 강남언니: 평점(/10), 리뷰 수, 의료진 수, 시술 종류
    • 응답: 페이지별 JSON → channel_data.visionAnalysis에 저장
  • WP-V3. Vision 데이터 → 리포트 + 증거 통합 (45min)

    • 파일: generate-report/index.ts, transformReport.ts
    • 변경:
      • 스크린샷 → report.screenshots[] (ScreenshotEvidence 형태)
        • 프론트엔드의 기존 EvidenceGallery/Lightbox에 바로 표시
      • Vision 추출 의료진 → clinicSnapshot.doctors 보강 (이미지에서 읽은 정보)
      • Vision 추출 개원 연도 → clinicSnapshot.established 보강
      • Vision 추출 YouTube/Instagram 수치 → KPI에 cross-reference
      • 각 채널 진단 항목에 evidenceIds 연결 → 해당 채널 스크린샷이 증거로 표시
    • 검증:
      • 그랜드성형외과 분석 → "SINCE 2004" 개원 연도 추출 확인
      • YouTube 분석 섹션 → 채널 랜딩 스크린샷 썸네일 표시 확인
      • Instagram 분석 섹션 → 프로필 스크린샷 표시 확인
      • 스크린샷 클릭 → 라이트박스 모달에서 풀사이즈 보기 확인

Vision Analysis 프롬프트 설계

System: You are a medical clinic website visual analyst. Extract structured
information from website screenshots. Respond ONLY with valid JSON.

User: Analyze this Korean plastic surgery clinic homepage screenshot.
Extract:
1. Founding year or operation duration (e.g., "SINCE 2004", "21년 무사고")
2. Doctor names and specialties shown in profile photos
3. Certification badges/marks (JCI, 보건복지부, medical tourism)
4. Main service categories from navigation menu
5. Social media icons/buttons visible (Instagram, YouTube, Blog, KakaoTalk, etc.)
6. Floating consultation buttons (KakaoTalk, LINE, WhatsApp)
7. Brand colors (primary, accent) from visual elements
8. Any promotional text or slogans in banners

{
  "foundingYear": "2004",
  "operationYears": 21,
  "doctors": [{"name": "김OO", "specialty": "안면윤곽", "position": "대표원장"}],
  "certifications": ["JCI", "보건복지부 인증"],
  "serviceCategories": ["눈성형", "코성형", "가슴성형", "안면윤곽"],
  "socialIcons": [{"platform": "instagram", "visible": true}, ...],
  "floatingButtons": ["kakaotalk", "line"],
  "brandColors": {"primary": "#C4A882", "accent": "#FF1493"},
  "slogans": ["끊임없이 의료성형 뷰티 트렌드를 연구하는 그랜드 의료진"]
}

Vision Analysis에 필요한 API/리소스

API 용도 비용
Firecrawl formats: ["screenshot"] 페이지 스크린샷 캡처 기존 요금에 포함
Gemini gemini-2.0-flash-exp 이미지 분석 (Vision) ~$0.002/이미지
Gemini GEMINI_API_KEY 이미 설정됨 (.env) 사용 가능

Vision으로 수집 가능한 추가 정보 (현재 누락)

정보 현재 수집 Vision 추가 후
개원 연도 AI 추측 (자주 틀림) 배너에서 직접 읽기
의료진 수/이름 ⚠️ 강남언니에서만 홈페이지 프로필에서 추출
인증 마크 미수집 JCI, 보건복지부 등 인식
시술 카테고리 ⚠️ Firecrawl JSON 네비게이션 메뉴에서 확인
카카오톡 상담 버튼 JS 렌더링이라 못 잡음 플로팅 버튼 감지
브랜드 컬러 ⚠️ CSS 추출 (부정확) 실제 비주얼에서 추출
슬로건/태그라인 ⚠️ 이미지 내 텍스트 누락 배너 텍스트 OCR

Sprint 1: 데이터 품질 Quick Wins (~2.5h) 완료

  • WP-1. YouTube Channel ID 정규식 수정 (20min)
  • WP-2. Naver Place 동명이인 방지 (30min)
  • WP-3. Google Maps URL 수정 (20min)
  • WP-4. Naver Blog 공식 블로그 분리 (30min)
  • WP-5. 강남언니 평점 정규화 (30min)
  • WP-6. Perplexity 모델 상수화 (20min)
  • WP-7. Apify 타임아웃 증가 (10min)

추가 완료:

  • 소셜 버튼 직접 추출 (Firecrawl actions + JS 렌더링 후 href 추출)

Sprint 2: 에러 가시성 (~2.25h)

  • WP-8. 채널 수집 에러 추적 핵심 (1.5h)

    • 파일: collect-channel-data/index.ts
    • DB: ALTER TABLE marketing_reports ADD COLUMN IF NOT EXISTS channel_errors JSONB DEFAULT '{}';
    • 변경:
      • HTTP 상태 코드 체크 (429, 403, 500)
      • Promise.allSettled 결과 순회 → channelErrors 기록
      • 부분 성공이어도 항상 DB 저장 (unconditional save)
      • status: "collected" vs "partial" vs "collection_failed"
      • 응답: { channelData, channelErrors, partialFailure }
    • 검증: API 토큰 무효화 시 에러가 기록되는지 확인
  • WP-9. Instagram/Facebook 검증 개선 (45min)

    • 파일: _shared/verifyHandles.ts
    • 변경:
      • verified 타입: boolean | "unverifiable"
      • Instagram: 로그인 리다이렉트 감지
      • Facebook: HEAD → GET, 실패 시 "unverifiable"
      • collect-channel-data에서 "unverifiable" 포함
    • 검증: Facebook 핸들이 "unverifiable"로 표시되고 수집은 시도됨

Sprint 3: 에러 회복 (~5.25h)

  • WP-10. API 재시도 유틸리티 (2h)

    • 파일: 새 _shared/retry.ts
    • 변경: fetchWithRetry() — 지수 백오프, 429 존중, AbortController 타임아웃
    • 검증: 429 응답 시 자동 재시도 후 성공
  • WP-11. 부분 실패 복구 (1h)

    • 파일: collect-channel-data/index.ts
    • 변경: Promise.allSettled 직후 무조건 중간 DB 저장
    • 검증: 일부 채널 실패해도 나머지 데이터 보존
  • WP-12. 파이프라인 이어하기 (1.5h)

    • 파일: AnalysisLoadingPage.tsx, supabase.ts
    • 변경:
      • sessionStorage에 reportId 저장
      • URL에 reportId 포함
      • DB status 폴링
      • status별 이어하기 로직
    • 검증: 분석 중 새로고침 → 이어서 진행
  • WP-13. Enrichment 재시도 버튼 (45min)

    • 파일: useEnrichment.ts, EmptyState.tsx
    • 변경: retry() 함수 + "다시 시도" 버튼 (최대 2회)
    • 검증: Enrichment 실패 후 버튼 클릭 → 재시도 성공

Sprint 4: UX 마무리 (~50min)

  • WP-14. EmptyState 상태별 UI (20min)

    • 파일: EmptyState.tsx
    • 변경: loading | error | not_found 상태별 다른 UI
    • 검증: 각 상태에 맞는 아이콘/메시지/버튼 표시
  • WP-15. Firecrawl Rate Limiting (30min)

    • 파일: _shared/retry.ts에 추가
    • 변경: 도메인별 500ms 간격 강제
    • 검증: Firecrawl 429 에러 발생 안 함

핵심 파일 목록

파일 Sprint 설명
supabase/functions/discover-channels/index.ts 0, 1 채널 발견 + Vision
supabase/functions/collect-channel-data/index.ts 0, 1, 2, 3 데이터 수집
supabase/functions/_shared/visionAnalysis.ts (신규) 0 Vision 분석 유틸
supabase/functions/_shared/config.ts 1 공유 설정
supabase/functions/_shared/verifyHandles.ts 1, 2 핸들 검증
supabase/functions/_shared/retry.ts (신규) 3 재시도 유틸
supabase/functions/enrich-channels/index.ts 1 레거시 enrichment
supabase/functions/generate-report/index.ts 0 리포트에 Vision 데이터 반영
supabase/migrations/20260404_channel_errors.sql (신규) 2 DB 마이그레이션
src/pages/AnalysisLoadingPage.tsx 3 로딩 페이지
src/hooks/useEnrichment.ts 3 Enrichment 훅
src/components/report/ui/EmptyState.tsx 4 빈 상태 UI
src/lib/supabase.ts 3 API 클라이언트

총 예상 소요 시간

Sprint 시간 배포 범위 상태
Sprint 0: Vision Analysis ~2h Edge Functions + Gemini 🔜 다음
Sprint 1: Quick Wins ~2.5h Edge Functions × 5 완료
Sprint 2: 에러 가시성 ~2.25h DB + Edge Functions 대기
Sprint 3: 에러 회복 ~5.25h Edge Functions + Frontend + Vercel 대기
Sprint 4: UX 마무리 ~50min Frontend + Vercel 대기
합계 ~13h

Vision Analysis 아키텍처

discover-channels (Stage A)
  ├─ A1. Firecrawl scrape (JSON + links)          ← 기존
  ├─ A2. Firecrawl map                              ← 기존
  ├─ A3. Firecrawl branding                         ← 기존
  ├─ A4. Firecrawl social buttons (JS actions)      ← 방금 추가
  └─ A5. Firecrawl screenshot + Gemini Vision       ← Sprint 0 신규
         ├─ 메인 페이지 스크린샷 캡처
         ├─ Gemini Vision 분석 (개원 연도, 의료진, 인증, 소셜 아이콘)
         └─ 결과를 clinic 정보 + socialHandles에 병합

collect-channel-data
  └─ Vision 데이터를 channel_data.visionAnalysis에 저장

generate-report
  └─ Vision 데이터를 리포트 프롬프트에 포함 (실제 데이터 기반)