보안 개선: - API 키 하드코딩 제거 (NAMECHEAP_API_KEY_INTERNAL) - CORS 정책: * → hosting.anvil.it.com 제한 - /health 엔드포인트 DB 정보 노출 방지 - Rate Limiting 인메모리 Map → Cloudflare KV 전환 - 분산 환경 일관성 보장 - 재시작 후에도 유지 - 자동 만료 (TTL) 문서: - CLAUDE.md Security 섹션 추가 - KV Namespace 설정 가이드 추가 - 배포/마이그레이션 가이드 추가 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
29 KiB
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
세션 시작 시 반드시 수행:
README.md를 Read 도구로 읽어 프로젝트 전체 구조 파악- 작업 대상 파일의 기존 코드 먼저 확인
필수 읽기: 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 주석 업데이트
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 run chat # CLI 테스트 클라이언트
CLI 테스트 클라이언트:
# .env 파일 생성 (최초 1회)
echo 'WEBHOOK_SECRET=...' > .env # Vault: secret/data/telegram-bot
# 대화형 모드
npm run chat
# 단일 메시지 모드
npm run chat "날씨 알려줘"
KV Namespace 생성 (최초 1회):
# Rate Limiting용 KV Namespace 생성
wrangler kv:namespace create RATE_LIMIT_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 설정:
curl https://telegram-summary-bot.kappa-d8e.workers.dev/setup-webhook
curl https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info
Code Style & Conventions
TypeScript
- Strict mode:
tsconfig.json에서 strict 활성화 - 타입 정의:
types.ts에 인터페이스 집중 관리 - any 사용 금지: 불가피한 경우 주석으로 이유 명시
에러 핸들링
// 패턴: try-catch + 사용자 친화적 메시지
try {
// 작업
} catch (error) {
console.error('[ServiceName] 작업 실패:', error);
return '죄송합니다. 일시적인 오류가 발생했습니다.';
}
로깅 규칙
console.log('[ServiceName] 동작 설명'); // 정상 동작
console.error('[ServiceName] 에러 설명:', error); // 에러
// wrangler tail로 확인 가능
네이밍
- 파일:
kebab-case.ts(예:openai-service.ts) - 함수:
camelCase(예:executeFunctionCall) - 상수:
UPPER_SNAKE_CASE(예:SUMMARY_THRESHOLD) - 타입/인터페이스:
PascalCase(예:TelegramUpdate)
Testing
현재 테스트 스크립트 없음 - 수동 테스트 필수
로컬 테스트 절차
# 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 상태 확인
curl https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info
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 필요 | 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만 |
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 활용
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() |
security.ts |
Webhook 보안, Rate Limiting (KV) | validateWebhook(), checkRateLimit() |
commands.ts |
봇 명령어 | handleCommand() |
telegram.ts |
Telegram API | sendMessage(), sendTypingAction() |
Function Calling Tools (8개):
| 도구 | 함수명 | 외부 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 |
코드 직접 처리 | 입금, 충전, 잔액, 계좌, 송금 |
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, ...
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가 도구를 호출하지 않고 직접 답변하는 경우:
- 시스템 프롬프트에 해당 키워드 추가 (
summary-service.ts:252-254) - 도구 description에 키워드 명시 (
openai-service.tstools 배열)
검색 한글→영문 자동 번역
"판골린 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 |
WEBHOOK_SECRET |
- | Telegram Webhook 인증 (wrangler secret, Vault: telegram-bot) |
BRAVE_API_KEY |
- | Brave Search API 키 (wrangler secret) |
DEPOSIT_API_SECRET |
- | Deposit API 인증 키 (namecheap-api용, wrangler secret) |
KV Namespaces:
| Binding | 설명 | 생성 명령 |
|---|---|---|
RATE_LIMIT_KV |
Rate Limiting 저장소 | wrangler kv:namespace create RATE_LIMIT_KV |
Bindings:
| Binding | 타입 | 용도 |
|---|---|---|
DB |
D1 Database | 사용자/메시지/예치금 데이터 |
AI |
Workers AI | OpenAI 폴백용 |
RATE_LIMIT_KV |
KV Namespace | 사용자별 Rate Limiting (30 req/60s) |
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/월) |
| 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
Web Page (Cloudflare Pages)
URL: https://hosting.anvil.it.com
프로젝트: anvil-hosting (Cloudflare Pages)
소스: web/index.html
배포:
wrangler pages deploy web --project-name anvil-hosting
구성:
- 로고: 3D 메탈릭 모루 + 대장간 망치 + 불꽃 (SVG)
- 서비스 순서: 도메인 → DDoS → 해외서버 → 웹호스팅
- 푸터 사업자 정보: namecheap-api 등록자 정보와 동일
등록자 정보 소스:
ssh npm-linode-1
cat /home/admin/namecheap_api/.env # REGISTRANT_* 변수들
푸터 정보:
- LIBEHAIM Inc. | Taro Tanaka
- #202 K-Flat, 3-1-13 Higashioi, Shinagawa-ku, Tokyo 140-0011, Japan
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")
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. 잔액 재확인 → 실제 등록 API 호출 → 결과 반환
관련 코드:
| 파일 | 역할 |
|---|---|
openai-service.ts:786-807 |
__KEYBOARD__ 마커 생성 |
telegram.ts:sendMessage() |
마커 파싱 → inline_keyboard 변환 |
index.ts:callback_query |
버튼 클릭 핸들링 |
domain-register.ts |
실제 도메인 등록 실행 |
버튼 콜백 데이터 형식:
// 확인: 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