# InforKeyword 아키텍처 설계서 v1

**작성일**: 2026-03-04
**작성**: 개발2팀

---

## 1. 기술스택

| 영역 | 기술 | 버전 | 비고 |
|------|------|------|------|
| 프론트엔드 | Next.js (App Router) | 15.x | TypeScript |
| UI 프레임워크 | Tailwind CSS + shadcn/ui | - | 빠른 개발 |
| 백엔드 (API) | Next.js API Routes | - | Vercel 배포 |
| 백엔드 (Worker) | Python (FastAPI) | 3.12 | 크롤링/OCR 처리 |
| DB | Firestore | - | insuwiki-j2h 프로젝트 |
| 인증 | Firebase Auth | - | 이메일/비밀번호 |
| LLM | Gemini 2.5 Flash | - | 기존 API 키 |
| OCR | Google Cloud Vision | - | GCP 프로젝트 |
| 검색량 API | 네이버 검색광고 API | - | 신규 계정 필요 |
| 크롤링 | axios + cheerio / Python requests + BeautifulSoup | - | |
| 배포 (FE) | Vercel | - | |
| 배포 (Worker) | Google Cloud Run | - | |
| 큐 | Cloud Tasks / Bull (Redis) | - | 분석 작업 비동기 처리 |

---

## 2. 시스템 구조도

```
┌─────────────────────────────────────────────────────────────┐
│                        사용자 (웹 브라우저)                      │
└─────────────────────┬───────────────────────────────────────┘
                      │ HTTPS
┌─────────────────────▼───────────────────────────────────────┐
│                     Vercel (Next.js)                         │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │   Pages      │  │  API Routes  │  │  Server Actions   │  │
│  │ - 로그인     │  │ /api/keyword │  │ - 분석 요청       │  │
│  │ - 키워드입력 │  │ /api/analyze │  │ - 결과 조회       │  │
│  │ - 리포트     │  │ /api/report  │  │                   │  │
│  └──────────────┘  └──────┬───────┘  └──────────────────┘  │
└────────────────────────────┼────────────────────────────────┘
                             │ REST API
┌────────────────────────────▼────────────────────────────────┐
│                  Cloud Run (Python Worker)                    │
│  ┌──────────────────────────────────────────────────────┐   │
│  │                  분석 파이프라인                        │   │
│  │                                                       │   │
│  │  1. 키워드 생성 (Gemini API)                          │   │
│  │  2. 다단 키워드 필터 (로컬)                            │   │
│  │  3. 자동완성어 조회 (네이버 비공식 API)                 │   │
│  │  4. 검색량 조회 (네이버 검색광고 API)                   │   │
│  │  5. 연관검색어 조회 (HTML 크롤링)                      │   │
│  │  6. 블로그탭 크롤링 (HTML)                             │   │
│  │  7. 블로그 본문 분석:                                  │   │
│  │     - 텍스트 내 전화번호/주소 (정규식)                  │   │
│  │     - 이미지 OCR (Google Vision)                       │   │
│  │     - 외부링크/톡톡/플레이스 (HTML 파싱)               │   │
│  │     - 홍보성 판별 (Gemini LLM)                         │   │
│  │     - 첨부파일 감지 (HTML 파싱)                        │   │
│  │  8. 카페탭 대표뱃지 크롤링 (HTML)                      │   │
│  └──────────────────────────────────────────────────────┘   │
└────────────┬──────────────┬──────────────┬──────────────────┘
             │              │              │
    ┌────────▼──┐  ┌───────▼──────┐  ┌───▼────────────────┐
    │ Firestore │  │ Gemini API   │  │ 네이버 검색광고 API │
    │ (결과 DB) │  │ (LLM)       │  │ (검색량)            │
    └───────────┘  └──────────────┘  └────────────────────┘
                   ┌──────────────┐  ┌────────────────────┐
                   │ Google Vision│  │ 네이버 검색/블로그   │
                   │ (OCR)       │  │ (크롤링)            │
                   └──────────────┘  └────────────────────┘
```

---

## 3. API 설계

### 3-1. 키워드 생성 API

```
POST /api/keyword/generate
Body: { "topic": "암보험", "tier": 2 | 3 }
Response: {
  "keywords": [
    { "keyword": "암보험 비교", "tier": 2 },
    { "keyword": "암보험 추천 30대", "tier": 3 },
    ...
  ]
}
```

### 3-2. 분석 요청 API

```
POST /api/analyze
Body: {
  "keywords": ["암보험 비교", "암보험 추천", "암보험 가격", "암보험 30대", "암보험 비갱신형"],
  "userId": "user123"
}
Response: {
  "analysisId": "ana_abc123",
  "status": "queued",
  "estimatedTime": 120  // 초
}
```

### 3-3. 분석 결과 조회 API

```
GET /api/analyze/:analysisId
Response: {
  "analysisId": "ana_abc123",
  "status": "completed",
  "results": [
    {
      "keyword": "암보험 비교",
      "steps": {
        "step1_multiword": { "pass": true, "wordCount": 2 },
        "step2_related": { "pass": true, "relatedKeywords": ["실비보험", "유병자보험"] },
        "step3_autocomplete": { "pass": true, "suggestions": ["암보험 비교 사이트", "암보험 비교 추천"] },
        "step4_searchVolume": { "pass": true, "pcVolume": 5200, "mobileVolume": 18000 },
        "step5_promotional": { "pass": true, "ratio": 0.3, "details": [...] },
        "step6_externalBlog": { "pass": true, "externalCount": 0 },
        "step7_cafeBadge": { "pass": true, "representativeCount": 3 }
      },
      "verdict": "INFORMATIONAL",  // or "NOT_INFORMATIONAL"
      "passedSteps": 7,
      "totalSteps": 7
    }
  ]
}
```

