o2o-infinith-demo/docs/DEVELOPER_HANDOFF.md

509 lines
18 KiB
Markdown

# 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*