Files
telegram-bot-workers/CLAUDE.md
kappa 2b1bc6a371 feat: improve server management and refund display
Server Management:
- Fix /server command API auth (query param instead of header)
- Show server specs (vCPU/RAM/Bandwidth) in /server list
- Prevent AI from refusing server deletion based on expiration date
- Add explicit instructions in tool description and system prompt

Refund Display:
- Show before/after balance in server deletion refund message
- Format: 환불 전 잔액 → 환불 금액 → 환불 후 잔액

Other Changes:
- Add stopped status migration for server orders
- Clean up callback handler (remove deprecated code)
- Update constants and pattern utilities

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 05:30:59 +09:00

56 KiB
Raw Blame History

CLAUDE.md

🔧 개발자용 문서: 기술 상세, 코드 패턴, 트러블슈팅 📖 README.md: 기능 소개, 배포 가이드, 사용법

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Auto-Read on Start

세션 시작 시 반드시 수행:

  1. README.md를 Read 도구로 읽어 프로젝트 전체 구조 파악
  2. 작업 대상 파일의 기존 코드 먼저 확인
필수 읽기: README.md → 작업 대상 파일

Agent Usage Policy

🎯 목표: 컨텍스트 절약 - 대부분의 작업을 에이전트에 위임

프로젝트 특성:

  • 언어: TypeScript (strict mode)
  • 런타임: Cloudflare Workers
  • 프레임워크: Wrangler, Workers AI, D1
  • 주요 디렉토리: src/tools/, src/routes/, src/services/

사용 가능한 에이전트 타입:

  • coder: 20년 이상의 경험을 갖고 있는 시니어 코딩 전문가 (코드 작성/수정 최우선)
    • TypeScript/Cloudflare Workers 구현 마스터
    • 프로덕션 수준의 코드 품질 보장
    • 엔터프라이즈급 에러 핸들링 및 타입 안정성
    • 성능 최적화 및 베스트 프랙티스 적용
    • 도구: Read, Write, Edit, Bash, Glob, Grep (쓰기 가능)
  • explorer: 코드베이스 탐색/분석 전문가 (thorough 레벨)
    • 프로젝트 구조 파악 및 의존성 분석
    • 대량 파일 읽기 및 패턴 인식
    • 도구: Read, Grep, Glob (읽기 전용)
  • planner: 설계/계획 수립 전문가
    • 아키텍처 설계 및 구현 계획 작성
    • 시스템 분석 및 개선 방향 제시
    • 도구: Read, Grep, Glob (읽기 전용)
  • reviewer: 코드 리뷰 전문가
    • 품질/보안/성능 검토
    • 페르소나 조합으로 전문화 (qa/security/performance)
    • 도구: Read, Grep, Glob (읽기 전용)
  • Bash: 빌드/배포/테스트 실행
    • 긴 로그 출력 분리 (컨텍스트 절약)
    • 시스템 명령어 실행

CRITICAL: 다음 작업은 반드시 Task tool (agent)를 사용하여 메인 세션 컨텍스트 절약:

작업 유형 조건 에이전트 타입 이유
코드 작성/수정 모든 코드 변경 coder TS/Workers 전문, 타입 안정성, 프로덕션 품질
리팩토링 파일 수 무관 coder (병렬) 일관성, 컨텍스트 분리, TS 최적화
Function Calling 도구 추가/수정 coder (병렬) tools/ + openai-service.ts 동시 처리
스키마 작업 D1 마이그레이션 coder 백업→마이그레이션→검증 전체 위임
프로젝트 분석 구조 파악 explorer (thorough) 대량 파일 읽기 분리
설계/계획 아키텍처 설계 planner 시스템 분석 및 개선 방향 제시
코드 리뷰 보안/성능 reviewercoder 분석 후 개선 제안 (reviewer는 읽기 전용)
빌드/배포 npm run, wrangler Bash 긴 로그 출력 분리
테스트 로컬 테스트 실행 Bash 테스트 출력 분리

에이전트 위임의 이점:

  • 각 에이전트가 독립 컨텍스트 사용 (메인 세션 부담 0)
  • 요약만 메인 세션에 반환 (토큰 대폭 절약)
  • 병렬 처리 가능 (시간 단축)
  • 메인 세션은 조율/지시만 담당

병렬 처리 필수:

  • 독립적인 파일 여러 개 → 병렬 coder 에이전트
  • 다른 디렉토리 동시 작업 → 병렬 coder 에이전트
  • Function Calling 도구 추가 → tools/{new}.ts + openai-service.ts 병렬

예시:

// ❌ 직접 수정 (컨텍스트 소모)
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

절대 지켜야 할 규칙:

