8.2 KiB
8.2 KiB
Durable Object SQLite 조회 가이드
Durable Object의 내장 SQLite 데이터베이스를 조회하는 여러 가지 방법을 제공합니다.
📋 목차
1. 웹 인터페이스로 조회
가장 간편한 방법입니다. 브라우저에서 바로 SQLite 데이터를 확인할 수 있습니다.
접속 방법
http://localhost:8788/sql-viewer
기능
- 📊 DB 정보: 데이터베이스 크기, 테이블 목록, 레코드 수 등
- 👥 사용자 목록: 모든 사용자 조회
- 🎲 배팅 목록: 최근 배팅 내역 조회
- ⚙️ 커스텀 쿼리: 원하는 SQL 쿼리 직접 실행
예시 화면
┌─────────────────────────────────────┐
│ 🔍 Durable Object SQLite Viewer │
├─────────────────────────────────────┤
│ [📊 DB 정보] [👥 사용자] [🎲 배팅] │
│ │
│ 결과: │
│ { │
│ "databaseSize": 12345, │
│ "userCount": 10, │
│ "betCount": 50 │
│ } │
└─────────────────────────────────────┘
2. API 엔드포인트로 조회
프로그래밍 방식으로 데이터를 조회할 수 있습니다.
2.1 데이터베이스 정보 조회
# HTTP GET
curl http://localhost:8788/sql-api/info
응답 예시:
{
"databaseSize": 24576,
"userCount": 3,
"betCount": 15,
"currentGameId": "550e8400-e29b-41d4-a716-446655440000",
"tables": [
{ "name": "user" },
{ "name": "current_bet" }
]
}
2.2 모든 사용자 조회
curl http://localhost:8788/sql-api/users
응답 예시:
[
{
"id": "user-123",
"nickname": "홍길동",
"email": "hong@example.com",
"joinGameCount": 5,
"capital": 15000
}
]
2.3 배팅 내역 조회
# 모든 배팅 (최근 100개)
curl http://localhost:8788/sql-api/bets
# 특정 게임의 배팅
curl "http://localhost:8788/sql-api/bets?gameId=550e8400-e29b-41d4-a716-446655440000"
응답 예시:
[
{
"id": 1,
"gameId": "550e8400-e29b-41d4-a716-446655440000",
"diceNum": 12,
"userId": "user-123",
"betType": "odd",
"amount": 1000,
"isWin": 1,
"reward": 2000
}
]
2.4 커스텀 SQL 쿼리 실행
curl -X POST http://localhost:8788/sql-api/query \
-H "Content-Type: application/json" \
-d '{"query": "SELECT * FROM user WHERE capital > 10000"}'
3. WebSocket으로 조회
실시간 게임 중에 WebSocket 연결을 통해 SQL을 조회할 수 있습니다.
JavaScript 예시
// WebSocket 연결
const ws = new WebSocket('ws://localhost:8788/api/counter');
// DB 정보 조회
ws.send(JSON.stringify({
type: 'sqlQuery',
query: 'info'
}));
// 모든 사용자 조회
ws.send(JSON.stringify({
type: 'sqlQuery',
query: 'users'
}));
// 커스텀 쿼리
ws.send(JSON.stringify({
type: 'sqlQuery',
query: 'SELECT * FROM current_bet WHERE amount > 5000'
}));
// 결과 수신
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'sqlResult') {
console.log('SQL 결과:', data.data);
} else if (data.type === 'sqlError') {
console.error('SQL 에러:', data.error);
}
};
4. 사용 가능한 테이블
4.1 user 테이블
사용자 정보를 저장합니다.
| 컬럼 | 타입 | 설명 |
|---|---|---|
id |
TEXT | 사용자 고유 ID (PRIMARY KEY) |
nickname |
TEXT | 닉네임 |
email |
TEXT | 이메일 (UNIQUE) |
joinGameCount |
INTEGER | 참여한 게임 횟수 |
capital |
INTEGER | 현재 자본금 |
예제 쿼리:
-- 모든 사용자 조회
SELECT * FROM user;
-- 자본금이 10000 이상인 사용자
SELECT * FROM user WHERE capital >= 10000;
-- 가장 많이 참여한 사용자
SELECT * FROM user ORDER BY joinGameCount DESC LIMIT 10;
4.2 current_bet 테이블
배팅 정보를 저장합니다.
| 컬럼 | 타입 | 설명 |
|---|---|---|
id |
INTEGER | 배팅 고유 ID (PRIMARY KEY, AUTO INCREMENT) |
gameId |
TEXT | 게임 고유 ID |
diceNum |
INTEGER | 주사위 합계 |
userId |
TEXT | 사용자 ID (FOREIGN KEY) |
betType |
TEXT | 배팅 타입 (odd/even/big/small) |
amount |
INTEGER | 배팅 금액 |
isWin |
INTEGER | 승리 여부 (0=패배, 1=승리) |
reward |
INTEGER | 보상 금액 |
예제 쿼리:
-- 모든 배팅 조회
SELECT * FROM current_bet;
-- 특정 게임의 배팅
SELECT * FROM current_bet WHERE gameId = 'xxx';
-- 사용자별 총 배팅액
SELECT userId, SUM(amount) as totalBet
FROM current_bet
GROUP BY userId;
-- 승리한 배팅만 조회
SELECT * FROM current_bet WHERE isWin = 1;
-- 게임별 통계
SELECT
gameId,
COUNT(*) as betCount,
SUM(amount) as totalAmount,
SUM(CASE WHEN isWin = 1 THEN 1 ELSE 0 END) as winCount
FROM current_bet
GROUP BY gameId;
5. 고급 쿼리 예제
5.1 사용자별 승률 계산
SELECT
u.nickname,
COUNT(cb.id) as totalBets,
SUM(CASE WHEN cb.isWin = 1 THEN 1 ELSE 0 END) as wins,
CAST(SUM(CASE WHEN cb.isWin = 1 THEN 1 ELSE 0 END) AS FLOAT) / COUNT(cb.id) * 100 as winRate
FROM user u
LEFT JOIN current_bet cb ON u.id = cb.userId
GROUP BY u.id, u.nickname
HAVING COUNT(cb.id) > 0;
5.2 배팅 타입별 통계
SELECT
betType,
COUNT(*) as count,
SUM(amount) as totalAmount,
AVG(amount) as avgAmount,
SUM(CASE WHEN isWin = 1 THEN 1 ELSE 0 END) as winCount
FROM current_bet
GROUP BY betType;
5.3 최근 게임 분석
SELECT
gameId,
diceNum,
COUNT(*) as betCount,
SUM(amount) as totalBet,
SUM(reward) as totalReward
FROM current_bet
WHERE gameId = (SELECT gameId FROM current_bet ORDER BY id DESC LIMIT 1)
GROUP BY gameId, diceNum;
6. 주의사항
보안
- 프로덕션 환경:
/sql/query엔드포인트는 개발용입니다. 프로덕션에서는 제거하거나 인증을 추가하세요. - SQL Injection: 사용자 입력을 직접 쿼리에 넣지 마세요. 항상 파라미터 바인딩을 사용하세요.
성능
- 인덱스 활용:
gameId와userId에 인덱스가 설정되어 있습니다. - LIMIT 사용: 큰 테이블 조회 시 항상
LIMIT를 사용하세요.
비용
- Cloudflare Durable Objects의 SQLite 사용 시 요금 정책을 확인하세요.
- Row read/write에 따라 비용이 발생합니다.
7. 문제 해결
"COUNTER binding not found" 에러
wrangler.jsonc에 Durable Object 바인딩이 올바르게 설정되어 있는지 확인하세요.
빈 결과가 반환됨
- 아직 사용자나 배팅이 생성되지 않았을 수 있습니다.
- 게임에 접속하여 배팅을 해보세요.
WebSocket 연결 실패
- 개발 서버가 실행 중인지 확인하세요:
pnpm run dev
8. 실전 예제
게임 시작 시 사용자 생성
// counter-do.ts의 fetch 메서드에서
this.createOrUpdateUser(
session.id,
'Player1',
'player1@example.com',
10000
);
배팅 저장
// startNoMoreBetPeriod 메서드에서 주사위를 굴린 후
const diceSum = this.dice1! + this.dice2! + this.dice3!;
this.sessions.forEach((session) => {
if (session.oddBet > 0) {
this.saveBet({
gameId: this.gameId!,
diceNum: diceSum,
userId: session.id,
betType: 'odd',
amount: session.oddBet,
isWin: diceSum % 2 === 1 ? 1 : 0,
reward: diceSum % 2 === 1 ? session.oddBet * 2 : 0
});
}
});
완성! 🎉
이제 Durable Object의 SQLite 데이터베이스를 다양한 방법으로 조회할 수 있습니다.