# INFINITH — Developer Handoff Document **Version:** 2.0 | **Updated:** 2026-04-01 | **Status:** Phase 1 Backend Complete, Frontend Integration Pending --- ## 1. Architecture Overview ``` [Frontend: React + Vite + TailwindCSS] │ ├── src/lib/supabase.ts (API client) │ ▼ [Supabase Edge Functions (Deno)] │ ├── generate-report ──── Orchestrator (Pipeline Entry) │ │ │ ├── 1) scrape-website ─── Firecrawl API │ │ ├── /v1/scrape (구조화 JSON 추출) │ │ ├── /v1/map (사이트맵 탐색) │ │ └── /v1/search (리뷰 검색) │ │ │ ├── 2) analyze-market ─── Perplexity API (sonar) │ │ ├── 경쟁 병원 분석 │ │ ├── 키워드 트렌드 │ │ ├── 시장 분석 │ │ └── 타겟 오디언스 │ │ │ └── 3) AI 리포트 합성 ─── Perplexity API (sonar) │ ├── enrich-channels ──── Phase 2 (Background Enrichment) │ ├── Instagram ─── Apify (instagram-profile-scraper) │ ├── Google Maps ── Apify (crawler-google-places) │ └── YouTube ───── Apify (youtube-channel-scraper) ⚠️ 수정 필요 │ ▼ [Supabase PostgreSQL] ├── scrape_results (스크래핑 캐시) └── marketing_reports (최종 리포트) ``` --- ## 2. Supabase Project | Item | Value | |------|-------| | **Project Ref** | `wkvjclkkonoxqtjxiwcw` | | **Region** | Seoul (ap-northeast-2) | | **Dashboard** | `https://supabase.com/dashboard/project/wkvjclkkonoxqtjxiwcw` | | **API URL** | `https://wkvjclkkonoxqtjxiwcw.supabase.co` | | **Edge Functions** | `https://wkvjclkkonoxqtjxiwcw.supabase.co/functions/v1/{function-name}` | | **CLI** | `npx supabase` (글로벌 설치 불필요) | | **Access Token** | `infinith-cli` (Supabase Dashboard > Account > Access Tokens) | ### Database Tables ```sql -- supabase/migrations/20260330_create_tables.sql scrape_results ( id UUID PRIMARY KEY, url TEXT NOT NULL, clinic_name TEXT, data JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT NOW() ) marketing_reports ( id UUID PRIMARY KEY, url TEXT NOT NULL, clinic_name TEXT, report JSONB NOT NULL DEFAULT '{}', -- 최종 AI 리포트 scrape_data JSONB DEFAULT '{}', -- 원본 스크래핑 데이터 analysis_data JSONB DEFAULT '{}', -- 시장 분석 데이터 created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ) ``` - RLS 활성화됨. `service_role` key로 Edge Function 호출 시 전체 접근 가능 - `anon` role은 `marketing_reports` SELECT만 가능 ### Supabase Secrets (Edge Functions 환경변수) Edge Function에서 `Deno.env.get()`으로 접근: ```bash # 시크릿 설정 명령어 npx supabase secrets set FIRECRAWL_API_KEY=fc-cdae60d9535d46b086ee0f44a09ab185 npx supabase secrets set PERPLEXITY_API_KEY=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 npx supabase secrets set APIFY_API_TOKEN=apify_api_1ArgFPTjHhDxhyd9UkNVOF3WCABfA21GcXmv npx supabase secrets set GEMINI_API_KEY=AIzaSyBUnPozy-crOLFwVepcamAnG8WWp2x1KZY # 자동 제공 (설정 불필요) # SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, SUPABASE_ANON_KEY ``` --- ## 3. Edge Functions — 상세 스펙 ### 3.1 `scrape-website` | Item | Detail | |------|--------| | **파일** | `supabase/functions/scrape-website/index.ts` | | **엔드포인트** | `POST /functions/v1/scrape-website` | | **Auth** | `--no-verify-jwt` (인증 불필요) | | **외부 API** | Firecrawl (`api.firecrawl.dev`) | | **소요시간** | ~15-20s | **Request:** ```json { "url": "https://viewclinic.com", "clinicName": "뷰성형외과" } ``` **Response:** ```json { "success": true, "data": { "clinic": { "clinicName", "address", "phone", "services[]", "doctors[]", "socialMedia{}" }, "siteLinks": ["url1", "url2"], "siteMap": ["url1", "url2"], "reviews": [{ "title", "url", "description" }], "scrapedAt": "ISO timestamp", "sourceUrl": "https://viewclinic.com" } } ``` **처리 흐름:** 1. Firecrawl `/v1/scrape` — 메인 URL에서 병원 정보 구조화 추출 (JSON schema 정의됨) 2. Firecrawl `/v1/map` — 사이트 전체 페이지 URL 수집 (limit: 50) 3. Firecrawl `/v1/search` — "{병원명} 리뷰 평점 후기 강남언니 바비톡" 검색 --- ### 3.2 `analyze-market` | Item | Detail | |------|--------| | **파일** | `supabase/functions/analyze-market/index.ts` | | **엔드포인트** | `POST /functions/v1/analyze-market` | | **외부 API** | Perplexity (`api.perplexity.ai`, model: `sonar`) | | **소요시간** | ~10-15s (4개 쿼리 병렬) | **Request:** ```json { "clinicName": "뷰성형외과", "services": ["코성형", "눈성형", "리프팅"], "address": "강남구", "scrapeData": { ... } } ``` **Response:** ```json { "success": true, "data": { "clinicName": "뷰성형외과", "services": [...], "address": "강남구", "analysis": { "competitors": { "data": {...}, "citations": [...] }, "keywords": { "data": {...}, "citations": [...] }, "market": { "data": {...}, "citations": [...] }, "targetAudience": { "data": {...}, "citations": [...] } }, "analyzedAt": "ISO timestamp" } } ``` **4개 병렬 Perplexity 쿼리:** 1. `competitors` — 주변 경쟁 병원 5곳 (이름, 시술, 온라인 평판, 마케팅 채널) 2. `keywords` — 네이버/구글 검색 키워드 트렌드 20개 3. `market` — 시장 규모, 성장률, 트렌드 분석 4. `targetAudience` — 연령/성별/관심사/채널 분석 --- ### 3.3 `generate-report` (Orchestrator) | Item | Detail | |------|--------| | **파일** | `supabase/functions/generate-report/index.ts` | | **엔드포인트** | `POST /functions/v1/generate-report` | | **외부 API** | 내부적으로 `scrape-website` + `analyze-market` + Perplexity 호출 | | **소요시간** | ~45s (전체 파이프라인) | **Request:** ```json { "url": "https://viewclinic.com", "clinicName": "뷰성형외과" } ``` **Response:** ```json { "success": true, "reportId": "uuid", "report": { "clinicInfo": { ... }, "executiveSummary": "경영진 요약", "overallScore": 72, "channelAnalysis": { "naverBlog": { "score", "status", "posts", "recommendation" }, "instagram": { "score", "status", "followers", "recommendation" }, "youtube": { "score", "status", "subscribers", "recommendation" }, "naverPlace": { "score", "rating", "reviews", "recommendation" }, "gangnamUnni": { "score", "rating", "reviews", "recommendation" }, "website": { "score", "issues[]", "recommendation" } }, "competitors": [{ "name", "strengths[]", "weaknesses[]", "marketingChannels[]" }], "keywords": { "primary": [{ "keyword", "monthlySearches", "competition" }], "longTail": [{ "keyword", "monthlySearches" }] }, "targetAudience": { "primary": {...}, "secondary": {...} }, "recommendations": [{ "priority", "category", "title", "description", "expectedImpact" }], "marketTrends": [...] }, "metadata": { "url": "...", "clinicName": "...", "generatedAt": "ISO timestamp", "dataSources": { "scraping": true, "marketAnalysis": true, "aiGeneration": true } } } ``` **파이프라인 순서:** 1. `scrape-website` 호출 → 병원 데이터 수집 2. `analyze-market` 호출 → 시장 분석 3. Perplexity `sonar` 모델로 최종 리포트 JSON 합성 4. `marketing_reports` 테이블에 저장 --- ### 3.4 `enrich-channels` (Phase 2 — Background) | Item | Detail | |------|--------| | **파일** | `supabase/functions/enrich-channels/index.ts` | | **엔드포인트** | `POST /functions/v1/enrich-channels` | | **외부 API** | Apify Actors (3개 병렬) | | **소요시간** | ~27s | **Request:** ```json { "reportId": "uuid", "clinicName": "뷰성형외과", "instagramHandle": "viewplastic", "youtubeChannelId": "@viewplastic", "address": "강남구" } ``` **Apify Actors 사용:** | Actor | Actor ID | 용도 | 검증 | |-------|----------|------|------| | Instagram Profile | `apify~instagram-profile-scraper` | 팔로워, 게시물, 바이오 | ✅ 정상 작동 (6s) | | Google Maps | `compass~crawler-google-places` | 평점, 리뷰, 영업시간 | ✅ 정상 작동 (10s) | | YouTube Channel | `streamers~youtube-channel-scraper` | 영상 목록, 조회수 | ⚠️ 빈 데이터 반환 — 다른 Actor 또는 YouTube Data API v3 필요 | **동작:** 기존 `marketing_reports`의 `report` 필드에 `channelEnrichment` 객체를 추가 저장 --- ## 4. Frontend 통합 가이드 ### 현재 상태 | 파일 | 상태 | 설명 | |------|------|------| | `src/lib/supabase.ts` | ✅ 완성 | `generateMarketingReport()`, `scrapeWebsite()` 함수 | | `src/pages/AnalysisLoadingPage.tsx` | ✅ 완성 | 실제 API 호출 + 프로그레스 UI | | `src/hooks/useReport.ts` | ⚠️ **Mock 데이터** | `mockReport` 반환 중 — 실제 API 연동 필요 | | `src/pages/ReportPage.tsx` (or similar) | ⚠️ 확인 필요 | `useReport()` 결과 렌더링 | ### 프론트엔드 환경변수 (`.env`) ```env VITE_SUPABASE_URL=https://wkvjclkkonoxqtjxiwcw.supabase.co VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIs... ``` ### API 호출 방식 Edge Functions는 `--no-verify-jwt`로 배포되어 있어 Authorization 헤더 불필요: ```typescript // src/lib/supabase.ts const response = await fetch( `${supabaseUrl}/functions/v1/generate-report`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url, clinicName }), } ); ``` ### TODO: `useReport()` 실제 연동 `AnalysisLoadingPage`에서 `/report/view-clinic`으로 navigate할 때 `location.state`에 report 데이터를 전달 중: ```typescript navigate('/report/view-clinic', { replace: true, state: { report: result.report, metadata: result.metadata }, }); ``` `useReport()` 훅에서 이 state를 받아 사용하도록 변경 필요. ### TODO: Progressive Loading (Phase 2) ``` Phase 1 (~45s): generate-report → 즉시 리포트 표시 Phase 2 (~27s): enrich-channels → 백그라운드에서 채널 데이터 보강 → 완료 시 리포트 UI에 실시간 반영 ``` 구현 방식: Phase 1 리포트 렌더 후 `enrich-channels` 비동기 호출 → Supabase Realtime 또는 polling으로 업데이트 감지 --- ## 5. API & Service 연결 현황 ### Connected (연결 완료, 코드 구현됨) | # | Service | 용도 | API Key 위치 | Dashboard | |---|---------|------|-------------|-----------| | 1 | **Firecrawl** | 웹사이트 스크래핑 (scrape/map/search) | `.env` + Supabase Secret | [Dashboard](https://www.firecrawl.dev/app) | | 2 | **Perplexity** | 시장 분석 + 리포트 생성 (sonar) | `.env` + Supabase Secret | [API Settings](https://www.perplexity.ai/settings/api) | | 3 | **Apify** | Instagram/Google Maps 채널 데이터 | `.env` + Supabase Secret | [Console](https://console.apify.com/) | | 4 | **Supabase** | DB + Edge Functions + Auth | `.env` (VITE_*) | [Dashboard](https://supabase.com/dashboard/project/wkvjclkkonoxqtjxiwcw) | ### Connected (MCP로 연결, 코드 미사용) | # | Service | 용도 | MCP Config | |---|---------|------|-----------| | 5 | **Gemini (nano-banana-pro)** | AI 이미지 생성/편집 | `claude_desktop_config.json` | | 6 | **Figma MCP** | 디자인 에셋 읽기, 브랜드 변수 | VS Code Extension | | 7 | **Slack MCP** | 팀 알림, 채널 메시징 | `claude_desktop_config.json` | | 8 | **Notion MCP** | 문서/DB 관리 | VS Code Extension | | 9 | **Google Drive** | 파일 저장/공유 | VS Code Extension | | 10 | **Ahrefs** | SEO/키워드 분석 (GSC 연동) | VS Code Extension | | 11 | **Claude in Chrome** | 브라우저 자동화 | Chrome Extension | | 12 | **Firebase** | (미사용, 연결만) | VS Code Extension | ### Not Connected (연동 필요) | # | Service | 용도 | 우선순위 | 문서 | |---|---------|------|---------|------| | 13 | **YouTube Data API v3** | 채널 통계, 영상 분석 | P0 | [Docs](https://developers.google.com/youtube/v3/getting-started) | | 14 | **Naver Search API** | 블로그/카페/뉴스 검색 | P0 | [Developers](https://developers.naver.com/) | | 15 | **Claude/Anthropic API** | AI 리포트 생성 (Perplexity 대체 가능) | P1 | [Docs](https://docs.anthropic.com/) | | 16 | **Creatomate** | 템플릿 기반 영상/이미지 생성 | P1 | [API Docs](https://creatomate.com/docs/api/introduction) | | 17 | **Instagram Graph API** | 공식 게시/인사이트 | P1 | [Docs](https://developers.facebook.com/docs/instagram-platform/) | | 18 | **Google Search Console** | SEO 성과 추적 | P1 | [Docs](https://developers.google.com/webmaster-tools) | | 19 | **Google Analytics 4** | 웹 트래픽 분석 | P1 | [Docs](https://developers.google.com/analytics/devguides/reporting/data/v1) | | 20 | **Naver Place/Map API** | 플레이스 리뷰/위치 | P2 | [Naver Cloud](https://www.ncloud.com/) | | 21 | **Google Maps Places API** | 구글 리뷰/평점 (Apify 대체중) | P2 | [Docs](https://developers.google.com/maps/documentation/places/web-service) | | 22 | **TikTok API** | 숏폼 게시/분석 | P2 | [Docs](https://developers.tiktok.com/) | | 23 | **Canva Connect API** | 템플릿 Autofill (Enterprise) | P2 | [Docs](https://www.canva.dev/docs/connect/) | | 24 | **Brandfetch** | 브랜드 로고/컬러 추출 | P2 | [Docs](https://docs.brandfetch.com/) | --- ## 6. 측정된 파이프라인 타이밍 ``` Phase 1: generate-report 전체 (~45초) ├── scrape-website ~15-20s ├── analyze-market ~10-15s (4 Perplexity 병렬) └── AI report 합성 ~10-15s Phase 2: enrich-channels (~27초, 백그라운드) ├── Instagram (Apify) ~6s ├── Google Maps (Apify) ~10s └── YouTube (Apify) ~11s (⚠️ 빈 데이터) Total: ~72초 (사용자 체감: 45초 → 리포트 표시) ``` --- ## 7. 알려진 이슈 & 해결 필요 | # | 이슈 | 상세 | 해결 방향 | |---|------|------|---------| | 1 | **Gemini API 429** | 프로젝트 spending cap $10 초과. `generate-report`에서 Perplexity로 대체 완료 | AI Studio에서 한도 증가 또는 Perplexity 유지 | | 2 | **YouTube Apify 빈 데이터** | `streamers~youtube-channel-scraper` Actor가 빈 배열 반환 | YouTube Data API v3 연동 또는 다른 Apify Actor 탐색 | | 3 | **useReport() Mock** | `useReport()` 훅이 mockReport 반환 중 | `location.state`에서 실제 데이터 읽도록 변경 | | 4 | **JWT 미검증** | Edge Functions가 `--no-verify-jwt`로 배포 | 프로덕션 전에 Supabase Auth 연동 + JWT 검증 활성화 | | 5 | **Instagram 핸들 자동 감지** | `scrape-website`에서 추출한 socialMedia.instagram을 `enrich-channels`에 자동 전달 필요 | `generate-report` orchestrator에서 연결 | --- ## 8. 프로젝트 구조 ``` remix_-infinith---infinite-marketing/ ├── src/ │ ├── components/ │ │ ├── Hero.tsx # 랜딩 히어로 (URL 입력) │ │ ├── icons/ # 커스텀 아이콘 │ │ └── ... │ ├── hooks/ │ │ └── useReport.ts # ⚠️ Mock 데이터 → 실제 연동 필요 │ ├── lib/ │ │ └── supabase.ts # ✅ Supabase 클라이언트 + API 함수 │ ├── pages/ │ │ ├── AnalysisLoadingPage.tsx # ✅ 분석 로딩 (실제 API 호출) │ │ └── ... │ └── ... ├── supabase/ │ ├── functions/ │ │ ├── scrape-website/ # ✅ Firecrawl 스크래핑 │ │ ├── analyze-market/ # ✅ Perplexity 시장 분석 │ │ ├── generate-report/ # ✅ 파이프라인 오케스트레이터 │ │ └── enrich-channels/ # ✅ Apify 채널 enrichment │ └── migrations/ │ └── 20260330_create_tables.sql # DB 스키마 ├── docs/ │ ├── API_CONNECTORS.md # API 레지스트리 (v1.0) │ ├── DESIGN_SYSTEM.md # 디자인 시스템 │ └── DEVELOPER_HANDOFF.md # 이 문서 ├── .env # 환경변수 (Git 제외) └── package.json ``` --- ## 9. Edge Functions 배포 가이드 ```bash # 1. Supabase CLI 로그인 npx supabase login # 2. 프로젝트 연결 npx supabase link --project-ref wkvjclkkonoxqtjxiwcw # 3. Secrets 설정 (최초 1회) npx supabase secrets set FIRECRAWL_API_KEY=fc-cdae60d9535d46b086ee0f44a09ab185 npx supabase secrets set PERPLEXITY_API_KEY=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 npx supabase secrets set APIFY_API_TOKEN=apify_api_1ArgFPTjHhDxhyd9UkNVOF3WCABfA21GcXmv # 4. 개별 함수 배포 npx supabase functions deploy scrape-website --no-verify-jwt npx supabase functions deploy analyze-market --no-verify-jwt npx supabase functions deploy generate-report --no-verify-jwt npx supabase functions deploy enrich-channels --no-verify-jwt # 5. DB 마이그레이션 npx supabase db push # 6. 로컬 테스트 npx supabase functions serve --env-file .env ``` --- ## 10. 개발 우선순위 로드맵 ### Phase 1 — 즉시 (Frontend ↔ Backend 연결) - [ ] `useReport()` 훅을 실제 API 데이터로 교체 - [ ] `generate-report` 호출 후 `enrich-channels` 자동 호출 연결 - [ ] 리포트 페이지에서 `channelEnrichment` 데이터 렌더링 - [ ] YouTube Data API v3 연동 (Apify 대체) - [ ] 에러 핸들링 강화 (재시도, timeout, 사용자 피드백) ### Phase 2 — Content Studio - [ ] Naver Search API 연동 (블로그/카페 검색) - [ ] 콘텐츠 캘린더 생성 로직 (리포트 기반) - [ ] Creatomate API 연동 (이미지/영상 생성) - [ ] Gemini 이미지 생성 연동 (nano-banana-pro MCP) ### Phase 3 — 자동화 & 배포 - [ ] Instagram Graph API (자동 게시) - [ ] Supabase Auth 연동 (사용자 인증) - [ ] Edge Function JWT 검증 활성화 - [ ] Google Analytics / Search Console 연동 - [ ] 자동 리포트 스케줄링 --- ## 11. 테스트 데이터 검증에 사용한 실제 병원: | 병원 | URL | Instagram | 비고 | |------|-----|-----------|------| | 뷰성형외과 | `https://viewclinic.com` | `viewplastic` (14,094 followers) | 전체 파이프라인 테스트 완료 | --- *Last updated: 2026-04-01*