### 3-4. 리포트 조회 API

```
GET /api/report/:analysisId
Response: {
  "report": {
    "summary": { "total": 5, "passed": 3, "failed": 2 },
    "keywords": [...],  // 상세 분석 결과
    "createdAt": "2026-03-04T12:00:00Z"
  }
}
```

---

## 4. 데이터 모델 (Firestore)

### Collection: `users`
```
users/{userId}
  - email: string
  - displayName: string
  - createdAt: timestamp
  - lastLoginAt: timestamp
```

### Collection: `analyses`
```
analyses/{analysisId}
  - userId: string (ref → users)
  - keywords: string[]  (선택된 5개 키워드)
  - topic: string  ("암보험")
  - status: "queued" | "processing" | "completed" | "failed"
  - createdAt: timestamp
  - completedAt: timestamp | null
  - results: map  (키워드별 7단계 분석 결과)
```

### Collection: `keyword_cache`
```
keyword_cache/{keyword_hash}
  - keyword: string
  - searchVolume: { pc: number, mobile: number }
  - autocomplete: string[]
  - relatedKeywords: string[]
  - cachedAt: timestamp
  - ttl: 7days  (검색량은 주 1회 갱신)
```

### Collection: `blog_analysis_cache`
```
blog_analysis_cache/{url_hash}
  - blogUrl: string
  - isPromotional: boolean
  - promotionalSignals: map
  - analyzedAt: timestamp
  - ttl: 30days
```

---

## 5. 분석 파이프라인 흐름

```
[사용자] 5개 키워드 선택 → "분석하기" 클릭
    ↓
[API] POST /api/analyze → analysisId 발급 → 큐에 추가
    ↓
[Worker] 키워드별 순차 분석 시작 (5개 직렬, 단계별 병렬 가능)
    ↓
[Step 1] 다단 키워드 판별 (로컬, 즉시)
    - 띄어쓰기 기준 단어 수 ≥ 2 → PASS
    ↓
[Step 2~4] OR 조건 (하나만 통과하면 PASS) — 병렬 실행
    ├─ [Step 2] 연관검색어 조회 (HTML 크롤링)
    ├─ [Step 3] 자동완성어 조회 (API 호출)
    └─ [Step 4] 검색량 조회 (검색광고 API)
    ↓
[Step 5] 블로그탭 TOP10 홍보성 분석
    5-1. 블로그탭 검색 결과 크롤링
    5-2. 광고 제거 → 비광고 TOP10 추출
    5-3. 각 블로그 본문 크롤링 (병렬 2~3개씩)
    5-4. 홍보성 판별 (5가지 기준):
         i)  전화번호/주소: 텍스트 정규식 + 이미지 OCR
         ii) 외부링크: HTML 파싱
         iii) 톡톡/플레이스: URL 패턴
         iv) 첨부파일: HTML 선택자
         v)  LLM 판별: Gemini API
    5-5. 홍보성 비율 계산 → 50% 이내면 PASS
    ↓
[Step 6] 외부블로그 부재 확인
    - Step 5에서 이미 추출한 TOP10 URL로 판별
    - blog.naver.com 외 도메인 존재 여부
    ↓
[Step 7] 카페탭 대표뱃지 카운트
    - 카페탭 크롤링 (ssc=tab.cafe.all)
    - TOP10 중 "대표" 뱃지 ≤ 5개면 PASS
    ↓
[결과] Firestore에 저장 → 상태 "completed" 업데이트
    ↓
[사용자] 폴링 or WebSocket으로 결과 수신 → 리포트 페이지 표시
```

---

## 6. 페이지 구조

```
/                     → 랜딩 페이지 (로그인 유도)
/login                → 로그인
/signup               → 회원가입
/dashboard            → 대시보드 (분석 이력)
/analyze              → 분석 시작
  /analyze/topic      → Step 1: 주제 입력 + 2단/3단 선택
  /analyze/select     → Step 2: LLM 생성 키워드 30개 중 5개 선택
  /analyze/progress   → Step 3: 분석 진행 상황 (실시간)
/report/:id           → 분석 리포트 상세
```

---

## 7. 보안 고려사항

- API 키는 환경변수로 관리 (Secret Manager 권장)
- Firebase Auth 토큰 검증 (API Routes에서)
- 크롤링 Rate Limit: 요청 간 300ms~1s 딜레이
- CORS 설정: 자체 도메인만 허용
- Firestore Security Rules: 사용자 본인 데이터만 접근

---

## 8. 성능 최적화

- **캐시**: 검색량/자동완성 결과 7일 캐시 (Firestore)
- **블로그 분석 캐시**: 동일 URL 30일 캐시
- **병렬 처리**: Step 2~4 병렬, 블로그 본문 크롤링 2~3개 동시
- **예상 소요시간**: 키워드 1개당 30~60초, 5개 총 2.5~5분
