# InsuWiki 기능 전수 조사 레지스트리

- **기준 커밋**: `20829a0` (`[task-321.1] 카드 편집창 3단계+ 계층 숫자 색상 옅게 조정`)
- **조사 기준일**: 2026-03-07
- **조사 대상**: `/home/jay/projects/insuwiki/` (node_modules, .next, dist, __pycache__ 제외)

---

## 목차

1. [인증 및 사용자 관리](#1-인증-및-사용자-관리)
2. [문서(Wiki) 시스템](#2-문서wiki-시스템)
3. [검색 시스템](#3-검색-시스템)
4. [AI 어시스턴트 (위키 전용)](#4-ai-어시스턴트-위키-전용)
5. [보험 약관 RAG 파이프라인](#5-보험-약관-rag-파이프라인)
6. [보험 약관 관리 (어드민)](#6-보험-약관-관리-어드민)
7. [YouTube 크롤링 파이프라인](#7-youtube-크롤링-파이프라인)
8. [음성 기능](#8-음성-기능)
9. [PWA / 공유 기능](#9-pwa--공유-기능)
10. [모니터링 및 보안](#10-모니터링-및-보안)
11. [Cloud Functions 자동화](#11-cloud-functions-자동화)
12. [관리자 패널](#12-관리자-패널)
13. [Firestore 컬렉션 구조](#13-firestore-컬렉션-구조)
14. [인프라 및 개발 환경](#14-인프라-및-개발-환경)

---

## 1. 인증 및 사용자 관리

### 1-1. Google OAuth 로그인

- **파일**: `nextapp/src/app/login/page.tsx`, `nextapp/src/contexts/AuthContext.tsx`
- **구현**:
  - Firebase Auth `signInWithPopup(GoogleAuthProvider)` 사용
  - 인앱 브라우저(Instagram, KakaoTalk 등) 감지 (`isInAppBrowser()`) → Android Chrome 강제 이동 안내
  - `returnUrl` 파라미터 + `sessionStorage` 기반 로그인 후 리다이렉트
  - 로그인 성공 시 `users` 컬렉션에 사용자 문서 자동 생성 (초기 role: `guest`)
  - `onSnapshot`으로 사용자 role 실시간 구독 (변경 즉시 반영)

### 1-2. 역할(Role) 기반 접근 제어

- **파일**: `nextapp/src/contexts/AuthContext.tsx`, `nextapp/src/lib/constants.ts`, `firestore.rules`
- **역할 체계**: `guest` / `member` / `admin`
- **ADMIN_EMAILS**: 하드코딩 상수로 관리 (`constants.ts`)
- **Firestore Rules**:
  - `isAuthenticated()`: 로그인 여부
  - `isAdmin()`: ADMIN_EMAILS 매칭 (서버 검증)
  - `isOwner(uid)`: 문서 작성자 본인 확인
  - `documents` 컬렉션: public/author/daily 패턴 기반 read, 작성자/admin만 update/delete
  - `insurance_chunks`, `rate_limits` 등 서버 전용 컬렉션 클라이언트 차단

### 1-3. 사용자 커스텀 이름

- **파일**: `nextapp/src/app/admin/users/page.tsx`
- 관리자가 사용자별 `customName` 설정 가능 (onBlur/Enter 저장)
- `formatAuthorName()` 유틸리티로 표시 이름 변환

---

## 2. 문서(Wiki) 시스템

### 2-1. 메인 대시보드

- **파일**: `nextapp/src/app/page.tsx`
- **3탭 구조**: Wiki(공개) / My(비공개+Daily) / Daily(오늘 일지)
- **카테고리 필터**: 의료 / 손해 / 자산 / 실무 / 전체 (5종)
- **뷰 모드 토글**: 카드 뷰 / 리스트 뷰
- **즐겨찾기**: `localStorage` 기반 별표 문서 상단 고정
- **커버 이미지**: 문서에서 첫 번째 이미지 자동 추출하여 카드 표시
- **문서 목록**: Firestore 쿼리 최대 50개, `updatedAt` 내림차순
- **스와이프 탭 전환**: `useSwipeableTabs` 훅으로 모바일 스와이프 지원
- **HubDocuments**: 상위 허브 문서 컴포넌트 (높은 `incomingLinkCount` 기준)
- **DailyNoteButton**: 오늘 날짜 Daily Note 바로가기 버튼

### 2-2. 문서 뷰어 / 편집기

- **파일**: `nextapp/src/app/docs/[id]/DocumentClient.tsx`, `nextapp/src/app/docs/[id]/useDocumentState.ts`
- **렌더링**: `react-markdown` + `remark-gfm` + `rehype-raw` (HTML 지원 Markdown)
- **에디터**: TipTap 기반 `ReflectEditor` (WYSIWYG)
- **TipTap 확장**:
  - `WikiLinkExtension`: `[[` 트리거 → WikiLink 입력 팝업 (tippy.js), `[[ID|Label]]` 및 `/docs/ID` 형식 양방향 파싱
  - `WikiAutoLink`: 위키 링크 자동 감지
  - `UnderlineMarkdown`: 밑줄 마크다운 지원
- **목차**: `TableOfContents` 컴포넌트 (헤딩 기반 자동 생성)
- **백링크 패널**: `BacklinksPanel` (이 문서를 링크한 문서 목록)
- **WikiLinkPreview**: 위키 링크 마우스 오버 시 미리보기 팝업

### 2-3. 문서 저장 및 버전 관리

- **파일**: `nextapp/src/app/docs/[id]/useDocumentState.ts`
- **Firestore 트랜잭션**: 낙관적 잠금 (`version` 필드 비교)
- **Revision 히스토리**: 5분 squash 로직 (5분 이내 재저장 시 이전 revision 덮어쓰기)
- **WikiLink 동기화**: 제목 변경 시 `outgoingLinks` 내 연결 문서 ID 일괄 업데이트
- **커버이미지 자동 추출**: 저장 시 본문에서 첫 번째 이미지 URL 추출하여 `coverImageUrl` 필드 저장
- **RevisionHistoryModal**: 개정 이력 시각화 모달

### 2-4. 문서 삭제 및 복원 (Soft Delete)

- **파일**: `nextapp/src/app/docs/[id]/useDocumentState.ts`, `nextapp/src/app/trash/TrashClient.tsx`
- **Soft Delete**: `isDeleted: true` 플래그 + `deletedAt`, `deletedBy` 기록
- **휴지통 페이지** (`/trash`):
  - 일반 사용자: 본인 삭제 문서만 조회
  - 관리자: 전체 삭제 문서 조회 (최대 200개)
  - 복원: `isDeleted: false` 업데이트
  - 영구삭제(Purge): `/api/admin/purge` API 호출 (관리자 또는 본인 문서)
- **문서 뷰어 삭제 배너**: 삭제된 문서 접근 시 배너 표시

### 2-5. 문서 공개 설정

- **Visibility 토글**: `public` / `private`
- **비공개 전환 차단**: 다중 기여자(`outgoingLinks` 타 사용자 편집 이력) 문서는 비공개 전환 불가
- **공개 전환 시 중복 제목 확인**: 동일 제목 공개 문서 존재 시 경고

### 2-6. Daily Notes

- **ID 패턴**: `daily-YYYY-MM-DD-{uid}`
- **항상 private**: Daily Note는 공개 불가
- **DailyNoteButton**: 오늘 날짜의 Daily Note 생성 또는 이동

### 2-7. 유령 문서(Phantom Document) 처리

- **파일**: `nextapp/src/app/docs/[id]/useDocumentState.ts`
- `new-{title}` 패턴: URL로 접근 시 해당 제목의 새 문서 생성 페이지로 처리
- `daily-{YYYY-MM-DD}-{uid}` 패턴: 해당 날짜의 Daily Note 자동 생성

### 2-8. 첨부파일 관리

- **파일**: `nextapp/src/app/api/upload/route.ts`, `nextapp/src/app/api/drive-image/[fileId]/route.ts`
- Google Drive에 사용자 이메일 별 폴더 자동 생성하여 업로드
- 파일 크기 제한: 50MB
- Drive Image 프록시: `/api/drive-image/[fileId]` 로 Drive 이미지 서빙
- 파일 삭제: `DELETE /api/upload` (Drive 파일 삭제)

### 2-9. 문서 잠금(Optimistic Locking)

- **파일**: `nextapp/src/hooks/useDocumentLock.ts`, `nextapp/src/utils/documentLock.ts`
- `editingBy` 필드 기반 선점형 잠금
- Heartbeat: 1분 주기 갱신
- 잠금 만료: 5분 (Heartbeat 미갱신 시)
- `ShadowIndicator` 컴포넌트: 다른 사용자가 편집 중임을 표시

### 2-10. 백링크 집계

- **파일**: `functions/src/aggregateBacklinks.ts`
- Cloud Function `aggregateBacklinks`: `documents` 컬렉션 `onWrite` 트리거
- `outgoingLinkIds` 변경 감지 → 대상 문서 `incomingLinkCount` ±1 배치 업데이트

### 2-11. 새 문서 생성

- **파일**: `nextapp/src/app/new/page.tsx`
- URL 파라미터 `?visibility=private`로 초기 visibility 설정
- 제목 입력 후 Firestore에 문서 생성 → 편집 페이지로 이동

---

## 3. 검색 시스템

### 3-1. 통합 검색 모달

- **파일**: `nextapp/src/components/search/SearchModal.tsx`
- **단축키**: `Ctrl+K` / `Cmd+K` 토글, `Escape` 닫기
- **커스텀 이벤트**: `open-search` 이벤트로 외부 버튼에서 열기 가능
- **3가지 모드**: 일반 검색 / AI 위키 검색 / 보험 약관 PDF 질의
- **전체 문서 로컬 캐싱**: 모달 최초 오픈 시 Firestore에서 최대 500개 문서 로드 후 클라이언트 필터링
- **키보드 네비게이션**: ArrowUp/Down + Enter

### 3-2. 일반 검색 (Normal Search)

- **파일**: `nextapp/src/components/search/NormalResults.tsx`, `nextapp/src/lib/utils/korean.ts`
- **한글 초성 검색**: `searchKorean()` 함수 (초성 분리 기반 매칭)
- **디바운스**: 입력 후 300ms 대기
- **정렬**: 제목 매칭 우선, 동일 가중치 시 `updatedAt` 내림차순
- 검색 결과 최대 20개

### 3-3. AI 위키 검색 (Semantic Search)

- **파일**: `nextapp/src/hooks/useSemanticSearch.ts`, `nextapp/src/app/api/ai/search-wiki/route.ts`
- BYOK Gemini API 키 사용 (`gemini-2.5-flash`)
- 위키 문서 embeddings 서브컬렉션 기반 코사인 유사도 검색 (임계값 0.6)
- `AIResults` 컴포넌트: 답변 + 출처 문서 목록 표시
- `AIEmptyState`: 검색 전 추천 질문 제공
- `FeedbackButtons`: AI 답변 정확도 피드백 (correct/incorrect/incomplete)

### 3-4. 보험 약관 PDF 질의 (InsurancePDFQuery)

- **파일**: `nextapp/src/components/search/InsurancePDFQuery.tsx`
- 검색 모달 내 보험 약관 질의 UI
- 연동: `/api/ai/query` → 쿼리 타입 분류 → B형/C형으로 라우팅

### 3-5. 위키맵 (WikiMap)

- **파일**: `nextapp/src/contexts/WikiMapContext.tsx`, `nextapp/src/hooks/useWikiMap.ts`
- 전체 공개 문서의 `{title: id}` 맵을 메모리에 유지
- WikiLink 입력 팝업의 자동완성에 활용
- Share Target 페이지의 문서 자동완성에도 활용

### 3-6. 보험 상품 자동완성

- **파일**: `nextapp/src/app/api/ai/autocomplete/route.ts`
- `insurance_metadata` 컬렉션에서 초성 검색(`getChoseong()`)
- 최대 10개 반환

---

## 4. AI 어시스턴트 (위키 전용)

### 4-1. AI 사이드패널

- **파일**: `nextapp/src/components/AISidepanel.tsx`
- 문서 편집 화면 우측에 표시되는 AI 보조 패널
- **6가지 AI 액션** (BYOK Gemini API 키 사용, `/api/ai/summarize`):
  1. **요약** (summarize): 문서 핵심 3-5 포인트 추출
  2. **구조화** (structure): 마크다운 섹션/불릿 재구성
  3. **정보 추출** (extract): 핵심 정보 JSON/테이블 변환
  4. **비교표** (compare): 비교 대상 마크다운 테이블 생성
  5. **PII 마스킹** (mask): 개인정보(주민번호, 전화번호, 이름) 자동 마스킹
  6. **코칭** (coach): 보험 상담 스크립트 피드백
- **refine**: 이전 AI 출력을 추가 지시사항으로 재처리

### 4-2. AI 요약 API

- **파일**: `nextapp/src/app/api/ai/summarize/route.ts`
- BYOK API 키 AES-256-GCM 복호화 후 Gemini `gemini-2.5-flash` 호출
- 프롬프트 종류: summarize / structure / compare / mask / coach / refine
- Rate limit 적용 (검증 후 진행)

### 4-3. Wiki 인덱싱 API

- **파일**: `nextapp/src/app/api/ai/index-wiki/route.ts`
- 위키 문서를 단락 기반으로 청킹 → `gemini-embedding-001`로 임베딩
- 문서 서브컬렉션 `embeddings`에 벡터 저장 (구 버전 일괄 삭제 후 갱신)

### 4-4. Wiki 벡터 검색 API

- **파일**: `nextapp/src/app/api/ai/search-wiki/route.ts`
- `collectionGroup('embeddings')` 전체 조회 + 클라이언트 코사인 유사도 계산 (임계값 0.6)
- 상위 5개 청크 → Gemini RAG 합성 (`gemini-2.5-flash`)

### 4-5. AI 설정 (BYOK)

- **파일**: `nextapp/src/app/api/ai/settings/route.ts`, `nextapp/src/utils/encryption.ts`
- 사용자별 Gemini API 키 저장/조회
- AES-256-GCM 암호화 (`AI_ENCRYPTION_KEY` 환경변수, 32바이트 필수)
- GET 시 Gemini API ping으로 키 유효성 검증 + 사용 가능 모델 목록 반환
- POST 시 키 업데이트 또는 삭제 (`FieldValue.delete()`)

### 4-6. 답변 검증 및 안전장치

- **파일**: `nextapp/src/lib/ai/answerValidator.ts`
- **유사도 임계값 게이트** (§ 8-1): TRUST(0.85+) / DISAMBIGUATION(0.70~0.85) / REJECT(0.70 미만)
- **시스템 프롬프트** (§ 8-2): 절대 변경 금지 (약관 원문 기반 답변 강제)
- **답변 자동 검증** (§ 8-3): 불확실 표현 감지, 출처 누락 감지, 결론만 있는 답변 차단
- **단정 표현 자동 치환** (§ 8 작업3): "보장됩니다" → "해당 조항에 따르면 보장되는 것으로 보입니다..." 등 8가지 패턴
- **PII 마스킹**: 주민번호, 전화번호, 이름+호칭 패턴 마스킹 (보험 약관 용어 제외 화이트리스트)
- **특약 경고 자동 삽입** (CL-10): 특약 관련 키워드 감지 시 보험증권 확인 안내 삽입
- **면책 문구**: 출처 타입별 맞춤 면책 문구 (policy/newsletter/youtube/conflict)

---

## 5. 보험 약관 RAG 파이프라인

### 5-1. 쿼리 라우터

- **파일**: `nextapp/src/lib/ai/queryRouter.ts`, `nextapp/src/app/api/ai/query/route.ts`
- **쿼리 분류** (5종):
  - `TABLE_QUERY` (A형): 테이블/구조화 데이터 조회 (미구현 placeholder)
  - `VECTOR_SEARCH` (B형): 벡터 유사도 검색 → `/api/ai/vector-search`로 위임
  - `DEEP_QUERY` (C형): 심층 분석 → Firestore `jobs` 컬렉션에 job 생성
  - `COMPLEX` (D형): 복합 쿼리 (처리 중)
  - `AMBIGUOUS`: 분류 불가 → Disambiguation 유도
- 키워드 기반 사전 분류 (`classifyQuery()`)

### 5-2. B형 벡터 검색 API

- **파일**: `nextapp/src/app/api/ai/vector-search/route.ts`
- **흐름**:
  1. 인젝션 필터 + Rate Limit 검증
  2. 보험 상품 자동완성(상품명 인식) → 미등록 상품 사전 차단
  3. 용어 정규화 (`insurance_terms` 동의어 확장)
  4. Gemini `text-embedding-004` 벡터화
  5. Firestore Vector Search (COSINE KNN)
  6. **CL-7**: `targetDate` 지정 시 `insurance_chunks_archive`에서 과거 버전 검색 (별도 엄격 Rate Limit)
  7. **CL-5**: 동적 임계값 게이트 (상품 특정: TRUST 0.92 / AMBIGUOUS 0.80, 일반: TRUST 0.80 / AMBIGUOUS 0.65)
  8. **CL-9**: 별표 키워드 트리거 → `insurance_appendices` 조회 → 컨텍스트 합산
  9. Gemini `gemini-2.0-flash` 답변 생성
  10. 단정 표현 치환 + PII 마스킹 + 면책 문구 삽입 + 특약 경고
  11. `query_logs` 감사 로그 저장
  12. `query_cache` 저장 (TTL 24시간, SHA-256 키)

### 5-3. C형 심층 분석 (RAG Query Cloud Function)

- **파일**: `functions/src/ragQuery.ts`
- `processInsuranceQuery`: `jobs` 컬렉션 `onDocumentCreated` 트리거 (type: `pdf_query`)
- **흐름**:
  1. `insurance_metadata`에서 Drive 파일 ID 로드
  2. `gemini_file_cache` 조회 (TTL 48시간) → Cache Hit 시 재사용
  3. Cache Miss 시 Google Drive → 임시파일 다운로드 → Gemini File API 업로드 → 캐시 저장
  4. Gemini `gemini-2.5-pro` 롱 컨텍스트 질의 (PDF 전체 업로드 방식)
  5. 답변 자동 검증 (불확실 표현, 출처 누락)
  6. `query_logs` 감사 로그 저장
  7. Job status `complete` 업데이트

### 5-4. 쿼리 캐시

- **파일**: `nextapp/src/lib/cache/queryCache.ts`
- Firestore `query_cache` 컬렉션
- 캐시 키: SHA-256(query) + productId + version (effectiveDate)
- TTL: 24시간 (Firestore TTL 정책 + 앱 레벨 이중 체크)
- 재인덱싱 시 해당 productId 캐시 전체 무효화

### 5-5. 과거 버전 조회 (CL-7)

- **파일**: `nextapp/src/lib/ai/versionFilter.ts`, `nextapp/src/app/api/ai/versions/route.ts`
- `insurance_chunks_archive` 컬렉션에 과거 버전 청크 보관
- `filterCurrentVersionMetadata()`: effectiveDateRange.end 없는 현행 버전 필터
- `filterMetadataByDate(targetDate)`: 특정 시점에 유효한 버전 필터
- `filterCurrentChunks()`: 현행 메타데이터 날짜와 매칭되는 청크 필터
- **버전 목록 API** (`GET /api/ai/versions?productId=xxx`): effectiveDate 내림차순 정렬

### 5-6. 피드백 수집

- **파일**: `nextapp/src/app/api/ai/feedback/route.ts`, `nextapp/src/components/search/FeedbackButtons.tsx`
- **PATCH** `/api/ai/feedback`: `query_logs` 문서의 `feedbackStatus` 업데이트
- **POST** `/api/ai/feedback`: `ai_feedback` 컬렉션에 상세 피드백 저장
  - 평가: `positive` / `negative`
  - 오류 유형: `exemption_missing` / `product_confusion` / `old_version` / `other`
  - 자유 텍스트: 최대 500자, HTML 태그 제거
  - 중복 피드백 차단 (queryId + userId 유니크)

---

## 6. 보험 약관 관리 (어드민)

### 6-1. PDF 업로드

- **파일**: `nextapp/src/app/admin/upload/page.tsx`, `nextapp/src/app/api/admin/drive-upload/route.ts`
- **UI**: 드래그&드롭 PDF 업로드, 파일명 규칙 사전 검증, 카테고리 오버라이드
- **파일명 규칙**: `{회사명}_{상품명}_{YYMM}.pdf`
- **Magic Bytes 검증**: PDF 헤더 `%PDF` 확인
- **Drive 폴더 구조**: `root/01_약관/{생명보험|손해보험|변액보험}/{회사명}/`
- **분산 락**: `upload_locks` 컬렉션, TTL 30분 (중복 업로드 방지)
- **롤백**: 인덱싱 실패 시 Drive 파일 삭제
- **`insurance_metadata` upsert** + **`jobs` 생성** (인덱싱 파이프라인 트리거)

### 6-2. PDF 인덱싱 파이프라인

- **파일**: `functions/src/pdfIndexing.ts`
- `onPdfIndexingJob`: Firestore `jobs` `onDocumentCreated` 트리거 (type: `index_pdf`)
- **타임아웃**: 540초, 메모리: 1GiB, 리전: `asia-northeast3`
- **흐름**:
  1. Google Drive → `pdf-parse` 텍스트 추출 (페이지당 `[PAGE N]` 마커 삽입)
  2. 스캔 PDF 감지: 페이지당 평균 50자 미만 → 조기 실패 (`스캔 PDF` 오류)
  3. `separateAppendixSections()`: 별표/부속서류 섹션 분리
  4. `structureAwareChunk()`: 구조 인식 청킹
     - 1차 분할: 제X조 기준
     - 2차 분할: 항(①②③) 기준
     - 단서조항 병합 (다만/단/그러나 키워드)
     - 면책 블록 병합
     - 양방향 300자 overlap
  5. `parseAppendixToMarkdownTable()`: 별표 텍스트 → Markdown 테이블 변환
  6. `embedWithRetry()`: Gemini `text-embedding-004` 임베딩 + 지수 백오프 retry
  7. **Blue-Green 인덱싱**: `blueGreenMode` 시 `insurance_chunks_staging`에 먼저 저장
  8. 용어 자동 추출 (`TERM_EXTRACTION_PROMPT`) → `insurance_terms` (verified: false)
  9. `summary_jobs` 생성 (요약 파이프라인 트리거)
  10. `logIndexingResult()`: `index_logs`에 품질 지표 기록

### 6-3. Blue-Green 재인덱싱

- **파일**: `nextapp/src/app/api/admin/insurance/reindex/route.ts`, `nextapp/src/app/api/admin/insurance/reindex/switch/route.ts`
- POST `/api/admin/insurance/reindex`: staging 데이터 정리 → `query_cache` 무효화 → `insurance_appendices` 정리 → 새 Blue-Green job 생성
- 스위칭 API: staging 컬렉션 → 실제 컬렉션으로 원자적 전환
- 어드민 UI에서 원클릭 재인덱싱 버튼 제공

### 6-4. 요약 파이프라인 (3단계)

- **파일**: `nextapp/src/app/api/admin/summary-generate/route.ts`, `nextapp/src/app/api/admin/summary-jobs/route.ts`, `scripts/summary-pipeline.ts`
- **Level 1**: 상품 전체 핵심 요약 (1개)
- **Level 2**: 섹션별 구조화 요약 (N개, `sectionTitle` + `pageRange`)
- `insurance_summaries` 컬렉션에 저장
- `summary_jobs` 컬렉션으로 파이프라인 상태 추적 (pending/processing/complete/failed)

### 6-5. 약관 목록 및 상세 관리

- **파일**: `nextapp/src/app/admin/terms/client.tsx`, `nextapp/src/app/admin/terms/[productId]/client.tsx`
- 카테고리 필터 (생명/손해/변액) + 상품명/회사명 검색
- Firestore `onSnapshot` 실시간 업데이트
- 상태 배지: 요약 대기/요약 중/요약 완료/요약 실패
- 스캔 PDF 오류 시 OCR 필요 안내 배지 표시
- 상세: 청크 목록 조회, 이력 조회, 전문 검색 (`/admin/terms/[productId]/search`)

### 6-6. 인덱싱 품질 로깅

- **파일**: `nextapp/src/lib/monitoring/indexLogger.ts`
- `logIndexingResult()`: `index_logs` 컬렉션에 저장
- 품질 지표: 평균 청크 크기, 단서조항 보존율, 총 조항 수, 청크 수 변화
- Blue-Green 스위칭 시각 기록

---

## 7. YouTube 크롤링 파이프라인

### 7-1. YouTube 채널 크롤링

- **파일**: `functions/src/crawlYoutubeChannels.ts`
- Cloud Function `crawlYoutubeChannels`: **6시간마다** 실행 (Cloud Scheduler)
- `youtube_channels` 컬렉션에서 `isActive: true` 채널 로드
- YouTube Data API로 채널별 최신 영상 목록 조회
- 이미 처리된 영상 스킵 (`youtube_transcripts` 존재 여부 확인)

### 7-2. 자막 추출 3단계 폴백

- **파일**: `functions/src/crawlYoutubeChannels.ts`, `functions/src/whisperStt.ts`
- **1단계**: InnerTube API (Android UA) → 한국어 자막 XML 파싱
- **2단계**: OpenAI Whisper STT 폴백 (`whisperStt.ts`)
  - YouTube 페이지 HTML → `ytInitialPlayerResponse` 파싱 → 오디오 스트림 URL 추출
  - 최저 비트레이트 오디오 선택 (파일 크기 최소화)
  - 25MB 초과 시 스킵
  - `whisper-1` 모델, `language=ko`, `response_format=text`
  - 5분 타임아웃
- **3단계**: 영상 제목 + 설명 텍스트 최종 폴백

### 7-3. 요약 및 저장

- **파일**: `functions/src/crawlYoutubeChannels.ts`
- **L3 청크 저장**: 500자, 50자 overlap → `youtube_transcripts` 컬렉션
- **L2 구조화 요약**: Gemini `gemini-2.5-flash`로 영상별 구조화 요약
  - `SKIP_GEMINI_SUMMARY` 플래그: 요약 스킵 → `youtube_summary_queue` 대기 등록
- **Drive 업로드**: `요약_요약.md` + `_전문.md` 파일 저장
- **`insurance_chunks` 저장**: `sourceType: 'youtube'`로 보험 청크에 통합
- **`youtube_summaries` 저장**: level L2 구조화 요약
- **약관 충돌 감지**: `conflictsWithPolicy` 필드, 유사도 0.95 기준 충돌 플래그

### 7-4. YouTube L1 채널 인사이트 생성

- **파일**: `functions/src/generateYoutubeChannelInsight.ts`
- HTTP Cloud Function `generateYoutubeChannelInsight`
- **흐름**:
  1. 활성 채널별 L2 요약 최신 20개 조회
  2. L2 3개 미만이면 스킵
  3. 기존 L1 존재 시: 최근 L1 이후 새 L2가 없으면 스킵 (증분 업데이트)
  4. L2 요약 결합 → Gemini `gemini-2.5-flash` L1 인사이트 생성
  5. `gemini-embedding-001` 임베딩 생성
  6. `youtube_summaries` (level: L1) 저장
- **L1 분석 항목**: 채널 핵심 주제 / 반복 등장 키워드·상품 / 채널 관점·논조 / 설계사 활용 가이드 / 주의 필요 콘텐츠

---

## 8. 음성 기능

### 8-1. 음성 명령 시스템

- **파일**: `nextapp/src/hooks/useVoiceCommand.ts`, `nextapp/src/contexts/VoiceCommandContext.tsx`, `nextapp/src/components/VoiceCommandProvider.tsx`
- Web Speech API (`useSpeechRecognition`) 기반
- **31개 명령어, 6개 그룹**:
  - 내비게이션 (4): 새 문서, 홈으로, 뒤로 가, 데일리 노트
  - 검색 (4): 검색, AI 검색, 위키 검색, 음성 검색
  - 에디터 (7): 편집, 편집 취소, 저장, 줄 바꿔, 새 문단, 할 일, 되돌려
  - 문서 관리 (5): 공개해, 비공개, 삭제, 히스토리, AI 열어
  - 녹음 제어 (5): 받아쓰기, 중지, 일시정지, 녹취 시작, 녹취 종료
  - AI 트리거 (6): 요약해줘, 구조화해줘, 정보 추출, 비교해줘, 개인정보 가려, 코칭해줘
- 더 긴 패턴 우선 매칭 (최장 일치)
- 중복 명령 3초 쿨다운
- `VoiceCommandContextProvider`: 페이지별 핸들러 등록/해제

### 8-2. 상담 녹음 (AudioRecorder)

- **파일**: `nextapp/src/components/AudioRecorderButton.tsx`, `nextapp/src/hooks/useAudioRecorder.ts`
- 문서 편집 화면 FAB(Floating Action Button)
- `RecordingConsentModal`: 녹음 전 동의 절차
- MediaRecorder API + Google Drive 업로드
- 녹음 완료 시 Drive 파일 링크를 문서 본문에 자동 삽입

### 8-3. AIWhispers (음성 전사)

- **파일**: `nextapp/src/components/AIWhispers.tsx`
- AI 사이드패널 내 음성 전사 컴포넌트
- 음성 명령 자동 실행 (`autoAction`)

---

## 9. PWA / 공유 기능

### 9-1. PWA 구성

- **파일**: `nextapp/public/sw.js`, `nextapp/src/components/ServiceWorkerRegistrar.tsx`, `nextapp/src/app/layout.tsx`
- `manifest.json` (앱 이름, 아이콘, 테마 색상)
- Apple 웹앱 메타데이터 (`appleWebApp: true`)
- Service Worker 등록 (`ServiceWorkerRegistrar` 컴포넌트)

### 9-2. Web Share Target

- **파일**: `nextapp/src/app/share-target/page.tsx`, `nextapp/src/app/api/share-target/route.ts`
- PWA Web Share Target 수신 페이지 (`/share-target`)
- URL + OG 제목 수신 → Daily Note 또는 My 노트 저장 선택
- My 노트 선택 시 기존 문서 검색 (초성 + Fuse.js 퍼지 검색, 임계값 0.3)
- 없으면 신규 문서 생성
- 로그인 필요 (미로그인 시 returnUrl 포함 로그인 페이지 리다이렉트)

---

## 10. 모니터링 및 보안

### 10-1. API 사용량 모니터링

- **파일**: `nextapp/src/lib/monitoring/costMonitor.ts`
- `api_usage_daily` 컬렉션에 일별 집계
- 추적 항목: Firestore Read/Write, Gemini Embedding/Generation 호출 수, 쿼리 수, 캐시 Hit/Miss
- **원자적 카운터**: `FieldValue.increment()` 사용
- **시간대별 분포**: `hourlyQueryCounts` (00~23시, UTC 기준)
- **비용 추정**: 단위당 달러 비용 곱 (embed: $0.000001, generation: $0.0000075)
- 기간별 조회: daily(1일) / weekly(7일) / monthly(30일)

### 10-2. 경고 시스템

- **파일**: `nextapp/src/lib/monitoring/alerting.ts`
- `checkDailyCostAlert()`: 일일 비용 $5 초과 또는 5,000 쿼리 초과 시 경고
- `detectAnomalousTraffic()`: 현재 시간 쿼리 수가 평균 대비 3배 이상 시 이상 감지

### 10-3. 모니터링 대시보드 API

- **파일**: `nextapp/src/app/api/admin/monitoring/route.ts`
- 관리자 전용 `GET /api/admin/monitoring?period=daily|weekly|monthly&date=YYYY-MM-DD`
- 사용량 + 비용 경고 + 트래픽 이상 병렬 조회

### 10-4. Rate Limiting

- **파일**: `nextapp/src/lib/security/rateLimiter.ts`
- **슬라이딩 윈도우** 방식, uid + IP 이중 체크
- **일반 제한**: minute(10회) / hour(100회) / day(500회)
- **과거 버전 전용 엄격 제한** (CL-7): minute(5회) / hour(30회) / day(100회)
- Firestore `rate_limits` 컬렉션 (클라이언트 접근 차단)

### 10-5. 인젝션 필터

- **파일**: `nextapp/src/lib/security/injectionFilter.ts`
- **영문 패턴 8개** + **한국어 패턴 7개** 총 15개 정규식
- **심각도 분류**: high(시스템 프롬프트 탈취) / medium(역할 변경) / low(경계 패턴)
- **화이트리스트**: 보험 도메인 컨텍스트 키워드 10개 (보험/약관/담보/보장/청구 등)가 함께 있으면 오탐 방지
- **입력 정제** (`sanitizeInput()`): HTML 태그 제거, 연속 공백 정규화, 최대 2000자 제한

### 10-6. 캐시 무효화 API

- **파일**: `nextapp/src/app/api/ai/invalidate-cache/route.ts`
- 특정 상품의 `query_cache` 일괄 삭제

---

## 11. Cloud Functions 자동화

| 함수명 | 트리거 | 주기/조건 | 역할 |
|--------|--------|-----------|------|
| `cleanupExpiredLocks` | Cloud Scheduler | 매 10분 | 30분 초과 편집 잠금 자동 해제 |
| `releaseDocumentLock` | HTTP Callable | 수동 호출 | 본인 잠금 즉시 해제 |
| `aggregateBacklinks` | Firestore onWrite | documents 변경 시 | outgoingLinkIds 변경 → incomingLinkCount 배치 업데이트 |
| `scheduledFirestoreExport` | Cloud Scheduler | 매일 04:00 KST | GCS 버킷 Firestore 전체 백업 |
| `processInsuranceQuery` | Firestore onDocumentCreated | jobs 컬렉션 생성 시 | C형 심층 분석 (PDF 롱 컨텍스트) |
| `onPdfIndexingJob` | Firestore onDocumentCreated | jobs 컬렉션 생성 시 | PDF 인덱싱 파이프라인 |
| `crawlYoutubeChannels` | Cloud Scheduler | 6시간마다 | YouTube 자막 추출 + 요약 + 저장 |
| `generateYoutubeChannelInsight` | HTTP | 수동/정기 호출 | 채널별 L1 통합 인사이트 생성 |

---

## 12. 관리자 패널

### 12-1. 관리자 레이아웃

- **파일**: `nextapp/src/app/admin/layout.tsx`
- 관리자 권한 체크 → 미권한 시 메인 리다이렉트

### 12-2. 사용자 관리

- **파일**: `nextapp/src/app/admin/users/page.tsx`
- 전체 사용자 목록 실시간 조회
- 역할 변경: guest/member/admin (본인 제외)
- 커스텀 이름 설정 (onBlur/Enter)

### 12-3. 약관 관리

- **파일**: `nextapp/src/app/admin/terms/` (5개 페이지)
- 약관 목록 + 카테고리 필터 + 검색
- 상품별 상세: 청크 목록 / 히스토리 / 전문 검색
- 원클릭 재인덱싱 + 결과 메시지
- 약관 통계 페이지 (`/admin/terms/stats`)

### 12-4. 모니터링

- **파일**: `nextapp/src/app/admin/monitoring/page.tsx`
- `query_logs` 기반 통계 (총 쿼리, 성공, 실패, 추정 토큰)
- 시스템 상태 표시
- daily/weekly/monthly 기간 선택

### 12-5. 관리자 공통 인증

- Bearer Token 검증 + `ADMIN_EMAILS` 화이트리스트 체크
- 모든 `/api/admin/*` 라우트에 적용

---

## 13. Firestore 컬렉션 구조

| 컬렉션명 | 용도 | 비고 |
|----------|------|------|
| `documents` | Wiki 문서 | 핵심 데이터, revision 서브컬렉션, embeddings 서브컬렉션 |
| `users` | 사용자 정보 | role, customName, aiSettings(암호화된 API 키) |
| `insurance_metadata` | 보험 상품 메타데이터 | companyId, productId, effectiveDateRange, driveFileId |
| `insurance_chunks` | 보험 약관 청크 + 임베딩 | Firestore Vector Search 인덱스 |
| `insurance_chunks_staging` | Blue-Green 스테이징 | 재인덱싱 임시 컬렉션 |
| `insurance_chunks_archive` | 과거 버전 청크 | CL-7 과거 버전 조회용 |
| `insurance_appendices` | 별표/부속서류 청크 | CL-9 별표 키워드 트리거용 |
| `insurance_summaries` | 3단계 요약 (Level 1/2) | 보험 상품 요약 |
| `insurance_terms` | 보험 용어 사전 | verified 플래그, 동의어 확장 |
| `jobs` | 비동기 작업 (인덱싱/질의) | type: index_pdf / pdf_query |
| `summary_jobs` | 요약 작업 | status: pending/processing/complete/failed |
| `query_logs` | AI 질의 감사 로그 | feedbackStatus, confidenceScore |
| `query_cache` | AI 질의 캐시 | TTL 24시간, SHA-256 키 |
| `rate_limits` | Rate Limit 카운터 | 슬라이딩 윈도우 |
| `gemini_file_cache` | Gemini File API 캐시 | TTL 48시간, Drive 파일 재업로드 방지 |
| `upload_locks` | PDF 업로드 분산 락 | TTL 30분 |
| `api_usage_daily` | API 사용량 일별 집계 | 비용 추정 포함 |
| `index_logs` | 인덱싱 품질 로그 | 청크 수 변화, 단서조항 보존율 |
| `ai_feedback` | AI 답변 사용자 피드백 | rating, errorType, freeText |
| `youtube_channels` | 크롤링 대상 채널 | isActive 플래그 |
| `youtube_transcripts` | YouTube 영상 L3 청크 | 500자 청크, 50자 overlap |
| `youtube_summaries` | YouTube 요약 (L1/L2) | level: L1(채널)/L2(영상), embedding |
| `youtube_summary_queue` | 요약 대기열 | SKIP_GEMINI_SUMMARY 플래그 시 |

---

## 14. 인프라 및 개발 환경

### 14-1. 기술 스택

| 영역 | 기술 |
|------|------|
| Frontend Framework | Next.js 16.1.6 (App Router) |
| UI Library | React 19.2.3 |
| Styling | Tailwind CSS v4 |
| Rich Text Editor | TipTap + tiptap-markdown |
| Markdown Rendering | react-markdown + remark-gfm + rehype-raw |
| AI SDK | @google/generative-ai ^0.24.1 |
| Backend | Firebase Cloud Functions v1/v2 |
| Database | Firestore (Vector Search 포함) |
| Storage | Google Drive API v3 |
| Auth | Firebase Authentication |
| Deployment | Vercel (Next.js), Firebase Functions |
| Test Framework | vitest (nextapp), Jest (functions) |

### 14-2. AI 모델 사용 현황

| 용도 | 모델 |
|------|------|
| 임베딩 (약관 청크) | `text-embedding-004` |
| 임베딩 (YouTube L1) | `gemini-embedding-001` |
| B형 벡터 검색 답변 | `gemini-2.0-flash` |
| AI 위키 검색 | `gemini-2.5-flash` |
| AI 사이드패널 (요약 등) | `gemini-2.5-flash` |
| YouTube 요약 (L2) | `gemini-2.5-flash` |
| YouTube L1 인사이트 | `gemini-2.5-flash` |
| C형 심층 분석 | `gemini-2.5-pro` |
| Whisper STT (폴백) | OpenAI `whisper-1` |

### 14-3. Firebase Emulator 구성

| 서비스 | 포트 |
|--------|------|
| Firebase Auth | 9099 |
| Firestore | 8080 |
| Cloud Functions | 5001 |
| Emulator UI | 4000 |

환경변수 `NEXT_PUBLIC_USE_EMULATOR=true` 시 자동 연결

### 14-4. 환경변수 (주요)

| 변수명 | 용도 |
|--------|------|
| `GEMINI_API_KEY` | Cloud Functions에서 사용 (서버 관리형) |
| `AI_ENCRYPTION_KEY` | BYOK API 키 AES-256-GCM 암호화 (32바이트 필수) |
| `FIREBASE_SERVICE_ACCOUNT_KEY` | Firebase Admin SDK 인증 |
| `DRIVE_CLIENT_ID/SECRET/REFRESH_TOKEN` | Google Drive OAuth2 인증 |
| `OPENAI_API_KEY` | Whisper STT (선택적, 없으면 STT 스킵) |
| `NEXT_PUBLIC_USE_EMULATOR` | Firebase Emulator 연결 토글 |

### 14-5. 스크립트

| 파일 | 용도 |
|------|------|
| `scripts/summary-pipeline.ts` | 보험 약관 요약 파이프라인 실행 |
| `scripts/aggregate-backlinks.ts` | 백링크 집계 수동 실행 |
| `scripts/seed-insurance-metadata.ts` | 보험 메타데이터 시드 |
| `scripts/seed-youtube-channels.ts` | YouTube 채널 시드 |
| `scripts/youtube-get-pending.ts` | 요약 대기 YouTube 영상 조회 |
| `scripts/youtube-upload-summary.ts` | YouTube 요약 수동 업로드 |
| `scripts/fix-author-names.ts` | 작성자 이름 일괄 수정 |
| `scripts/delete-old-youtube-docs.js` | 구 YouTube 문서 정리 |
| `nextapp/scripts/backfill-soft-delete.js` | isDeleted 필드 백필 |
| `functions/src/seed-drive-dummy-files.ts` | Drive 더미 파일 시드 |

### 14-6. 테스트 구성

- **vitest** (`nextapp/vitest.config.ts`): 단위 테스트 (hangul, korean, text, encryption, constants, answerValidator, versionFilter, cl7-version-history)
- **Cloud Functions 테스트** (`functions/src/__tests__/`): pdfIndexing, appendixDetection, appendixTrigger, reindexApi, youtubeWhisper

---

*이 문서는 2026-03-07 기준 커밋 `20829a0`의 소스 코드를 전수 분석하여 작성되었습니다.*