규칙 이유
배포 전 npm run dev 로컬 테스트 프로덕션 장애 방지
D1 스키마 변경 시 마이그레이션 SQL 별도 작성 기존 데이터 보존
Secrets(BOT_TOKEN, API_KEY 등) 코드에 하드코딩 금지 보안
wrangler.toml의 ID 값 변경 금지 리소스 연결 유지
Function Calling 도구 추가 시 tools 배열 + executeFunctionCall() 동시 수정 불일치 방지

위험한 작업:

  • wrangler d1 execute 직접 실행 (production) → 반드시 확인 요청
  • user_deposits, deposit_transactions 테이블 직접 수정 → 금전 관련, 주의

Documentation Rules

작업 완료 후 문서 자동 업데이트:

트리거 조건

변경 유형 업데이트 대상
src/*.ts 파일 수정 CLAUDE.md (Core Services, 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

OpenAPI Specification: openapi.yaml

문서 보기:

# 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 run dev           # 로컬 개발 (wrangler dev)
npm run deploy        # Cloudflare Workers 배포
npm run db:init       # D1 스키마 초기화 (production) ⚠️ 주의
npm run db:init:local # D1 스키마 초기화 (local)
npm run tail          # Workers 로그 스트리밍
npm test              # 단위 테스트 실행 (Vitest)
npm run test:watch    # Watch 모드
npm run test:coverage # 커버리지 리포트

KV Namespace 생성 (최초 1회):

# Rate Limiting용 KV Namespace 생성 (필수)
wrangler kv:namespace create RATE_LIMIT_KV

# 서버 상담 세션용 KV Namespace 생성 (서버 추천 기능 사용 시 필수)
wrangler kv:namespace create SESSION_KV

# 출력된 id를 wrangler.toml의 [[kv_namespaces]] 섹션에 입력

Secrets 설정:

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 설정 (token + secret 필요)
curl "https://telegram-summary-bot.kappa-d8e.workers.dev/setup-webhook?token=${BOT_TOKEN}&secret=${WEBHOOK_SECRET}"

# Webhook 정보 조회 (token + secret 필요)
curl "https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info?token=${BOT_TOKEN}&secret=${WEBHOOK_SECRET}"

Database Migrations:

# 로컬 테스트
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):

-- 인덱스 사용 확인
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):

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

TypeScript

  • Strict mode: tsconfig.json에서 strict 활성화
  • 타입 정의: types.ts에 인터페이스 집중 관리
  • any 사용 금지: 불가피한 경우 주석으로 이유 명시

에러 핸들링

// 패턴: try-catch + 사용자 친화적 메시지 + 구조화된 로깅
import { createLogger } from './utils/logger';
const logger = createLogger('service-name');

try {
  // 작업
} catch (error) {
  logger.error('작업 실패', error as Error, { context: 'data' });
  return '죄송합니다. 일시적인 오류가 발생했습니다.';
}

로깅 규칙

// 구조화된 로깅 (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' });   // 경고

// 성능 측정
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)

프레임워크: Vitest + Miniflare (Cloudflare Workers 환경 시뮬레이션)

실행 명령어:

npm test              # 모든 테스트 실행
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원 입금 시도
동시성 처리 동일 사용자 동시 입금, Race condition
Batch 실패 처리 db.batch() 부분 실패 시뮬레이션
7글자 매칭 "홍길동아버지님" → "홍길동아버지" 자동 매칭
관리자 권한 비관리자 confirm/reject/pending 차단
거래 상태 confirmed 거래 취소 차단
Edge Cases 999,999,999원, 특수문자, 1글자 이름

Mock 전략:

  • D1 Database: Miniflare in-memory SQLite
  • Environment Variables: vitest.config.ts에서 바인딩
  • KV Namespace: Rate Limiting 모킹

헬퍼 함수 (tests/setup.ts):

createTestUser(telegramId, username)        // 테스트용 사용자 생성
createBankNotification(depositorName, amount) // 은행 알림 생성
createDepositTransaction(userId, amount, status) // 거래 생성
getTestDB()                                  // DB 바인딩 가져오기

추가 예정:

  • openai-service.ts - Function Calling 도구 테스트
  • summary-service.ts - 프로필 시스템 테스트
  • Integration Tests - 전체 워크플로우 테스트

수동 테스트 (Webhook)

로컬 테스트 절차:

# 1. 로컬 D1 초기화 (최초 1회)
npm run db:init:local

# 2. 로컬 서버 실행
npm run dev

# 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":"테스트"}}'

배포 후 확인:

# 로그 스트리밍
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)

목적: Claude Code가 봇과 대화하여 기능을 테스트할 때 사용

API 엔드포인트

# 봇과 대화
curl -s -X POST 'https://telegram-cli-web.kappa-d8e.workers.dev/api/chat' \
  -H 'Content-Type: application/json' \
  -d '{"message": "안녕"}'

응답 형식:

{
  "response": "봇 응답 텍스트...",
  "time_ms": 1234
}

Claude가 사용하는 경우

테스트 시나리오:

# 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

Secrets 필요:

  • BOT_TOKEN: Telegram Bot Token (Vault: telegram-bot)
  • WEBHOOK_SECRET: Bot Worker /api/test 인증용 (Vault: telegram-bot)

배포 명령:

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 파이프라인에서 자동 테스트

Troubleshooting

자주 발생하는 에러

증상 원인 해결
D1_ERROR: no such table 스키마 미적용 npm run db:init 실행
401 Unauthorized (OpenAI) API 키 만료/잘못됨 wrangler secret put OPENAI_API_KEY
Webhook 응답 없음 Secret Token 불일치 WEBHOOK_SECRET 재설정 후 /setup-webhook
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만 허용됨

디버깅 명령어

# D1 데이터 직접 조회 (로컬)
wrangler d1 execute telegram-conversations --local --command "SELECT * FROM users LIMIT 5"

# D1 데이터 직접 조회 (production) ⚠️ 주의
wrangler d1 execute telegram-conversations --command "SELECT * FROM users LIMIT 5"

Security

Endpoint Security

공개 엔드포인트:

엔드포인트 보안 수준 설명
/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 정책:

// 문의 폼 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'

Rate Limiting (Cloudflare KV 기반):

  • 사용자별 메시지 제한 (30 requests / 60초)
  • KV Namespace: RATE_LIMIT_KV (wrangler.toml)
  • 인스턴스 간 공유, 재시작 후 유지
  • 자동 만료 (TTL), 분산 환경 일관성 보장
  • 과도한 요청 시 경고 메시지 + 차단

Health Check 정책

이전 (보안 취약):

{
  "status": "ok",
  "timestamp": "...",
  "stats": {
    "users": 123,        // DB 테이블 정보 노출
    "summaries": 456     // 민감한 통계 노출
  }
}

현재 (보안 강화):

{
  "status": "ok",
  "timestamp": "2026-01-19T12:34:56.789Z"  // 최소 정보만
}

상세 정보 필요 시:

  • 별도 인증된 Admin 엔드포인트 추가 검토 (미구현)
  • 또는 Cloudflare Dashboard의 Analytics 활용

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시간)

사용 예시:

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
  }
);

알림 메시지 형식:

🚨 시스템 알림 (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 에러 시

에러 핸들링:

  • 알림 전송 실패 시 로그만 기록하고 무시
  • 메인 로직에 영향 없음

테스트:

# 테스트 엔드포인트를 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 사용 예시:

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) 자동 전환

에러 핸들링 구조 (index.ts:handleMessage)

Webhook 수신
    ↓
보안 검증 실패 → 401 반환 (로그 기록)
    ↓
Rate Limit 초과 → 경고 메시지 전송 + return
    ↓
사용자 DB 조회/생성 (try-catch)
    ↓ 실패 시 → "일시적인 오류" 메시지 전송 + return
    ↓
메시지 처리 (전체 try-catch)
    ├── 명령어 처리 (handleCommand)
    └── AI 응답 생성 (generateAIResponse)
    ↓ 실패 시 → "메시지 처리 오류" 메시지 전송
    ↓
응답 전송 (sendMessage)

중요: 모든 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시간 (자동 만료)
  • 상태: gatheringrecommendingcompleted

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

Function Calling 추가 방법

// 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);

프로필 시스템 흐름 (3개 요약 통합 방식)

메시지 수신 → message_buffer 저장 (최대 19개)
                    ↓ 20개 도달
     ┌──────────────┴──────────────┐
     ↓                              ↓
기존 요약 3개 조회            사용자 발언만 추출
     ↓                              ↓
     └──────────→ OpenAI 통합 분석 ←┘
                        ↓
              summaries 테이블 저장 (generation++)
                        ↓
              구버전 삭제 (최근 3개만 유지)

통합 분석 방식:

  • 기존: 최신 요약 1개만 참조하여 업데이트
  • 변경: 모든 요약 (최대 3개) + 새 메시지 → AI가 통합 분석

Context Enrichment

// 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 도구 파라미터:

{
  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 설정:

[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. 검증 리포트 생성

마이그레이션:

# 로컬 테스트
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 도구 파라미터:

{
  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):

[[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 도구 파라미터:

{
  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회, 지수 백오프)
  • 동시성 충돌 시 사용자 친화적 에러 메시지

버튼 콜백 데이터 형식:

// 확인: 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 전환:

# 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