feat: add optimistic locking and improve type safety

- Implement optimistic locking for deposit balance updates
  - Prevent race conditions in concurrent deposit requests
  - Add automatic retry with exponential backoff (max 3 attempts)
  - Add version column to user_deposits table

- Improve type safety across codebase
  - Add explicit types for Namecheap API responses
  - Add typed function arguments (ManageDepositArgs, etc.)
  - Remove `any` types from deposit-agent and tool files

- Add reconciliation job for balance integrity verification
  - Compare user_deposits.balance vs SUM(confirmed transactions)
  - Alert admin on discrepancy detection

- Set up test environment with Vitest + Miniflare
  - Add 50+ test cases for deposit system
  - Add helper functions for test data creation

- Update documentation
  - Add migration guide for version columns
  - Document optimistic locking patterns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-19 23:23:09 +09:00
parent 8d0fe30722
commit f5df0c0ffe
21 changed files with 13448 additions and 169 deletions

137
CLAUDE.md
View File

@@ -176,6 +176,9 @@ npm run db:init # D1 스키마 초기화 (production) ⚠️ 주의
npm run db:init:local # D1 스키마 초기화 (local)
npm run tail # Workers 로그 스트리밍
npm run chat # CLI 테스트 클라이언트
npm test # 단위 테스트 실행 (Vitest)
npm run test:watch # Watch 모드
npm run test:coverage # 커버리지 리포트
```
**CLI 테스트 클라이언트:**
@@ -301,9 +304,60 @@ end(); // duration 자동 기록
## Testing
**현재 테스트 스크립트 없음** - 수동 테스트 필수
### 자동화된 단위 테스트 (Vitest)
### 로컬 테스트 절차
**프레임워크**: Vitest + Miniflare (Cloudflare Workers 환경 시뮬레이션)
**실행 명령어**:
```bash
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`):
```typescript
createTestUser(telegramId, username) // 테스트용 사용자 생성
createBankNotification(depositorName, amount) // 은행 알림 생성
createDepositTransaction(userId, amount, status) // 거래 생성
getTestDB() // DB 바인딩 가져오기
```
**추가 예정**:
- `openai-service.ts` - Function Calling 도구 테스트
- `summary-service.ts` - 프로필 시스템 테스트
- Integration Tests - 전체 워크플로우 테스트
---
### 수동 테스트 (Webhook)
**로컬 테스트 절차**:
```bash
# 1. 로컬 D1 초기화 (최초 1회)
npm run db:init:local
@@ -318,7 +372,7 @@ curl -X POST http://localhost:8787/webhook \
-d '{"message":{"chat":{"id":123},"text":"테스트"}}'
```
### 배포 후 확인
**배포 후 확인**:
```bash
# 로그 스트리밍
npm run tail
@@ -327,6 +381,10 @@ npm run tail
curl https://telegram-summary-bot.kappa-d8e.workers.dev/webhook-info
```
**수동 테스트 예제** (자동화 예정):
- `src/services/__test__/notification.test.ts` - 관리자 알림
- `src/utils/__test__/logger.test.ts` - 구조화된 로깅
---
## Troubleshooting
@@ -999,6 +1057,79 @@ crons = ["0 15 * * *"] # KST 00:00
**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 적용 |
| `migrations/002_add_version_columns.sql` | 스키마 마이그레이션 |
**적용 대상:**
- `request_deposit` (auto_matched case): 은행 알림 자동 매칭 시 잔액 증가
- `confirm_deposit`: 관리자 수동 확인 시 잔액 증가
**정합성 검증 (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) ✅ 정합성 보장
```
---
## Domain System