- Auto-Read: 세션 시작 시 README.md 필수 읽기 - Critical Rules: 배포/보안/위험 작업 규칙 추가 - Documentation Rules: 트리거 조건 테이블화 - Code Style: TypeScript, 에러 핸들링, 로깅, 네이밍 규칙 - Testing: 로컬 테스트 절차, 배포 후 확인 - Troubleshooting: 자주 발생하는 에러 6개 + 해결법 - 기존 섹션 테이블 형식으로 가독성 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
334 lines
11 KiB
Markdown
334 lines
11 KiB
Markdown
# CLAUDE.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 → 작업 대상 파일
|
|
```
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
```bash
|
|
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 로그 스트리밍
|
|
```
|
|
|
|
**Secrets 설정:**
|
|
```bash
|
|
wrangler secret put BOT_TOKEN # Telegram Bot Token
|
|
wrangler secret put WEBHOOK_SECRET # Webhook 검증용
|
|
wrangler secret put OPENAI_API_KEY # OpenAI API 키
|
|
```
|
|
|
|
**Webhook 설정:**
|
|
```bash
|
|
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 사용 금지**: 불가피한 경우 주석으로 이유 명시
|
|
|
|
### 에러 핸들링
|
|
```typescript
|
|
// 패턴: try-catch + 사용자 친화적 메시지
|
|
try {
|
|
// 작업
|
|
} catch (error) {
|
|
console.error('[ServiceName] 작업 실패:', error);
|
|
return '죄송합니다. 일시적인 오류가 발생했습니다.';
|
|
}
|
|
```
|
|
|
|
### 로깅 규칙
|
|
```typescript
|
|
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
|
|
|
|
**현재 테스트 스크립트 없음** - 수동 테스트 필수
|
|
|
|
### 로컬 테스트 절차
|
|
```bash
|
|
# 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":"테스트"}}'
|
|
```
|
|
|
|
### 배포 후 확인
|
|
```bash
|
|
# 로그 스트리밍
|
|
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`의 정규식 패턴 확인 |
|
|
|
|
### 디버깅 명령어
|
|
```bash
|
|
# 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"
|
|
```
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
**Message Flow:**
|
|
```
|
|
Telegram Webhook → Security Validation → Command/Message Router
|
|
↓
|
|
┌──────────────────────────┴──────────────────────────┐
|
|
↓ ↓
|
|
Command Handler AI Response Generator
|
|
(commands.ts) (openai-service.ts)
|
|
↓
|
|
Function Calling
|
|
(weather, search, time, calc, docs)
|
|
↓
|
|
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()` |
|
|
| `security.ts` | Webhook 보안 | `validateWebhook()`, `checkRateLimit()` |
|
|
| `commands.ts` | 봇 명령어 | `handleCommand()` |
|
|
| `telegram.ts` | Telegram API | `sendMessage()`, `sendTypingAction()` |
|
|
|
|
**Function Calling Tools (7개):**
|
|
| 도구 | 함수명 | 외부 API |
|
|
|------|--------|----------|
|
|
| 날씨 | `get_weather` | wttr.in |
|
|
| 검색 | `web_search` | DuckDuckGo |
|
|
| 시간 | `get_current_time` | 내장 |
|
|
| 계산 | `calculate` | 내장 |
|
|
| 문서 | `lookup_docs` | Context7 |
|
|
| 도메인 | `manage_domain` | Domain Agent → Namecheap |
|
|
| 예치금 | `manage_deposit` | D1 |
|
|
|
|
**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 |
|
|
|
|
**AI Fallback:** OpenAI 미설정 시 Workers AI (Llama 3.1 8B) 자동 전환
|
|
|
|
---
|
|
|
|
## Key Patterns
|
|
|
|
### Function Calling 추가 방법
|
|
```typescript
|
|
// 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);
|
|
```
|
|
|
|
### 프로필 시스템 흐름
|
|
```
|
|
메시지 수신 → message_buffer 저장 (최대 19개)
|
|
↓ 20개 도달
|
|
사용자 발언만 추출 → OpenAI 분석
|
|
↓
|
|
summaries 테이블 저장 (generation++)
|
|
↓
|
|
구버전 삭제 (최근 3개만 유지)
|
|
```
|
|
|
|
### Context Enrichment
|
|
```typescript
|
|
// getConversationContext() 반환값 구조
|
|
{
|
|
profile: "이전 프로필 요약",
|
|
recentMessages: [ /* 최근 10개 */ ]
|
|
}
|
|
// → AI 프롬프트의 system 메시지에 포함
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
`wrangler.toml` 환경변수:
|
|
| 변수 | 기본값 | 설명 |
|
|
|------|--------|------|
|
|
| `SUMMARY_THRESHOLD` | 20 | 프로필 업데이트 주기 (메시지 수) |
|
|
| `MAX_SUMMARIES_PER_USER` | 3 | 유지할 프로필 버전 수 |
|
|
| `DOMAIN_AGENT_ID` | - | OpenAI Assistant ID |
|
|
| `DOMAIN_OWNER_ID` | - | 도메인 관리 권한 Telegram ID |
|
|
| `DEPOSIT_ADMIN_ID` | - | 예치금 관리 권한 Telegram ID |
|
|
|
|
---
|
|
|
|
## External Integrations
|
|
|
|
| 서비스 | 용도 | 엔드포인트 | 주의사항 |
|
|
|--------|------|-----------|----------|
|
|
| Context7 | 문서 조회 | context7.com API | - |
|
|
| Domain Agent | 도메인 관리 | OpenAI Assistants | `asst_MzPFKoqt7V4w6bc0UwcXU4ob` |
|
|
| Namecheap API | 도메인 백엔드 | namecheap-api.anvil.it.com | 날짜: MM/DD/YYYY → ISO 변환 |
|
|
| WHOIS API | WHOIS 조회 | whois-api-eight.vercel.app | ccSLD 미지원 |
|
|
| wttr.in | 날씨 | wttr.in | - |
|
|
| DuckDuckGo | 검색 | api.duckduckgo.com | - |
|
|
| Vault | API 키 관리 | vault.anvil.it.com | - |
|
|
|
|
---
|
|
|
|
## Deposit System
|
|
|
|
**자동 매칭 흐름:**
|
|
```
|
|
[시나리오 1: 사용자 먼저]
|
|
"홍길동 50000원 입금" → bank_notifications 검색
|
|
↓
|
|
매칭 O → confirmed | 매칭 X → pending
|
|
|
|
[시나리오 2: SMS 먼저]
|
|
은행 SMS → Email Worker → 파싱 → bank_notifications 저장
|
|
↓
|
|
deposit_transactions 검색 (pending)
|
|
↓
|
|
매칭 O → confirmed + 잔액↑ | 매칭 X → 저장만
|
|
```
|
|
|
|
**입금 계좌:** 하나은행 427-910018-27104 (주식회사 아이언클래드)
|
|
- Vault 경로: `secret/companies/ironclad-corp`
|
|
|
|
---
|
|
|
|
## Domain System
|
|
|
|
**도구 목록:**
|
|
| 도구 | 권한 | 설명 |
|
|
|------|------|------|
|
|
| `list_domains` | 소유자 | 도메인 목록 |
|
|
| `get_domain_info` | 소유자 | 상세 정보 (만료일 등) |
|
|
| `get_nameservers` | 공개 | 네임서버 조회 |
|
|
| `set_nameservers` | 소유자 | 네임서버 변경 |
|
|
| `get_price` | 공개 | TLD 가격 (원화) |
|
|
| `check_domains` | 공개 | 가용성 확인 |
|
|
| `whois_lookup` | 공개 | WHOIS 조회 |
|
|
|
|
**가격 정책:** Namecheap 원가 + 13%, 매일 환율 업데이트
|
|
|
|
**권한 체크:** `user_domains` 테이블 `verified=1`
|