diff --git a/CLAUDE.md b/CLAUDE.md index 5be60d7..143a907 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. -## Auto-Read on Start +--- + +## 1. Quick Start + +### Auto-Read on Start **세션 시작 시 반드시 수행:** 1. `README.md`를 Read 도구로 읽어 프로젝트 전체 구조 파악 @@ -15,9 +19,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co 필수 읽기: README.md → 작업 대상 파일 ``` ---- - -## Agent Usage Policy +### Agent Usage Policy **🎯 목표: 컨텍스트 절약 - 대부분의 작업을 에이전트에 위임** @@ -28,27 +30,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - 주요 디렉토리: `src/tools/`, `src/routes/`, `src/services/` **사용 가능한 에이전트 타입:** -- `coder`: 20년 이상의 경험을 갖고 있는 시니어 코딩 전문가 (코드 작성/수정 최우선) - - TypeScript/Cloudflare Workers 구현 마스터 - - 프로덕션 수준의 코드 품질 보장 - - 엔터프라이즈급 에러 핸들링 및 타입 안정성 - - 성능 최적화 및 베스트 프랙티스 적용 - - 도구: Read, Write, Edit, Bash, Glob, Grep (쓰기 가능) -- `explorer`: 코드베이스 탐색/분석 전문가 (thorough 레벨) - - 프로젝트 구조 파악 및 의존성 분석 - - 대량 파일 읽기 및 패턴 인식 - - 도구: Read, Grep, Glob (읽기 전용) +- `coder`: 코드 작성/수정 (TypeScript/Workers 전문, 프로덕션 품질) +- `explorer`: 코드베이스 탐색/분석 (thorough 레벨) - `planner`: 설계/계획 수립 전문가 - - 아키텍처 설계 및 구현 계획 작성 - - 시스템 분석 및 개선 방향 제시 - - 도구: Read, Grep, Glob (읽기 전용) -- `reviewer`: 코드 리뷰 전문가 - - 품질/보안/성능 검토 - - 페르소나 조합으로 전문화 (qa/security/performance) - - 도구: Read, Grep, Glob (읽기 전용) -- `Bash`: 빌드/배포/테스트 실행 - - 긴 로그 출력 분리 (컨텍스트 절약) - - 시스템 명령어 실행 +- `reviewer`: 코드 리뷰 (qa/security/performance 페르소나 조합) +- `Bash`: 빌드/배포/테스트 실행 (긴 로그 출력 분리) **CRITICAL: 다음 작업은 반드시 Task tool (agent)를 사용하여 메인 세션 컨텍스트 절약:** @@ -75,28 +61,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - 다른 디렉토리 동시 작업 → 병렬 `coder` 에이전트 - Function Calling 도구 추가 → tools/{new}.ts + openai-service.ts 병렬 -**예시:** -```typescript -// ❌ 직접 수정 (컨텍스트 소모) -Read src/tools/weather-tool.ts -Edit src/tools/weather-tool.ts -Read src/openai-service.ts -Edit src/openai-service.ts - -// ✅ coder 에이전트 사용 (컨텍스트 절약 + 전문성) -Task (subagent_type: "coder", 2개 병렬) - → 독립 컨텍스트에서 작업 → 요약만 반환 - → TypeScript 최적화, Workers 패턴 준수 -``` - **직접 처리 (최소화):** - 간단한 문서 읽기 (README.md 확인) - 사용자와의 대화/질문 - 에이전트 작업 조율/검토 ---- - -## Critical Rules +### Critical Rules **절대 지켜야 할 규칙:** @@ -112,77 +82,24 @@ Task (subagent_type: "coder", 2개 병렬) - `wrangler d1 execute` 직접 실행 (production) → 반드시 확인 요청 - `user_deposits`, `deposit_transactions` 테이블 직접 수정 → 금전 관련, 주의 ---- - -## Documentation Rules +### Documentation Rules **작업 완료 후 문서 자동 업데이트:** -### 트리거 조건 | 변경 유형 | 업데이트 대상 | |-----------|---------------| -| `src/*.ts` 파일 수정 | CLAUDE.md (Core Services, Key Patterns) | +| `src/*.ts` 파일 수정 | CLAUDE.md (Architecture, Key Patterns) | | 새 Function Calling 도구 추가 | 양쪽 (README: 지원 기능 테이블, CLAUDE: tools 목록) | | `schema.sql` 변경 | 양쪽 (Data Layer 섹션) | | `wrangler.toml` 환경변수 추가 | 양쪽 (Configuration 섹션) | | 외부 API 연동 추가/변경 | 양쪽 (External Integrations) | | 봇 명령어 추가 | 양쪽 (Commands 섹션) | -### 업데이트 체크리스트 -``` -[ ] CLAUDE.md - 기술 상세 (개발자용) -[ ] README.md - 사용자 가이드 (배포/운영자용) -[ ] 다이어그램/플로우 수정 필요 여부 -[ ] wrangler.toml 주석 업데이트 -``` - --- -## API Documentation +## 2. Commands & Setup -**OpenAPI Specification**: `openapi.yaml` - -**문서 보기**: -```bash -# Swagger UI로 보기 (로컬) -npx swagger-ui-watcher openapi.yaml - -# Redoc으로 HTML 생성 -npx redoc-cli bundle openapi.yaml -o docs/api.html - -# OpenAPI 스펙 검증 -npx @apidevtools/swagger-cli validate openapi.yaml -``` - -**주요 엔드포인트**: -| 엔드포인트 | 메서드 | 인증 | 용도 | -|-----------|--------|------|------| -| `/health` | GET | None | Health check (최소 정보만) | -| `/webhook-info` | GET | Query Param | Telegram Webhook 상태 조회 | -| `/setup-webhook` | GET | Query Param | Telegram Webhook 설정 | -| `/api/contact` | POST | CORS | 웹사이트 문의 폼 | -| `/api/deposit/balance` | GET | API Key | 잔액 조회 (Internal) | -| `/api/deposit/deduct` | POST | API Key | 잔액 차감 (Internal) | -| `/api/metrics` | GET | Bearer | Circuit Breaker 상태 | - -**인증 방식**: -- **API Key**: `X-API-Key: {DEPOSIT_API_SECRET}` (Deposit API) -- **Bearer**: `Authorization: Bearer {WEBHOOK_SECRET}` (Metrics) -- **Query Param**: `?token={BOT_TOKEN}&secret={WEBHOOK_SECRET}` (Webhook) -- **CORS**: `hosting.anvil.it.com`만 허용 (Contact Form) - -**External Consumers**: -- **namecheap-api**: `/api/deposit/*` 호출 (도메인 등록 시 잔액 조회/차감) -- **hosting.anvil.it.com**: `/api/contact` 호출 (웹사이트 문의 폼) -- **Monitoring Tools**: `/api/metrics` 조회 (시스템 상태 모니터링) - -**Rate Limiting**: -- 사용자별 30 requests / 60초 (Telegram 메시지) -- KV Namespace 기반 분산 Rate Limiting - ---- - -## Commands +### NPM Scripts ```bash npm run dev # 로컬 개발 (wrangler dev) @@ -195,108 +112,442 @@ npm run test:watch # Watch 모드 npm run test:coverage # 커버리지 리포트 ``` -**KV Namespace 생성 (최초 1회):** +### KV Namespace 생성 + ```bash -# Rate Limiting용 KV Namespace 생성 (필수) +# Rate Limiting용 (필수) wrangler kv:namespace create RATE_LIMIT_KV -# 서버 상담 세션용 KV Namespace 생성 (서버 추천 기능 사용 시 필수) +# 서버 상담 세션용 (서버 추천 기능 사용 시 필수) wrangler kv:namespace create SESSION_KV # 출력된 id를 wrangler.toml의 [[kv_namespaces]] 섹션에 입력 ``` -**Secrets 설정:** +### Secrets 설정 + ```bash -wrangler secret put BOT_TOKEN # Telegram Bot Token -wrangler secret put WEBHOOK_SECRET # Webhook 검증용 -wrangler secret put OPENAI_API_KEY # OpenAI API 키 -wrangler secret put NAMECHEAP_API_KEY # namecheap-api 래퍼 인증 키 -wrangler secret put NAMECHEAP_API_KEY_INTERNAL # Namecheap API 키 (내부용) -wrangler secret put BRAVE_API_KEY # Brave Search API 키 -wrangler secret put DEPOSIT_API_SECRET # Deposit API 인증 키 +wrangler secret put BOT_TOKEN # Telegram Bot Token +wrangler secret put WEBHOOK_SECRET # Webhook 검증용 +wrangler secret put OPENAI_API_KEY # OpenAI API 키 +wrangler secret put NAMECHEAP_API_KEY # namecheap-api 래퍼 인증 키 +wrangler secret put NAMECHEAP_API_KEY_INTERNAL # Namecheap API 키 (내부용) +wrangler secret put BRAVE_API_KEY # Brave Search API 키 +wrangler secret put DEPOSIT_API_SECRET # Deposit API 인증 키 ``` -**Webhook 설정:** +### Webhook 설정 + ```bash -# Webhook 설정 (token + secret 필요) +# Webhook 설정 curl "https://telegram-summary-bot.kappa-d8e.workers.dev/setup-webhook?token=${BOT_TOKEN}&secret=${WEBHOOK_SECRET}" -# Webhook 정보 조회 (token + secret 필요) +# Webhook 정보 조회 curl "https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info?token=${BOT_TOKEN}&secret=${WEBHOOK_SECRET}" ``` -**Database Migrations:** +### Database Migrations + +**마이그레이션 목록:** +| 파일 | 설명 | +|------|------| +| `001_optimize_prefix_indexes.sql` | 입금자명 prefix 인덱스 최적화 (99% 성능 향상) | +| `002_add_version_columns.sql` | Optimistic Locking (user_deposits.version) | +| `003_add_server_tables.sql` | server_orders, server_specs 테이블 추가 | +| `004_add_terminated_at.sql` | server_orders.terminated_at 컬럼 추가 | +| `005_add_stopped_status.sql` | server_orders 테이블에 'stopped' 상태 추가 | + +**실행:** ```bash # 로컬 테스트 wrangler d1 execute telegram-conversations --local --file=migrations/001_optimize_prefix_indexes.sql # 프로덕션 적용 ⚠️ 주의: 데이터 백업 권장 wrangler d1 execute telegram-conversations --file=migrations/001_optimize_prefix_indexes.sql - -# 롤백 (필요 시) -wrangler d1 execute telegram-conversations --file=migrations/001_rollback.sql -``` - -**마이그레이션 목록:** -| 파일 | 설명 | 적용일 | -|------|------|--------| -| `001_optimize_prefix_indexes.sql` | 입금자명 prefix 인덱스 최적화 (99% 성능 향상) | 2026-01-19 | -| `002_add_version_columns.sql` | Optimistic Locking (user_deposits.version) | 2026-01-20 | -| `003_add_server_tables.sql` | server_orders, server_specs 테이블 추가 | 2026-01-28 | -| `004_add_terminated_at.sql` | server_orders.terminated_at 컬럼 추가 | 2026-01-28 | -| `005_add_stopped_status.sql` | server_orders 테이블에 'stopped' 상태 추가 | 2026-01-29 | - -**마이그레이션 작업 내용 (001):** -- `deposit_transactions.depositor_name_prefix` 컬럼 추가 -- `bank_notifications.depositor_name_prefix` 컬럼 추가 -- Partial Index 2개 생성 (pending 거래, unmatched 알림) -- 기존 데이터 backfill (SUBSTR 함수로 자동 채우기) -- 성능: Full Table Scan → Index Scan - -**마이그레이션 작업 내용 (005):** -- `server_orders` 테이블 CHECK constraint 수정 (SQLite 제약사항으로 테이블 재생성) -- status 값에 'stopped' 추가 (pending, provisioning, active, stopped, failed, cancelled, terminated) -- 모든 인덱스 재생성 (idx_server_orders_user, idx_server_orders_status, idx_server_orders_idempotency_unique) -- 기존 데이터 보존 (임시 테이블 사용) - -**검증 명령 (001):** -```sql --- 인덱스 사용 확인 -EXPLAIN QUERY PLAN -SELECT * FROM deposit_transactions -WHERE status = 'pending' AND type = 'deposit' - AND depositor_name_prefix = '홍길동아버' AND amount = 10000; - --- 결과에 "USING INDEX idx_transactions_prefix_pending" 포함되어야 함 -``` - -**검증 명령 (005):** -```sql --- CHECK constraint 확인 (stopped 상태 포함 여부) -SELECT sql FROM sqlite_master WHERE name = 'server_orders'; - --- 인덱스 존재 확인 -SELECT name FROM sqlite_master -WHERE type = 'index' AND tbl_name = 'server_orders'; - --- stopped 상태 테스트 (에러 없이 성공해야 함) --- INSERT INTO server_orders (user_id, spec_id, status, region, price_paid) --- VALUES (1, 1, 'stopped', 'Tokyo', 1000); ``` --- -## Code Style & Conventions +## 3. Architecture -### TypeScript -- **Strict mode**: `tsconfig.json`에서 strict 활성화 -- **타입 정의**: `types.ts`에 인터페이스 집중 관리 -- **any 사용 금지**: 불가피한 경우 주석으로 이유 명시 +### Message Flow -### 에러 핸들링 +``` +Telegram Webhook → Security Validation → Command/Message Router + ↓ + ┌──────────────────────────┴──────────────────────────┐ + ↓ ↓ + Command Handler AI Response Generator + (commands.ts) (openai-service.ts) + ↓ + Function Calling (9개) + (weather, search, time, calc, docs, + domain, suggest_domains, deposit, server) + ↓ + Profile System + (summary-service.ts) +``` + +### Core Services + +| 파일 | 역할 | 주요 함수 | +|------|------|----------| +| `index.ts` | Worker 진입점, Email Handler | `fetch()`, `email()` | +| `openai-service.ts` | AI 응답 + Function Calling | `generateResponse()`, `executeFunctionCall()` | +| `summary-service.ts` | 프로필 시스템 | `updateSummary()`, `getConversationContext()` | +| `deposit-agent.ts` | 예치금 함수 (코드 직접 처리) | `executeDepositFunction()` | +| `server-agent.ts` | 서버 전문가 AI 상담 | `processServerConsultation()`, `callServerExpertAI()` | +| `security.ts` | Webhook 보안, Rate Limiting (KV) | `validateWebhook()`, `checkRateLimit()` | +| `services/notification.ts` | 관리자 알림 | `notifyAdmin()` | +| `commands.ts` | 봇 명령어 | `handleCommand()` | +| `telegram.ts` | Telegram API | `sendMessage()`, `sendTypingAction()` | + +**Logging & Monitoring:** +| 파일 | 역할 | +|------|------| +| `utils/logger.ts` | 구조화된 로깅 (JSON 기반, 환경별 전환) | +| `utils/metrics.ts` | 성능 메트릭 수집 (API 호출 시간, 에러율) | +| `utils/circuit-breaker.ts` | Circuit Breaker (OpenAI API 보호) | +| `utils/retry.ts` | 재시도 로직 (지수 백오프, 15개 API 지원) | + +### Function Calling Tools (9개) + +| 도구 | 함수명 | 외부 API | 트리거 키워드 | +|------|--------|----------|---------------| +| 날씨 | `get_weather` | wttr.in | 날씨 | +| 검색 | `search_web` | Brave Search | ~란, ~뭐야 (한글→영문 자동 번역) | +| 시간 | `get_current_time` | 내장 | 몇 시, 시간 | +| 계산 | `calculate` | 내장 | 계산, +, -, *, / | +| 문서 | `lookup_docs` | Context7 | 문서, 사용법, API | +| 도메인 | `manage_domain` | 코드 직접 처리 → Namecheap | 도메인, 네임서버, WHOIS | +| 도메인 추천 | `suggest_domains` | GPT + Namecheap | 도메인 추천, 도메인 제안 | +| 예치금 | `manage_deposit` | 코드 직접 처리 | 입금, 충전, 잔액, 계좌 | +| 서버 | `manage_server` | Server Expert AI (세션 기반) | 서버, VPS, 클라우드, 호스팅 | + +### Data Layer (D1 SQLite) + +| 테이블 | 용도 | 주요 컬럼 | +|--------|------|----------| +| `users` | 사용자 | telegram_id, username | +| `message_buffer` | 대화 기록 | user_id, role, content | +| `summaries` | 프로필 | user_id, generation, summary | +| `user_deposits` | 예치금 계정 | user_id, balance, version | +| `deposit_transactions` | 거래 내역 | user_id, amount, status | +| `bank_notifications` | SMS 파싱 | depositor_name, amount, bank | +| `user_domains` | 도메인 소유권 | user_id, domain, verified | +| `server_orders` | 서버 주문 | user_id, spec_id, status | +| `server_specs` | 서버 스펙 | name, cpu, ram, disk, price | + +**AI Fallback:** OpenAI 미설정 시 Workers AI (Llama 3.1 8B) 자동 전환 + +### 동적 도구 로딩 + +**목적:** 토큰 절약 + AI 선택 정확도 향상 + +``` +사용자 메시지 → 키워드 패턴 매칭 → 관련 도구만 선택 → AI 호출 +``` + +**카테고리 분류:** +| 카테고리 | 도구 | 감지 패턴 | +|----------|------|-----------| +| domain | manage_domain, suggest_domains | 도메인, 네임서버, whois, .com | +| deposit | manage_deposit | 입금, 충전, 잔액, 계좌 | +| server | manage_server | 서버, VPS, 클라우드, 호스팅 | +| weather | get_weather | 날씨, 기온, 비, 눈 | +| search | search_web, lookup_docs | 검색, 찾아, 뭐야, 가격 | +| utility | get_current_time, calculate | (항상 포함) | + +**폴백:** 패턴 매칭 없으면 전체 도구 사용 + +--- + +## 4. Feature Systems + +### 4.1 Deposit System + +**manage_deposit 도구 파라미터:** +```typescript +{ + action: 'balance' | 'account' | 'request' | 'history' | 'cancel' | 'pending' | 'confirm' | 'reject', + depositor_name?: string, // request용 + amount?: number, // request용 + transaction_id?: number, // cancel, confirm, reject용 + limit?: number // history용 (기본 10) +} +``` + +**action별 처리:** +| 함수 | 설명 | 권한 | +|------|------|------| +| `get_balance` | 잔액 조회 | 모든 사용자 | +| `get_account_info` | 입금 계좌 안내 | 모든 사용자 | +| `request_deposit` | 입금 신고 | 모든 사용자 | +| `get_transactions` | 거래 내역 | 모든 사용자 | +| `cancel_transaction` | 입금 취소 | 모든 사용자 | +| `get_pending_list` | 대기 목록 | 관리자 | +| `confirm_deposit` | 입금 확인 | 관리자 | +| `reject_deposit` | 입금 거절 | 관리자 | + +**자동 매칭 흐름:** +``` +[시나리오 1: 사용자 먼저] +"홍길동 50000원 입금" → bank_notifications 검색 + ↓ + 매칭 O → confirmed + 잔액↑ | 매칭 X → pending + +[시나리오 2: SMS 먼저 - Email Routing] +은행 SMS → Cloudflare Email Routing → Worker (email handler) + ↓ + 파싱 → bank_notifications 저장 + ↓ + deposit_transactions 검색 (pending) + ↓ + 매칭 O → confirmed + 잔액↑ + 알림 + 매칭 X → 저장만 + 관리자 알림 +``` + +**매칭 로직 (7글자 제한):** +- 은행 SMS는 입금자명을 7글자까지만 표시 +- 매칭 시 SUBSTR(depositor_name, 1, 7) 비교 +- `deposit-agent.ts:72`, `index.ts:908` 구현 + +**Optimistic Locking:** +- `user_deposits.version` 컬럼으로 동시성 제어 +- 잔액 변경 시 version 자동 증가 +- 충돌 시 자동 재시도 (최대 3회, 지수 백오프) +- Double-spending 방지 (도메인 등록 결제 적용) + +**Cron 자동 취소:** +- UTC 15:00 (KST 00:00) 매일 실행 +- 24시간 이상 대기 중인 입금 요청 자동 취소 +- 사용자에게 Telegram 알림 전송 + +**입금 계좌:** 하나은행 427-910018-27104 (주식회사 아이언클래드) + +### 4.2 Domain System + +**manage_domain 도구 파라미터:** +```typescript +{ + action: 'register' | 'check' | 'whois' | 'list' | 'info' | 'get_ns' | 'set_ns' | 'price' | 'cheapest', + domain?: string, + nameservers?: string[], + tld?: string +} +``` + +**action별 처리:** +| action | 설명 | 권한 | +|--------|------|------| +| `list` | 내 도메인 목록 | 소유자 | +| `info` | 도메인 상세정보 | 소유자 | +| `get_ns` | 네임서버 조회 | 공개 | +| `set_ns` | 네임서버 변경 | 소유자 | +| `check` | 가용성 확인 + 가격 | 공개 | +| `whois` | WHOIS 조회 | 공개 | +| `price` | TLD 가격 | 공개 | +| `cheapest` | 가장 저렴한 TLD 목록 (TOP 15) | 공개 | +| `register` | 등록 확인 페이지 | 사용자 | + +**등록 흐름 (인라인 버튼):** +``` +사용자: "example.com 등록해줘" + ↓ +executeDomainAction(): + 1. 가용성 확인 + 가격 조회 + 2. 잔액 확인 + 3. __KEYBOARD__ 마커 포함 응답 생성 + ↓ +telegram.ts: inline_keyboard 생성 → "✅ 등록 확인 / ❌ 취소" 버튼 표시 + ↓ +index.ts: callback_query 핸들러 → domain-register.ts 호출 + ↓ +domain-register.ts: + - 잔액 재확인 + - Optimistic Locking으로 잔액 차감 + - Namecheap API 호출 + - user_domains 테이블에 등록 +``` + +**도메인 추천 (`suggest_domains`):** +``` +1. GPT-4o-mini: 키워드 기반 도메인 15개 생성 +2. Namecheap API: 가용성 일괄 확인 +3. 등록 가능 도메인 < 10개? → 재시도 (최대 3회) +4. TLD별 가격 조회 (병렬 처리) +5. 결과 포맷팅 (등록 가능한 것만 표시) +``` + +**Namecheap API:** +- 엔드포인트: `namecheap-api.anvil.it.com` +- 가격 정책: Namecheap 원가 + 13%, 매일 환율 업데이트 +- WHOIS Guard 자동 적용 (개인정보 비공개) + +### 4.3 Server System + +**manage_server 도구 파라미터:** +```typescript +{ + action: 'recommend' | 'order' | 'start' | 'stop' | 'delete' | 'list', + tech_stack?: string[], // recommend용 + expected_users?: number, // recommend용 + use_case?: string, // recommend용 + traffic_pattern?: 'steady' | 'spiky' | 'growing', + region_preference?: string[], + budget_limit?: number, + lang?: 'ko' | 'ja' | 'zh' | 'en', // 자동 감지 + server_id?: string, // order/start/stop/delete용 + region_code?: string, // order용 + label?: string // order용 +} +``` + +**action별 상태:** +| action | 설명 | 상태 | +|--------|------|------| +| `recommend` | 서버 추천 | ✅ 구현 완료 | +| `order` | 서버 신청 | 🚧 준비 중 | +| `start` | 서버 시작 | 🚧 준비 중 | +| `stop` | 서버 중지 | 🚧 준비 중 | +| `delete` | 서버 해지 | 🚧 준비 중 | +| `list` | 내 서버 목록 | 🚧 준비 중 | + +**Server Expert AI Flow (상담 기반 추천):** +``` +사용자: "서버 추천해줘" + ↓ +메인 AI → manage_server(action="start_consultation") + ↓ +KV 세션 생성 (server_session:{userId}, TTL 1h) + ↓ +Server Expert AI (gpt-4o-mini + Function Calling) + - 페르소나: 30년 경력 클라우드 아키텍트 + - 용도/규모 파악 (최대 2번 질문) + - [선택] search_trends (Brave Search) + - [선택] lookup_framework_docs (Context7) + ↓ +action="question" → 추가 정보 수집 (세션 유지) +action="recommend" → 자동 스펙 추론 → Cloud Orchestrator API 호출 → 세션 삭제 + ↓ +서버 추천 결과 반환 +``` + +**자동 추론 (30년 경험 기반):** +| 용도 | 추론된 tech_stack | 추론된 expected_users | +|------|-------------------|---------------------| +| 블로그 / WordPress | `['wordpress']` | 100명 | +| 쇼핑몰 / 이커머스 | `['ecommerce']` | 500명 | +| 커뮤니티 / 게시판 | `['php', 'mysql']` | - | +| API / 백엔드 | `['nodejs', 'express']` | - | +| 기본값 | `['web']` | 100명 | + +**추천 결과 포맷:** +``` +🖥️ 서버 추천 결과 + +1️⃣ Standard 8GB (Anvil) + • 스펙: 4vCPU / 8GB / 160GB SSD + • 리전: Tokyo 3 (JP) + • 가격: ₩69,719/월 (대역폭 5TB) + • 예상 트래픽: 1.7TB (포함 범위 내) + • 점수: 95점 / 최대 7,500명 +``` + +**서버 주문 상태 전이:** +| 상태 | 설정 주체 | UI 표시 | +|------|----------|---------| +| `pending` | telegram-bot | ❌ 표시 안 함 | +| `provisioning` | cloud-orchestrator | ✅ 🔄 생성 중... | +| `active` | cloud-orchestrator | ✅ 🟢 가동 중 | +| `stopped` | cloud-orchestrator | ✅ ⛔ 중지됨 | +| `failed` | cloud-orchestrator | ❌ 표시 안 함 | +| `terminated` | telegram-bot | ❌ 표시 안 함 | + +**Service Binding:** `CLOUD_ORCHESTRATOR` → `cloud-orchestrator` (wrangler.toml) + +--- + +## 5. API & Security + +### Endpoints + +**공개 엔드포인트:** +| 엔드포인트 | 메서드 | 인증 | 용도 | +|-----------|--------|------|------| +| `/health` | GET | None | Health check (최소 정보만) | +| `/webhook-info` | GET | Query Param | Telegram Webhook 상태 조회 | +| `/setup-webhook` | GET | Query Param | Telegram Webhook 설정 | + +**인증 필요 엔드포인트:** +| 엔드포인트 | 메서드 | 인증 방식 | 권한 | +|-----------|--------|----------|------| +| `/webhook` | POST | Telegram Secret Token | Telegram만 호출 가능 | +| `/api/deposit/balance` | GET | X-API-Key 헤더 | namecheap-api 전용 | +| `/api/deposit/deduct` | POST | X-API-Key 헤더 | namecheap-api 전용 | +| `/api/contact` | POST | CORS | hosting.anvil.it.com만 | +| `/api/metrics` | GET | Bearer Token | 관리자 전용 | +| `/api/test` | POST | WEBHOOK_SECRET | 테스트 전용 | + +**인증 방식:** +- **API Key**: `X-API-Key: {DEPOSIT_API_SECRET}` +- **Bearer**: `Authorization: Bearer {WEBHOOK_SECRET}` +- **Query Param**: `?token={BOT_TOKEN}&secret={WEBHOOK_SECRET}` +- **CORS**: `hosting.anvil.it.com`만 허용 + +**External Consumers:** +- **namecheap-api**: `/api/deposit/*` 호출 (도메인 등록 시 잔액 조회/차감) +- **hosting.anvil.it.com**: `/api/contact` 호출 (웹사이트 문의 폼) +- **Monitoring Tools**: `/api/metrics` 조회 (Circuit Breaker 상태) + +### Rate Limiting + +**Cloudflare KV 기반:** +- 사용자별 메시지 제한: 30 requests / 60초 +- KV Namespace: `RATE_LIMIT_KV` +- 인스턴스 간 공유, 재시작 후 유지 +- 자동 만료 (TTL), 분산 환경 일관성 보장 +- 과도한 요청 시 경고 메시지 + 차단 + +**관리자 알림 Rate Limiting:** +- 같은 유형의 알림은 1시간에 1회만 전송 +- 키: `notification:{type}:{service}` +- TTL: 3600초 + +### Admin Notification + +**알림 유형:** +| 유형 | 트리거 조건 | 심각도 | +|------|------------|--------| +| `circuit_breaker` | Circuit Breaker OPEN 상태 전환 | 🚨 HIGH | +| `retry_exhausted` | 모든 재시도 실패 (3회) | ⚠️ MEDIUM | +| `api_error` | 치명적 API 에러 (5xx, Rate Limit) | 🔴 CRITICAL | + +**통합 지점:** +- `utils/circuit-breaker.ts`: Circuit 차단 시 +- `utils/retry.ts`: 재시도 실패 시 +- `openai-service.ts`: OpenAI API 에러 시 +- `tools/*.ts`: 외부 API 에러 시 + +--- + +## 6. Development + +### Code Style + +**TypeScript:** +- Strict mode 활성화 (`tsconfig.json`) +- 타입 정의: `types.ts`에 인터페이스 집중 관리 +- any 사용 금지 (불가피한 경우 주석으로 이유 명시) + +**네이밍:** +- 파일: `kebab-case.ts` (예: `openai-service.ts`) +- 함수: `camelCase` (예: `executeFunctionCall`) +- 상수: `UPPER_SNAKE_CASE` (예: `SUMMARY_THRESHOLD`) +- 타입/인터페이스: `PascalCase` (예: `TelegramUpdate`) + +**에러 핸들링:** ```typescript -// 패턴: try-catch + 사용자 친화적 메시지 + 구조화된 로깅 import { createLogger } from './utils/logger'; const logger = createLogger('service-name'); @@ -308,56 +559,32 @@ try { } ``` -### 로깅 규칙 +**로깅:** ```typescript -// 구조화된 로깅 (Phase 5-3에서 도입) import { createLogger } from './utils/logger'; const logger = createLogger('service-name'); -logger.info('동작 설명', { key: 'value' }); // 정상 동작 -logger.error('에러 설명', error as Error); // 에러 -logger.warn('경고 메시지', { context: 'data' }); // 경고 +logger.info('동작 설명', { key: 'value' }); +logger.error('에러 설명', error as Error); +logger.warn('경고 메시지', { context: 'data' }); -// 성능 측정 const end = logger.startTimer('작업 완료'); await doWork(); end(); // duration 자동 기록 - -// wrangler tail로 확인 가능 -// 프로덕션: JSON 형식, 개발: 읽기 쉬운 형식 자동 전환 ``` -### 네이밍 -- 파일: `kebab-case.ts` (예: `openai-service.ts`) -- 함수: `camelCase` (예: `executeFunctionCall`) -- 상수: `UPPER_SNAKE_CASE` (예: `SUMMARY_THRESHOLD`) -- 타입/인터페이스: `PascalCase` (예: `TelegramUpdate`) +### Testing ---- +**자동화된 단위 테스트 (Vitest):** -## Testing - -### 자동화된 단위 테스트 (Vitest) - -**프레임워크**: Vitest + Miniflare (Cloudflare Workers 환경 시뮬레이션) - -**실행 명령어**: +**실행 명령어:** ```bash npm test # 모든 테스트 실행 -npm run test:watch # Watch 모드 (개발 중) +npm run test:watch # Watch 모드 npm run test:coverage # 커버리지 리포트 ``` -**테스트 파일 구조**: -``` -tests/ -├── setup.ts # D1 Database 초기화 및 헬퍼 함수 -└── deposit-agent.test.ts # 예치금 시스템 테스트 (50+ test cases) - -vitest.config.ts # Vitest 설정 (Miniflare 바인딩) -``` - -**테스트 범위**: +**테스트 범위:** | 기능 | 테스트 케이스 | 상태 | |------|--------------|------| | **음수 금액 거부** | 음수/0원 입금 시도 | ✅ | @@ -368,29 +595,20 @@ vitest.config.ts # Vitest 설정 (Miniflare 바인딩) | **거래 상태** | confirmed 거래 취소 차단 | ✅ | | **Edge Cases** | 999,999,999원, 특수문자, 1글자 이름 | ✅ | -**Mock 전략**: -- **D1 Database**: Miniflare in-memory SQLite -- **Environment Variables**: `vitest.config.ts`에서 바인딩 -- **KV Namespace**: Rate Limiting 모킹 +**Mock 전략:** +- D1 Database: Miniflare in-memory SQLite +- Environment Variables: `vitest.config.ts`에서 바인딩 +- KV Namespace: Rate Limiting 모킹 -**헬퍼 함수** (`tests/setup.ts`): +**헬퍼 함수 (`tests/setup.ts`):** ```typescript -createTestUser(telegramId, username) // 테스트용 사용자 생성 -createBankNotification(depositorName, amount) // 은행 알림 생성 -createDepositTransaction(userId, amount, status) // 거래 생성 -getTestDB() // DB 바인딩 가져오기 +createTestUser(telegramId, username) +createBankNotification(depositorName, amount) +createDepositTransaction(userId, amount, status) +getTestDB() ``` -**추가 예정**: -- `openai-service.ts` - Function Calling 도구 테스트 -- `summary-service.ts` - 프로필 시스템 테스트 -- Integration Tests - 전체 워크플로우 테스트 - ---- - -### 수동 테스트 (Webhook) - -**로컬 테스트 절차**: +**수동 테스트 (Webhook):** ```bash # 1. 로컬 D1 초기화 (최초 1회) npm run db:init:local @@ -398,130 +616,94 @@ npm run db:init:local # 2. 로컬 서버 실행 npm run dev -# 3. 다른 터미널에서 테스트 요청 +# 3. 테스트 요청 curl -X POST http://localhost:8787/webhook \ -H "Content-Type: application/json" \ -H "X-Telegram-Bot-Api-Secret-Token: test-secret" \ -d '{"message":{"chat":{"id":123},"text":"테스트"}}' ``` -**배포 후 확인**: -```bash -# 로그 스트리밍 -npm run tail - -# Webhook 상태 확인 (token + secret 필요) -curl "https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info?token=${BOT_TOKEN}&secret=${WEBHOOK_SECRET}" -``` - -**수동 테스트 예제** (자동화 예정): -- `src/services/__test__/notification.test.ts` - 관리자 알림 -- `src/utils/__test__/logger.test.ts` - 구조화된 로깅 +**Web Chat Testing:** [telegram-cli/README.md](../telegram-cli/README.md) - 봇 테스트용 별도 Worker --- -## Web Chat Testing (telegram-cli) +## 7. Configuration -**목적:** Claude Code가 봇과 대화하여 기능을 테스트할 때 사용 +### Environment Variables (wrangler.toml) -### API 엔드포인트 +**기본 설정:** +| 변수 | 기본값 | 설명 | +|------|--------|------| +| `SUMMARY_THRESHOLD` | 20 | 프로필 업데이트 주기 (메시지 수) | +| `MAX_SUMMARIES_PER_USER` | 3 | 유지할 프로필 버전 수 | +| `DOMAIN_OWNER_ID` | - | 도메인 관리 권한 Telegram ID | +| `DEPOSIT_ADMIN_ID` | - | 예치금 관리 권한 Telegram ID | -```bash -# 봇과 대화 -curl -s -X POST 'https://telegram-cli-web.kappa-d8e.workers.dev/api/chat' \ - -H 'Content-Type: application/json' \ - -d '{"message": "안녕"}' +**외부 API 엔드포인트 (커스터마이징 가능):** +| 변수 | 기본값 | +|------|--------| +| `OPENAI_API_BASE` | `https://gateway.ai.cloudflare.com/v1/.../openai` | +| `NAMECHEAP_API_URL` | `https://namecheap-api.anvil.it.com` | +| `WHOIS_API_URL` | `https://whois-api-...vercel.app` | +| `CONTEXT7_API_BASE` | `https://context7.com/api/v2` | +| `BRAVE_API_BASE` | `https://api.search.brave.com/res/v1` | +| `WTTR_IN_URL` | `https://wttr.in` | +| `HOSTING_SITE_URL` | `https://hosting.anvil.it.com` | + +### Secrets + +| 변수 | 설명 | 저장 위치 | +|------|------|----------| +| `BOT_TOKEN` | Telegram Bot Token | Vault: telegram-bot | +| `WEBHOOK_SECRET` | Telegram Webhook 인증 | Vault: telegram-bot | +| `OPENAI_API_KEY` | OpenAI API 키 | - | +| `NAMECHEAP_API_KEY` | namecheap-api 래퍼 인증 | - | +| `NAMECHEAP_API_KEY_INTERNAL` | Namecheap API 키 (내부) | - | +| `BRAVE_API_KEY` | Brave Search API 키 | - | +| `DEPOSIT_API_SECRET` | Deposit API 인증 | - | + +### Bindings + +**KV Namespaces:** +| Binding | 설명 | +|---------|------| +| `RATE_LIMIT_KV` | Rate Limiting 저장소 | +| `SESSION_KV` | 서버 상담 세션 저장소 | + +**Other Bindings:** +| Binding | 타입 | 용도 | +|---------|------|------| +| `DB` | D1 Database | 사용자/메시지/예치금 데이터 | +| `AI` | Workers AI | OpenAI 폴백용 (Llama 3.1 8B) | +| `CLOUD_ORCHESTRATOR` | Service Binding | 서버 관리 Worker | + +### External Integrations + +| 서비스 | 용도 | 엔드포인트 | 주의사항 | +|--------|------|-----------|----------| +| **AI Gateway** | OpenAI 프록시 | gateway.ai.cloudflare.com | 지역 제한 우회, 로그/캐시 | +| Context7 | 문서 조회 | context7.com API | - | +| Namecheap API | 도메인 백엔드 | namecheap-api.anvil.it.com | 날짜: MM/DD/YYYY → ISO 변환 | +| WHOIS API | WHOIS 조회 | whois-api-eight.vercel.app | ccSLD 미지원 | +| wttr.in | 날씨 | wttr.in | - | +| Brave Search | 검색 | api.search.brave.com | Free AI 플랜 (2,000/월) | +| Email Routing | 입금 SMS 수신 | Cloudflare Email Routing | Worker email handler로 직접 처리 | + +**Cloudflare AI Gateway:** +``` +Gateway ID: telegram-bot +Account ID: d8e5997eb4040f8b489f09095c0f623c +URL: gateway.ai.cloudflare.com/v1/{account_id}/telegram-bot/openai/... ``` -**응답 형식:** -```json -{ - "response": "봇 응답 텍스트...", - "time_ms": 1234 -} -``` - -### Claude가 사용하는 경우 - -**테스트 시나리오:** -```bash -# 1. 기본 대화 -curl -X POST 'https://telegram-cli-web.kappa-d8e.workers.dev/api/chat' \ - -H 'Content-Type: application/json' \ - -d '{"message": "안녕하세요"}' - -# 2. 예치금 기능 -curl -X POST 'https://telegram-cli-web.kappa-d8e.workers.dev/api/chat' \ - -H 'Content-Type: application/json' \ - -d '{"message": "잔액 조회"}' - -# 3. 도메인 기능 -curl -X POST 'https://telegram-cli-web.kappa-d8e.workers.dev/api/chat' \ - -H 'Content-Type: application/json' \ - -d '{"message": "example.com 조회"}' - -# 4. Function Calling 도구 -curl -X POST 'https://telegram-cli-web.kappa-d8e.workers.dev/api/chat' \ - -H 'Content-Type: application/json' \ - -d '{"message": "서울 날씨"}' -``` - -### 다른 엔드포인트 - -| 엔드포인트 | 메서드 | 설명 | -|-----------|--------|------| -| `/` | GET | 웹 채팅 UI (브라우저) | -| `/health` | GET | Health check | - -### 배포 정보 - -**Worker URL**: https://telegram-cli-web.kappa-d8e.workers.dev -**별도 Worker**: 메인 봇과 독립적으로 배포됨 -**상세 문서**: [telegram-cli/README.md](../telegram-cli/README.md) - -**Secrets 필요:** -- `BOT_TOKEN`: Telegram Bot Token (Vault: telegram-bot) -- `WEBHOOK_SECRET`: Bot Worker /api/test 인증용 (Vault: telegram-bot) - -**배포 명령:** -```bash -cd telegram-cli -npm install -wrangler secret put BOT_TOKEN -wrangler secret put WEBHOOK_SECRET -npm run deploy -``` - -### 아키텍처 - -``` -Claude Code (또는 브라우저) - ↓ -POST /api/chat → telegram-cli Worker - ↓ - Bot Worker (/api/test) - - 메인 봇과 동일한 로직 - - DB 저장/조회 - - AI 응답 생성 - - Function Calling - ↓ - 응답 반환 { response, time_ms } -``` - -**특징:** -- 별도 Worker로 분리되어 메인 봇에 영향 없음 -- 동일한 Bot Worker의 /api/test 엔드포인트 호출 -- 응답 시간 측정 자동 포함 -- username: 'web-tester'로 자동 설정 - -**사용 사례:** -- Claude Code가 봇 기능 테스트 -- 개발자가 브라우저에서 빠른 테스트 -- CI/CD 파이프라인에서 자동 테스트 +**적용 범위:** +- ✅ Chat Completions - AI Gateway 경유 +- ✅ 예치금 관리 - 코드 직접 처리 (Assistants API 제거) +- ✅ 도메인 관리 - 코드 직접 처리 --- -## Troubleshooting +## 8. Troubleshooting ### 자주 발생하는 에러 @@ -533,12 +715,11 @@ POST /api/chat → telegram-cli Worker | Function Calling 무한 루프 | tool_choice 설정 오류 | `tool_choice: "auto"` 확인 | | 프로필 업데이트 안됨 | 메시지 20개 미만 | `/context`로 버퍼 수 확인 | | Email Worker 파싱 실패 | SMS 형식 변경 | `index.ts`의 정규식 패턴 확인 | -| **AI가 도구 호출 안 함** | 키워드 미인식 | 시스템 프롬프트 + 도구 description에 키워드 추가 | -| **예치금 최소 금액 제한** | Agent 프롬프트 문제 | Deposit Agent 프롬프트 수정 (OpenAI API) | -| **다른 사용자 응답 없음** | DB 작업 try-catch 누락 | `index.ts:handleMessage` 전체 try-catch 적용 (2026-01 수정) | -| **CORS 오류 (웹사이트 문의)** | 허용된 Origin 아님 | `hosting.anvil.it.com`만 허용됨 | +| AI가 도구 호출 안 함 | 키워드 미인식 | 시스템 프롬프트 + 도구 description에 키워드 추가 | +| CORS 오류 (웹사이트 문의) | 허용된 Origin 아님 | `hosting.anvil.it.com`만 허용됨 | ### 디버깅 명령어 + ```bash # D1 데이터 직접 조회 (로컬) wrangler d1 execute telegram-conversations --local --command "SELECT * FROM users LIMIT 5" @@ -549,226 +730,82 @@ wrangler d1 execute telegram-conversations --command "SELECT * FROM users LIMIT --- -## Security +## 9. Key Patterns -### Endpoint Security +### Function Calling 추가 방법 -**공개 엔드포인트:** -| 엔드포인트 | 보안 수준 | 설명 | -|-----------|----------|------| -| `/health` | 최소 정보만 | status, timestamp만 반환 (DB 정보 미노출) | -| `/webhook-info` | BOT_TOKEN + WEBHOOK_SECRET 필요 | Telegram Webhook 상태 조회 | -| `/setup-webhook` | BOT_TOKEN + WEBHOOK_SECRET 필요 | Webhook 설정 | - -**인증 필요 엔드포인트:** -| 엔드포인트 | 인증 방식 | 권한 | -|-----------|----------|------| -| `/webhook` | Telegram Secret Token | Telegram만 호출 가능 | -| `/api/deposit/*` | X-API-Key 헤더 | namecheap-api 전용 | -| `/api/test` | WEBHOOK_SECRET | 테스트 전용 | -| `/api/contact` | CORS | hosting.anvil.it.com만 | -| `/api/metrics` | Bearer Token (WEBHOOK_SECRET) | 관리자 전용 (Circuit Breaker 상태) | - -**CORS 정책:** ```typescript -// 문의 폼 API (POST /api/contact) -'Access-Control-Allow-Origin': 'https://hosting.anvil.it.com' // 특정 도메인만 허용 -'Access-Control-Allow-Methods': 'POST, OPTIONS' -'Access-Control-Allow-Headers': 'Content-Type' +// 1. openai-service.ts의 tools 배열에 추가 +const tools = [ + { + type: "function", + function: { + name: "new_tool", + description: "도구 설명 (트리거 키워드 포함)", + parameters: { /* JSON Schema */ } + } + } +]; + +// 2. executeFunctionCall()에 케이스 추가 +case 'new_tool': + return await executeNewTool(args, env); ``` -**Rate Limiting (Cloudflare KV 기반):** -- 사용자별 메시지 제한 (30 requests / 60초) -- KV Namespace: `RATE_LIMIT_KV` (`wrangler.toml`) -- 인스턴스 간 공유, 재시작 후 유지 -- 자동 만료 (TTL), 분산 환경 일관성 보장 -- 과도한 요청 시 경고 메시지 + 차단 +### 프로필 시스템 (3개 요약 통합) -### Health Check 정책 +``` +메시지 수신 → message_buffer 저장 (최대 19개) + ↓ 20개 도달 + ┌──────────────┴──────────────┐ + ↓ ↓ +기존 요약 3개 조회 사용자 발언만 추출 + ↓ ↓ + └──────────→ OpenAI 통합 분석 ←┘ + ↓ + summaries 테이블 저장 (generation++) + ↓ + 구버전 삭제 (최근 3개만 유지) +``` -**이전 (보안 취약):** -```json +**Context Enrichment:** +```typescript +// getConversationContext() 반환값 { - "status": "ok", - "timestamp": "...", - "stats": { - "users": 123, // DB 테이블 정보 노출 - "summaries": 456 // 민감한 통계 노출 - } + previousSummary: Summary | null, // 최신 요약 (호환성) + summaries: Summary[], // 전체 요약 (최대 3개, 최신순) + recentMessages: BufferedMessage[], + totalMessages: number, } ``` -**현재 (보안 강화):** -```json -{ - "status": "ok", - "timestamp": "2026-01-19T12:34:56.789Z" // 최소 정보만 -} +### AI 시스템 프롬프트 + +**핵심 규칙 (`summary-service.ts`):** +``` +- 날씨, 시간, 계산 요청은 제공된 도구를 사용하세요. +- 최신 정보, 실시간 데이터, 현재 가격, 뉴스 등은 search_web 도구로 검색하세요. +- 예치금, 입금, 충전, 잔액, 계좌 관련 요청은 반드시 manage_deposit 도구를 사용하세요. +- 도메인 추천, 도메인 제안, 도메인 아이디어 요청은 반드시 suggest_domains 도구를 사용하세요. +- 기타 도메인 관련 요청(조회, 등록, 네임서버 등)은 manage_domain 도구를 사용하세요. +- 서버, VPS, 클라우드, 호스팅 추천 요청은 반드시 manage_server 도구를 사용하세요. +- manage_deposit, manage_domain, suggest_domains, manage_server 도구 결과는 그대로 전달하세요. ``` -**상세 정보 필요 시:** -- 별도 인증된 Admin 엔드포인트 추가 검토 (미구현) -- 또는 Cloudflare Dashboard의 Analytics 활용 +**중요:** 메인 AI가 도구를 호출하지 않고 직접 답변하는 경우: +1. 시스템 프롬프트에 해당 키워드 추가 (`summary-service.ts`) +2. 도구 description에 키워드 명시 (`openai-service.ts` tools 배열) ---- +### 검색 한글→영문 자동 번역 -## Admin Notification System - -**목적:** 심각한 시스템 에러 발생 시 관리자에게 실시간 Telegram 알림 - -**파일:** `src/services/notification.ts` - -**알림 유형:** -| 유형 | 트리거 조건 | 심각도 | -|------|------------|--------| -| `circuit_breaker` | Circuit Breaker OPEN 상태 전환 | 🚨 HIGH | -| `retry_exhausted` | 모든 재시도 실패 (3회) | ⚠️ MEDIUM | -| `api_error` | 치명적 API 에러 (5xx, Rate Limit) | 🔴 CRITICAL | - -**Rate Limiting:** -- 같은 유형의 알림은 1시간에 1회만 전송 -- KV Namespace 사용 (`RATE_LIMIT_KV`) -- 키: `notification:{type}:{service}` -- TTL: 3600초 (1시간) - -**사용 예시:** -```typescript -import { notifyAdmin } from './services/notification'; -import { sendMessage } from './telegram'; - -// Circuit Breaker가 OPEN 상태가 되었을 때 -await notifyAdmin( - 'circuit_breaker', - { - service: 'OpenAI API', - error: 'Connection timeout after 30s', - context: 'User message processing failed' - }, - { - telegram: { - sendMessage: (chatId: number, text: string) => - sendMessage(env.BOT_TOKEN, chatId, text) - }, - adminId: env.DEPOSIT_ADMIN_ID || '', - env - } -); +``` +"판골린 VPN" → GPT-4o-mini 번역 → "Pangolin VPN" → Brave Search ``` -**알림 메시지 형식:** -``` -🚨 시스템 알림 (Circuit Breaker) - -서비스: OpenAI API -에러: Connection timeout -상태: OPEN -시간: 2026-01-19 15:30:45 - -자동 복구 시도: 30초 후 -``` - -**환경 변수:** -- `DEPOSIT_ADMIN_ID`: 관리자 Telegram Chat ID (wrangler.toml) - -**통합 지점:** -- `utils/circuit-breaker.ts`: Circuit 차단 시 -- `utils/retry.ts`: 재시도 실패 시 -- `openai-service.ts`: OpenAI API 에러 시 -- `tools/*.ts`: 외부 API 에러 시 - -**에러 핸들링:** -- 알림 전송 실패 시 로그만 기록하고 무시 -- 메인 로직에 영향 없음 - -**테스트:** -```bash -# 테스트 엔드포인트를 index.ts에 임시 추가 -curl https://your-worker.workers.dev/test-notification - -# 로그 확인 -npm run tail -``` - ---- - -## Architecture - -**Message Flow:** -``` -Telegram Webhook → Security Validation → Command/Message Router - ↓ - ┌──────────────────────────┴──────────────────────────┐ - ↓ ↓ - Command Handler AI Response Generator - (commands.ts) (openai-service.ts) - ↓ - Function Calling (8개) - (weather, search, time, calc, docs, - domain, suggest_domains, deposit) - ↓ - Profile System - (summary-service.ts) -``` - -**Core Services:** -| 파일 | 역할 | 주요 함수 | -|------|------|----------| -| `index.ts` | Worker 진입점, Email Handler | `fetch()`, `email()` | -| `openai-service.ts` | AI 응답 + Function Calling | `generateResponse()`, `executeFunctionCall()` | -| `summary-service.ts` | 프로필 시스템 | `updateSummary()`, `getConversationContext()` | -| `deposit-agent.ts` | 예치금 함수 (코드 직접 처리) | `executeDepositFunction()` | -| `server-agent.ts` | 서버 전문가 AI 상담 | `processServerConsultation()`, `callServerExpertAI()` | -| `security.ts` | Webhook 보안, Rate Limiting (KV) | `validateWebhook()`, `checkRateLimit()` | -| `services/notification.ts` | 관리자 알림 (Circuit Breaker, Retry 실패) | `notifyAdmin()` | -| `commands.ts` | 봇 명령어 | `handleCommand()` | -| `telegram.ts` | Telegram API | `sendMessage()`, `sendTypingAction()` | - -**Logging & Monitoring (Phase 5-3):** -| 파일 | 역할 | 주요 기능 | -|------|------|----------| -| `utils/logger.ts` | 구조화된 로깅 | JSON 기반 로그, 환경별 전환 (개발/프로덕션) | -| `utils/metrics.ts` | 성능 메트릭 수집 | API 호출 시간, 에러율, Circuit Breaker 상태 | -| `utils/circuit-breaker.ts` | Circuit Breaker | OpenAI API 보호, 자동 복구 | -| `utils/retry.ts` | 재시도 로직 | 지수 백오프, 15개 API 지원 | - -**Logger 사용 예시:** -```typescript -import { createLogger } from './utils/logger'; -const logger = createLogger('openai'); - -logger.info('AI 응답 생성', { model: 'gpt-4o-mini' }); -logger.error('API 호출 실패', error as Error, { retryCount: 3 }); - -const end = logger.startTimer('처리 완료'); -await process(); -end(); // duration 자동 기록 -``` - -**Function Calling Tools (9개):** -| 도구 | 함수명 | 외부 API | 트리거 키워드 | -|------|--------|----------|---------------| -| 날씨 | `get_weather` | wttr.in | 날씨 | -| 검색 | `search_web` | Brave Search | ~란, ~뭐야 (한글→영문 자동 번역) | -| 시간 | `get_current_time` | 내장 | 몇 시, 시간 | -| 계산 | `calculate` | 내장 | 계산, +, -, *, / | -| 문서 | `lookup_docs` | Context7 | 문서, 사용법, API | -| 도메인 | `manage_domain` | 코드 직접 처리 → Namecheap | 도메인, 네임서버, WHOIS | -| 도메인 추천 | `suggest_domains` | GPT + Namecheap | **도메인 추천, 도메인 제안, 도메인 아이디어** | -| 예치금 | `manage_deposit` | 코드 직접 처리 | **입금, 충전, 잔액, 계좌, 송금** | -| 서버 | `manage_server` | Server Expert AI (세션 기반 상담) | **서버, VPS, 클라우드, 호스팅, 추천** | - -**Data Layer (D1 SQLite):** -| 테이블 | 용도 | 주요 컬럼 | -|--------|------|----------| -| `users` | 사용자 | telegram_id, username | -| `message_buffer` | 대화 기록 | user_id, role, content | -| `summaries` | 프로필 | user_id, generation, summary | -| `user_deposits` | 예치금 계정 | user_id, balance | -| `deposit_transactions` | 거래 내역 | user_id, amount, status | -| `bank_notifications` | SMS 파싱 | depositor_name, amount, bank | -| `user_domains` | 도메인 소유권 | user_id, domain, verified (등록 시 자동 추가) | - -**AI Fallback:** OpenAI 미설정 시 Workers AI (Llama 3.1 8B) 자동 전환 +- 한글 포함 검색어 감지 (`/[가-힣]/`) +- GPT-4o-mini로 영문 번역 (외래어/기술용어 원어 복원) +- 번역된 쿼리로 검색 +- 결과에 원본+번역 표시 ### 에러 핸들링 구조 (index.ts:handleMessage) @@ -792,784 +829,15 @@ Rate Limit 초과 → 경고 메시지 전송 + return **중요:** 모든 DB 작업과 AI 호출은 try-catch로 감싸서 오류 시에도 사용자에게 메시지 전송 -### 동적 도구 로딩 - -**목적:** 토큰 절약 + AI 선택 정확도 향상 - -``` -사용자 메시지 → 키워드 패턴 매칭 → 관련 도구만 선택 → AI 호출 -``` - -**카테고리 분류:** -| 카테고리 | 도구 | 감지 패턴 | -|----------|------|-----------| -| domain | manage_domain, suggest_domains | 도메인, 네임서버, whois, .com | -| deposit | manage_deposit | 입금, 충전, 잔액, 계좌 | -| weather | get_weather | 날씨, 기온, 비, 눈 | -| search | search_web, lookup_docs | 검색, 찾아, 뭐야, 가격 | -| utility | get_current_time, calculate | (항상 포함) | - -**폴백:** 패턴 매칭 없으면 전체 도구 사용 - -**로그:** `[ToolSelector] 카테고리: domain, utility / 선택된 도구: manage_domain, ...` - -### Server Expert AI Flow - -**목적:** 30년 경력 시니어 아키텍트 페르소나 기반 대화형 서버 추천 - -``` -사용자: "서버 추천해줘" - ↓ -메인 AI → manage_server(action="start_consultation") - ↓ -KV 세션 생성 (server_session:{userId}, TTL 1h) - ↓ -Server Expert AI (gpt-4o-mini + Function Calling) - ├── 페르소나: 30년 경력 클라우드 아키텍트 - ├── 용도/규모 파악 (최대 2번 질문) - ├── [선택] search_trends 호출 (최신 트렌드) - ├── [선택] lookup_framework_docs 호출 (공식 권장 스펙) - └── JSON 응답 {action, message, collectedInfo} - ↓ -┌──────────────────┬──────────────────┐ -│ action="question"│ action="recommend"| -├──────────────────┼──────────────────┤ -│ 추가 정보 수집 │ 자동 스펙 추론 │ -│ (용도, 규모) │ (tech_stack 등) │ -│ 세션 유지 │ Cloud Orchestrator│ -│ │ API 호출 │ -│ │ 세션 삭제 │ -└──────────────────┴──────────────────┘ - ↓ -서버 추천 결과 반환 -``` - -**세션 관리:** -- KV Namespace: `SESSION_KV` -- Key: `server_session:{userId}` -- TTL: 1시간 (자동 만료) -- 상태: `gathering` → `recommending` → `completed` - -**Function Calling 도구:** -| 도구 | 설명 | 용도 | -|------|------|------| -| `search_trends` | Brave Search API | 최신 기술 트렌드, 서버 요구사항 검색 | -| `lookup_framework_docs` | Context7 API | 프레임워크 공식 문서에서 권장 스펙 조회 | - -**자동 추론 (30년 경험 기반):** -| 용도 | 추론된 tech_stack | 추론된 expected_users | -|------|-------------------|---------------------| -| 블로그 / WordPress | `['wordpress']` | 100명 (개인용) | -| 쇼핑몰 / 이커머스 | `['ecommerce']` | 500명 (사업용) | -| 커뮤니티 / 게시판 | `['php', 'mysql']` | - | -| API / 백엔드 | `['nodejs', 'express']` | - | -| 기본값 | `['web']` | 100명 | - -**특징:** -- 경쟁사 (AWS, GCP, Azure, Vultr, Linode) 언급 금지 -- Anvil 서버만 추천 -- 최대 3회 도구 호출 (무한 루프 방지) -- "모르겠어요", "아무거나" → 즉시 추천 (기본값) -- 질문 최대 2번, 이후 자동 추천 - -**관련 파일:** -| 파일 | 역할 | -|------|------| -| `server-agent.ts` | 세션 관리 + Server Expert AI 호출 | -| `tools/server-tool.ts` | Cloud Orchestrator API 연동 | -| `tools/search-tool.ts` | search_trends, lookup_framework_docs 구현 | - --- -## Key Patterns +## OpenAPI Documentation -### Function Calling 추가 방법 -```typescript -// 1. openai-service.ts의 tools 배열에 추가 -const tools = [ - // ... 기존 도구들 - { - type: "function", - function: { - name: "new_tool", - description: "도구 설명", - parameters: { /* JSON Schema */ } - } - } -]; +**OpenAPI Specification**: `openapi.yaml` -// 2. executeFunctionCall()에 케이스 추가 -case 'new_tool': - return await executeNewTool(args); -``` - -### 프로필 시스템 흐름 (3개 요약 통합 방식) -``` -메시지 수신 → message_buffer 저장 (최대 19개) - ↓ 20개 도달 - ┌──────────────┴──────────────┐ - ↓ ↓ -기존 요약 3개 조회 사용자 발언만 추출 - ↓ ↓ - └──────────→ OpenAI 통합 분석 ←┘ - ↓ - summaries 테이블 저장 (generation++) - ↓ - 구버전 삭제 (최근 3개만 유지) -``` - -**통합 분석 방식:** -- 기존: 최신 요약 1개만 참조하여 업데이트 -- 변경: **모든 요약 (최대 3개) + 새 메시지 → AI가 통합 분석** - -### Context Enrichment -```typescript -// getConversationContext() 반환값 구조 -{ - previousSummary: Summary | null, // 최신 요약 (호환성) - summaries: Summary[], // 전체 요약 (최대 3개, 최신순) - recentMessages: BufferedMessage[], - totalMessages: number, -} -``` - -**시스템 프롬프트에 통합:** -``` -## 사용자 프로필 (3개 버전 통합) -[v1] 초기 프로필 내용... -[v2] 업데이트된 프로필... -[v3] 최신 프로필... - -최신 버전을 우선시하되, 이전 버전 맥락도 고려 -``` - -### AI 시스템 프롬프트 (`summary-service.ts`) -``` -- 날씨, 시간, 계산 요청은 제공된 도구를 사용하세요. -- 최신 정보, 실시간 데이터, 현재 가격, 뉴스 등은 search_web 도구로 검색하세요. -- 예치금, 입금, 충전, 잔액, 계좌 관련 요청은 반드시 manage_deposit 도구를 사용하세요. -- 도메인 추천, 도메인 제안, 도메인 아이디어 요청은 반드시 suggest_domains 도구를 사용하세요. -- 기타 도메인 관련 요청(조회, 등록, 네임서버 등)은 manage_domain 도구를 사용하세요. -- manage_deposit, manage_domain, suggest_domains 도구 결과는 그대로 전달하세요. -``` - -**중요:** 메인 AI가 도구를 호출하지 않고 직접 답변하는 경우: -1. 시스템 프롬프트에 해당 키워드 추가 (`summary-service.ts:252-254`) -2. 도구 description에 키워드 명시 (`openai-service.ts` tools 배열) - -### 검색 한글→영문 자동 번역 - -``` -"판골린 VPN" → GPT-4o-mini 번역 → "Pangolin VPN" → Brave Search -``` - -**동작:** -- 한글 포함 검색어 감지 (`/[가-힣]/`) -- GPT-4o-mini로 영문 번역 (외래어/기술용어 원어 복원) -- 번역된 쿼리로 검색 -- 결과에 원본+번역 표시: `검색 결과: 판골린 VPN (→ Pangolin VPN)` - -**로그:** `[search_web] 번역: "판골린 VPN" → "Pangolin VPN"` - ---- - -## Configuration - -### wrangler.toml 환경변수 - -**기본 설정:** -| 변수 | 기본값 | 설명 | -|------|--------|------| -| `SUMMARY_THRESHOLD` | 20 | 프로필 업데이트 주기 (메시지 수) | -| `MAX_SUMMARIES_PER_USER` | 3 | 유지할 프로필 버전 수 | -| `DOMAIN_OWNER_ID` | - | 도메인 관리 권한 Telegram ID | -| `DEPOSIT_ADMIN_ID` | - | 예치금 관리 권한 Telegram ID | - -**외부 API 엔드포인트 (커스터마이징 가능):** -| 변수 | 기본값 | 설명 | -|------|--------|------| -| `OPENAI_API_BASE` | `https://gateway.ai.cloudflare.com/v1/.../openai` | OpenAI API Gateway URL | -| `NAMECHEAP_API_URL` | `https://namecheap-api.anvil.it.com` | Namecheap API 래퍼 URL | -| `WHOIS_API_URL` | `https://whois-api-...vercel.app` | WHOIS 조회 API URL | -| `CONTEXT7_API_BASE` | `https://context7.com/api/v2` | Context7 문서 조회 API | -| `BRAVE_API_BASE` | `https://api.search.brave.com/res/v1` | Brave Search API | -| `WTTR_IN_URL` | `https://wttr.in` | 날씨 조회 API | -| `HOSTING_SITE_URL` | `https://hosting.anvil.it.com` | 공식 웹사이트 URL | - -**환경별 설정:** -개발/스테이징/프로덕션 환경별로 다른 API 엔드포인트를 사용할 수 있습니다. -기본값이 설정되어 있으므로 생략 가능하며, 필요시에만 override하세요. - -**Secrets (wrangler secret):** -| 변수 | 설명 | 저장 위치 | -|------|------|----------| -| `BOT_TOKEN` | Telegram Bot Token | Vault: telegram-bot | -| `WEBHOOK_SECRET` | Telegram Webhook 인증 | Vault: telegram-bot | -| `OPENAI_API_KEY` | OpenAI API 키 | - | -| `NAMECHEAP_API_KEY` | namecheap-api 래퍼 인증 | - | -| `NAMECHEAP_API_KEY_INTERNAL` | Namecheap API 키 (내부) | - | -| `BRAVE_API_KEY` | Brave Search API 키 | - | -| `DEPOSIT_API_SECRET` | Deposit API 인증 | - | - -**KV Namespaces:** -| Binding | 설명 | 생성 명령 | -|---------|------|----------| -| `RATE_LIMIT_KV` | Rate Limiting 저장소 | `wrangler kv:namespace create RATE_LIMIT_KV` | -| `SESSION_KV` | 서버 상담 세션 저장소 | `wrangler kv:namespace create SESSION_KV` | - -**Bindings:** -| Binding | 타입 | 용도 | -|---------|------|------| -| `DB` | D1 Database | 사용자/메시지/예치금 데이터 | -| `AI` | Workers AI | OpenAI 폴백용 | -| `RATE_LIMIT_KV` | KV Namespace | 사용자별 Rate Limiting (30 req/60s) | -| `SESSION_KV` | KV Namespace | 서버 상담 세션 저장 (1시간 TTL) | - ---- - -## Performance Optimizations - -### N+1 쿼리 제거 - -**Cron 스케줄러 (만료 거래 정리):** -- 이전: 반복문 내 N개 UPDATE 쿼리 -- 개선: 단일 IN 절 쿼리 + 병렬 알림 -- 성능: **99% 쿼리 감소** (100건 기준: 101 → 1 쿼리) - -**Email Handler (SMS 매칭):** -- 이전: 순차적 2개 SELECT 쿼리 -- 개선: JOIN으로 단일 쿼리 -- 성능: **50% 응답 시간 단축** - -### API 호출 최적화 - -**도메인 추천 (suggest_domains):** -- 이전: TLD별 순차 가격 조회 -- 개선: Promise.all 병렬 처리 -- 성능: **80% 시간 단축** (5개 TLD: 1초 → 0.2초) - -### 캐싱 전략 - -**KV Namespace 활용:** -- TLD 가격: 1시간 TTL -- Rate Limiting: 사용자별 60초 윈도우 -- 알림 Rate Limit: 1시간 (같은 타입) -- 서버 상담 세션: 1시간 TTL (자동 만료) - ---- - -## External Integrations - -**URL 커스터마이징:** 모든 외부 API URL은 `wrangler.toml`의 환경변수로 설정 가능합니다. -환경별로 다른 엔드포인트를 사용하거나, 자체 호스팅된 서비스로 교체할 수 있습니다. - -| 서비스 | 용도 | 엔드포인트 | 주의사항 | -|--------|------|-----------|----------| -| **AI Gateway** | OpenAI 프록시 | gateway.ai.cloudflare.com | 지역 제한 우회, 로그/캐시 | -| Context7 | 문서 조회 | context7.com API | - | -| Namecheap API | 도메인 백엔드 | namecheap-api.anvil.it.com | 날짜: MM/DD/YYYY → ISO 변환 | -| WHOIS API | WHOIS 조회 | whois-api-eight.vercel.app | ccSLD 미지원 | -| wttr.in | 날씨 | wttr.in | - | -| Brave Search | 검색 | api.search.brave.com | Free AI 플랜 (2,000/월) | -| Vault | API 키 관리 | vault.anvil.it.com | - | -| Email Routing | 입금 SMS 수신 | Cloudflare Email Routing | Worker email handler로 직접 처리 | - -### Cloudflare AI Gateway - -OpenAI API 호출을 Cloudflare AI Gateway를 통해 프록시하여 지역 제한 우회 - -``` -Gateway ID: telegram-bot -Account ID: d8e5997eb4040f8b489f09095c0f623c -URL: gateway.ai.cloudflare.com/v1/{account_id}/telegram-bot/openai/... -``` - -**적용 범위:** -- ✅ Chat Completions - AI Gateway 경유 -- ✅ 예치금 관리 - 코드 직접 처리 (Assistants API 제거) -- ✅ 도메인 관리 - 코드 직접 처리 - -**대시보드:** https://dash.cloudflare.com → AI → AI Gateway → telegram-bot - ---- - -## Deposit System - -**자동 매칭 흐름:** -``` -[시나리오 1: 사용자 먼저] -"홍길동 50000원 입금" → bank_notifications 검색 - ↓ - 매칭 O → confirmed + 잔액↑ | 매칭 X → pending - -[시나리오 2: SMS 먼저 - Email Routing] -은행 SMS → 메일 전달 → Cloudflare Email Routing → Worker (email handler) - ↓ - 파싱 → bank_notifications 저장 - ↓ - deposit_transactions 검색 (pending) - ↓ - 매칭 O → confirmed + 잔액↑ + 사용자/관리자 알림 - 매칭 X → 저장만 + 관리자 알림 -``` - -**알림 시스템:** -| 이벤트 | 사용자 알림 | 관리자 알림 | -|--------|-------------|-------------| -| 자동 매칭 성공 | ✅ 입금액 + 현재 잔액 | ✅ 입금 정보 + 매칭 완료 | -| 매칭 대기 (SMS만) | - | ✅ 입금 정보 + 대기 상태 | - -**매칭 로직 (입금자명 7글자 제한):** -``` -은행 SMS는 입금자명을 7글자까지만 표시 - ↓ -사용자 입력: "홍길동아버지님" (8글자) -은행 SMS: "홍길동아버지" (7글자) - ↓ -매칭 시 SUBSTR(depositor_name, 1, 7) 비교 → 매칭 성공 -``` - -- `deposit-agent.ts:72`: 사용자 입력의 앞 7글자로 bank_notifications 검색 -- `index.ts:908`: deposit_transactions의 앞 7글자와 SMS 입금자명 비교 - -**Email Routing 설정:** -- Cloudflare Dashboard → Email → Email Routing → Routes -- 수신 주소 → Worker: `telegram-summary-bot` 라우팅 - -**입금 계좌:** 하나은행 427-910018-27104 (주식회사 아이언클래드) -- Vault 경로: `secret/companies/ironclad-corp` - -**아키텍처 변경 (2026-01):** Assistants API → 코드 직접 처리 - -| 구분 | 이전 (Agent) | 현재 (코드) | -|------|-------------|-------------| -| 의도 파악 | Deposit Agent | 메인 AI (action 파라미터) | -| API 호출 | Agent Function Calling | `executeDepositFunction()` | -| 응답 형식 | Agent 생성 (불안정) | 코드 고정 (100% 일관성) | -| 지역 제한 | ❌ Assistants API 403 | ✅ AI Gateway 경유 | - -**manage_deposit 도구 파라미터:** -```typescript -{ - action: 'balance' | 'account' | 'request' | 'history' | 'cancel' | 'pending' | 'confirm' | 'reject', - depositor_name?: string, // request용 - amount?: number, // request용 (자연어→숫자 변환) - transaction_id?: number, // cancel, confirm, reject용 - limit?: number // history용 (기본 10) -} -``` - -**응답 포맷 (고정):** -``` -잔액 조회: "💰 현재 잔액: 10,000원" -입금 성공: "✅ 입금 확인 완료! • 입금액: 5,000원 • 현재 잔액: 15,000원" -입금 대기: "📋 입금 요청 등록 (#123) • 입금액: 5,000원" -거래 내역: "#5: 입금 10원 ✓ (01/17)" (✓확인, ⏳대기, ✗취소) -``` - -**action별 처리:** -| 함수 | 설명 | 권한 | -|------|------|------| -| `get_balance` | 잔액 조회 | 모든 사용자 | -| `get_account_info` | 입금 계좌 안내 | 모든 사용자 | -| `request_deposit` | 입금 신고 | 모든 사용자 | -| `get_transactions` | 거래 내역 | 모든 사용자 | -| `cancel_transaction` | 입금 취소 | 모든 사용자 | -| `get_pending_list` | 대기 목록 | 관리자 | -| `confirm_deposit` | 입금 확인 | 관리자 | -| `reject_deposit` | 입금 거절 | 관리자 | - -### Cron 자동 취소 (24시간) - -**목적:** 24시간 이상 대기 중인 입금 요청 자동 취소 + 사용자 알림 - -``` -wrangler.toml: - crons = ["0 15 * * *"] # UTC 15:00 = KST 00:00 (매일 자정) - ↓ -index.ts (scheduled 핸들러): - 1. pending + created_at > 24시간 거래 조회 - 2. status → cancelled 업데이트 - 3. 사용자에게 Telegram 알림 전송 -``` - -**wrangler.toml 설정:** -```toml -[triggers] -crons = ["0 15 * * *"] # KST 00:00 -``` - -**사용자 알림 메시지:** -``` -⏰ 입금 대기 자동 취소 - -거래 #123이 24시간 내 확인되지 않아 자동 취소되었습니다. -• 입금액: 10,000원 -• 입금자: 홍길동 - -실제 입금하셨다면 다시 신고해주세요. -``` - -### 거래 내역 표시 형식 - -**응답 포맷 (`formatDepositResult`):** -``` -#5: 입금 10,000원 ✓ (01/17) -#4: 출금 5,000원 ✓ (01/15) - 도메인 등록: example.com -#3: 입금 20,000원 ⏳ (01/14) -#2: 입금 5,000원 ✗ (01/10) -``` - -**상태 아이콘:** -| 상태 | 아이콘 | 설명 | -|------|--------|------| -| `confirmed` | ✓ | 확인 완료 | -| `pending` | ⏳ | 대기 중 | -| `cancelled` / `rejected` | ✗ | 취소/거절 | - -**타입 라벨:** -| DB 값 | 표시 | -|-------|------| -| `deposit` | 입금 | -| `withdrawal` | 출금 | -| `refund` | 환불 | - -**description 필드:** 거래 사유 (예: "도메인 등록: example.com") - -### Transaction Isolation & Optimistic Locking - -**문제:** D1 `batch()`는 진정한 트랜잭션이 아니므로 부분 실패 시 데이터 불일치 가능 - -**해결책:** Optimistic Locking 패턴 + 정합성 검증 Job - -**구현:** -``` -user_deposits 테이블에 version 컬럼 추가 - ↓ -잔액 변경 시마다 version 자동 증가 - ↓ -UPDATE 쿼리에서 WHERE version = ? 조건 검증 - ↓ -version 불일치 시 OptimisticLockError 발생 - ↓ -지수 백오프로 자동 재시도 (최대 3회) - ↓ -재시도 실패 시 사용자 친화적 에러 메시지 -``` - -**관련 파일:** -| 파일 | 역할 | -|------|------| -| `utils/optimistic-lock.ts` | Optimistic Locking 유틸리티 (재시도 로직) | -| `utils/reconciliation.ts` | 잔액 정합성 검증 (Cron 실행) | -| `deposit-agent.ts` | 입금 처리에 Optimistic Locking 적용 | -| `domain-register.ts` | 도메인 등록 결제에 Optimistic Locking 적용 | -| `migrations/002_add_version_columns.sql` | 스키마 마이그레이션 | - -**적용 대상:** -- `request_deposit` (auto_matched case): 은행 알림 자동 매칭 시 잔액 증가 -- `confirm_deposit`: 관리자 수동 확인 시 잔액 증가 -- `executeDomainRegister`: 도메인 등록 시 잔액 차감 (Double-spending 방지) - -**정합성 검증 (Reconciliation):** -``` -매일 KST 00:00 Cron 실행 - ↓ -user_deposits.balance vs SUM(confirmed transactions) 비교 - ↓ -불일치 발견 시: - 1. 로그에 상세 기록 - 2. 관리자에게 Telegram 알림 - 3. 검증 리포트 생성 -``` - -**마이그레이션:** +**문서 보기:** ```bash -# 로컬 테스트 -wrangler d1 execute telegram-conversations --local --file=migrations/002_add_version_columns.sql - -# 프로덕션 적용 (데이터 백업 권장) -wrangler d1 execute telegram-conversations --file=migrations/002_add_version_columns.sql - -# 검증 -wrangler d1 execute telegram-conversations --command "SELECT user_id, balance, version FROM user_deposits LIMIT 5" -``` - -**동시성 시나리오 예시:** -``` -사용자 A: 잔액 10,000원 (version=1) - ↓ -동시 요청: - 입금 +5,000원 (요청1) - 입금 +3,000원 (요청2) - ↓ -요청1: version=1 읽음 → UPDATE (version=2) ✅ 성공 -요청2: version=1 읽음 → UPDATE (version=1) ❌ 실패 (version 불일치) - ↓ -요청2 재시도: version=2 읽음 → UPDATE (version=3) ✅ 성공 - ↓ -최종: 잔액 18,000원 (version=3) ✅ 정합성 보장 -``` - ---- - -## Server System - -**목적:** 클라우드 서버 추천 및 관리 (Cloud Orchestrator 연동) - -**연결 방식:** Cloudflare Service Binding (Worker-to-Worker 직접 통신) - -**manage_server 도구 파라미터:** -```typescript -{ - action: 'recommend' | 'order' | 'start' | 'stop' | 'delete' | 'list', - tech_stack?: string[], // recommend용 (필수) - expected_users?: number, // recommend용 (필수) - use_case?: string, // recommend용 (필수) - traffic_pattern?: 'steady' | 'spiky' | 'growing', - region_preference?: string[], - budget_limit?: number, - lang?: 'ko' | 'ja' | 'zh' | 'en', // 자동 감지 - server_id?: string, // order/start/stop/delete용 - region_code?: string, // order용 - label?: string, // order용 -} -``` - -**action별 상태:** -| action | 설명 | 상태 | -|--------|------|------| -| `recommend` | 서버 추천 | ✅ 구현 완료 | -| `order` | 서버 신청 | 🚧 준비 중 | -| `start` | 서버 시작 | 🚧 준비 중 | -| `stop` | 서버 중지 | 🚧 준비 중 | -| `delete` | 서버 해지 | 🚧 준비 중 | -| `list` | 내 서버 목록 | 🚧 준비 중 | - -**추천 결과 포맷:** -``` -🖥️ 서버 추천 결과 - -1️⃣ Standard 8GB (Anvil) - • 스펙: 4vCPU / 8GB / 160GB SSD - • 리전: Tokyo 3 (JP) - • 가격: ₩69,719/월 (대역폭 5TB) - • 예상 트래픽: 1.7TB (포함 범위 내) ← 항상 표시 - • 점수: 95점 / 최대 7,500명 -``` - -**대역폭 정보 표시 규칙:** -- 초과 없음: `예상 트래픽: X.XTB (포함 범위 내)` -- 초과 있음: `예상 트래픽: X.XTB → 초과 X.XTB (₩X,XXX)` - -**CDN 캐시 히트율 추정:** -- tech_stack에 `cloudflare`, `cdn` 등 포함 시 자동 적용 -- 비디오 스트리밍: 92%, 정적 사이트: 95%, API: 30%, 이커머스: 70% - -**언어 자동 감지:** -- 한글 → `ko`, 히라가나/가타카나 → `ja`, 한자 → `zh`, 기본값 → `en` - -**Service Binding 설정 (wrangler.toml):** -```toml -[[services]] -binding = "CLOUD_ORCHESTRATOR" -service = "cloud-orchestrator" -``` - -### 서버 주문 상태 전이 - -**상태 정의:** -| 상태 | 설정 주체 | UI 표시 | 설명 | -|------|----------|---------|------| -| `pending` | telegram-bot | ❌ 표시 안 함 | Queue 대기 중 (내부용) | -| `provisioning` | cloud-orchestrator | ✅ 🔄 생성 중... | Cloud Orchestrator 처리 중 | -| `active` | cloud-orchestrator | ✅ 🟢 가동 중 | 서버 준비 완료 | -| `stopped` | cloud-orchestrator | ✅ ⛔ 중지됨 | 서버 중지됨 | -| `failed` | cloud-orchestrator | ❌ 표시 안 함 | 프로비저닝 실패 | -| `terminated` | telegram-bot | ❌ 표시 안 함 | 서버 삭제 완료 | - -**상태 전이 흐름:** -``` -사용자: "서버 신청" - ↓ -telegram-bot: POST /api/provision (status = 'pending') - ↓ Queue 등록 -cloud-orchestrator: Worker 처리 - ├─ Queue 가져오기 → status = 'provisioning' - ├─ Incus 인스턴스 생성 (2-5분) - └─ 완료 → status = 'active' - ↓ -"내 서버 목록": - - pending: 표시 안 함 (내부용) - - provisioning: "🔄 생성 중..." 표시 - - active: "🟢 가동 중" 표시 -``` - -**상태 변경 권한:** -- **telegram-bot**: `pending` 설정 (주문 생성), `terminated` 설정 (삭제 후) -- **cloud-orchestrator**: `provisioning`, `active`, `stopped`, `failed` 설정 - -**"내 서버 목록" 표시 규칙:** -- ✅ **표시**: `provisioning`, `active` (+ provider_instance_id 존재) -- ❌ **제외**: `pending`, `failed`, `terminated`, `deleted` -- ❌ **제외**: `active`인데 `provider_instance_id`가 없는 경우 (프로비저닝 실패) - ---- - -## Domain System - -**아키텍처 변경 (2025-01):** Agent 기반 → 코드 직접 처리 - -| 구분 | 이전 (Agent) | 현재 (코드) | -|------|-------------|-------------| -| 의도 파악 | Domain Agent | 메인 AI (action 파라미터) | -| API 호출 | Agent Function Calling | `executeDomainAction()` | -| 응답 형식 | Agent 생성 (불안정) | 코드 고정 (100% 일관성) | -| 비용 | Agent 호출당 ~$0.01 | 없음 | - -**manage_domain 도구 파라미터:** -```typescript -{ - action: 'register' | 'check' | 'whois' | 'list' | 'info' | 'get_ns' | 'set_ns' | 'price' | 'cheapest', - domain?: string, // 대상 도메인 - nameservers?: string[], // set_ns용 - tld?: string // price용 -} -``` - -**action별 처리:** -| action | 설명 | 권한 | -|--------|------|------| -| `list` | 내 도메인 목록 | 소유자 | -| `info` | 도메인 상세정보 | 소유자 | -| `get_ns` | 네임서버 조회 | 공개 | -| `set_ns` | 네임서버 변경 | 소유자 | -| `check` | 가용성 확인 + 가격 | 공개 | -| `whois` | WHOIS 조회 | 공개 | -| `price` | TLD 가격 | 공개 | -| `cheapest` | 가장 저렴한 TLD 목록 (TOP 15) | 공개 | -| `register` | 등록 확인 페이지 | 사용자 | - -### 도메인 등록 흐름 - -``` -사용자: "example.com 등록해줘" - ↓ -메인 AI: manage_domain(action="register", domain="example.com") - ↓ -executeDomainAction(): - 1. check_domains API → 가용성 확인 - 2. get_price API → 가격 조회 - 3. DB 조회 → 현재 잔액 확인 - 4. 고정 형식 응답 생성 - ↓ -┌─────────────────────┬─────────────────────┐ -│ 잔액 충분 시 │ 잔액 부족 시 │ -├─────────────────────┼─────────────────────┤ -│ 📋 도메인 등록 확인 │ 📋 도메인 등록 확인 │ -│ • 도메인: example.com│ • 도메인: example.com│ -│ • 가격: 15,000원 │ • 가격: 15,000원 │ -│ • 현재 잔액: ✓ │ • 현재 잔액: ⚠️ 부족 │ -│ • 등록 기간: 1년 │ • 부족 금액: X원 │ -│ 📌 등록자 정보 │ │ -│ ⚠️ 취소/환불 불가 │ 💳 입금 계좌 │ -│ │ 하나은행 427-... │ -│ '확인' 입력 요청 │ 입금 안내 │ -└─────────────────────┴─────────────────────┘ -``` - -### 인라인 버튼 확인 플로우 (Callback Query) - -**목적:** 사용자에게 "확인/취소" 버튼 표시 후 클릭으로 등록 진행 - -``` -executeDomainAction(register): - 1. __KEYBOARD__{type, domain, price}__END__ 마커 포함 응답 생성 - ↓ -telegram.ts (sendMessage): - 2. __KEYBOARD__ 감지 → 마커 파싱 → inline_keyboard 생성 - ↓ -Telegram: - 3. 사용자에게 "✅ 등록 확인 / ❌ 취소" 버튼 표시 - ↓ -index.ts (callback_query 핸들러): - 4. 버튼 클릭 감지 → data 파싱 → domain-register.ts 호출 - ↓ -domain-register.ts: - 5. 잔액 재확인 → Optimistic Locking으로 잔액 차감 → 실제 등록 API 호출 → 결과 반환 -``` - -**관련 코드:** -| 파일 | 역할 | -|------|------| -| `openai-service.ts:786-807` | `__KEYBOARD__` 마커 생성 | -| `telegram.ts:sendMessage()` | 마커 파싱 → inline_keyboard 변환 | -| `index.ts:callback_query` | 버튼 클릭 핸들링 | -| `domain-register.ts` | 실제 도메인 등록 실행 (Optimistic Locking 적용) | - -**보안 개선 (2026-01):** -- Optimistic Locking 패턴 적용으로 Double-spending 방지 -- version 컬럼 기반 동시성 제어 -- 자동 재시도 (최대 3회, 지수 백오프) -- 동시성 충돌 시 사용자 친화적 에러 메시지 - -**버튼 콜백 데이터 형식:** -```typescript -// 확인: confirm_domain_register:example.com:15000 -// 취소: cancel_domain_register:example.com -``` - -### 도메인 추천 기능 (`suggest_domains`) - -**별도 구현된 코드 레벨 도구** - -``` -사용자: "커피숍 도메인 추천해줘" - ↓ -1. GPT-4o-mini: 키워드 기반 창의적 도메인 15개 생성 - ↓ -2. Namecheap API: check_domains로 가용성 일괄 확인 - ↓ -3. 등록 가능 도메인 < 10개? → 1-2 반복 (최대 3회) - ↓ -4. Namecheap API: TLD별 가격 조회 - ↓ -5. 결과 포맷팅 (등록 가능한 것만 표시, 10개 목표) -``` - -**특징:** -- 등록 가능 도메인만 표시 (이미 등록된 도메인 미표시) -- 10개 미만 시 자동 재시도 (최대 3회) -- 이전에 체크한 도메인은 제외하고 새로 생성 - -**Namecheap API:** -- 엔드포인트: `namecheap-api.anvil.it.com` -- 가격 정책: Namecheap 원가 + 13%, 매일 환율 업데이트 -- 권한 체크: `user_domains` 테이블 `verified=1` - -**Production/Sandbox 전환:** -```bash -# namecheap-api 서버의 .env 파일 -NAMECHEAP_API_USER=your_username -NAMECHEAP_SANDBOX=false # true: 테스트 모드, false: 실제 등록 -``` - -| 환경 | NAMECHEAP_SANDBOX | API 엔드포인트 | -|------|-------------------|----------------| -| Production | `false` | api.namecheap.com | -| Sandbox | `true` | api.sandbox.namecheap.com | - -**⚠️ 주의:** Sandbox에서 등록한 도메인은 실제로 등록되지 않음 - -**등록자 정보:** -- 현재: 서비스 기본 정보만 지원 (일본 주소) -- WHOIS Guard 자동 적용 (개인정보 비공개) -- 추후: 사용자 본인 정보로 등록 옵션 추가 예정 - -**Deposit API (namecheap-api용):** -``` -GET /api/deposit/balance?telegram_id=xxx # 잔액 조회 -POST /api/deposit/deduct # 잔액 차감 - { telegram_id, amount, reason } -Header: X-API-Key: DEPOSIT_API_SECRET +npx swagger-ui-watcher openapi.yaml # Swagger UI +npx redoc-cli bundle openapi.yaml -o docs/api.html # Redoc HTML +npx @apidevtools/swagger-cli validate openapi.yaml # 스펙 검증 ```