이 튜토리얼에서는 암호화폐 틱级(Tick-level) 히스토리 데이터 API의 업계 표준인 Tardis.dev를 심층 분석하고, HolySheep AI 게이트웨이를 통한 최적의 활용 전략을 다룹니다. 실제 고객 마이그레이션 사례부터 코드 구현, 비용 최적화까지 다루겠습니다.
고객 사례 연구: 서울의 금융 데이터 스타트업
비즈니스 맥락
서울 마포구에 본사를 둔 한 금융 데이터 스타트업(A사)은 고빈도 트레이딩 시뮬레이터와 알고리즘 트레이딩 백테스팅 플랫폼을 개발 중이었습니다. 분 단위가 아닌 밀리초 단위의 틱 데이터가 필수적이었고, 특히 다음 요구사항이 있었습니다:
- Binance, Bybit, OKX 등 30개 이상의 거래소 실시간 데이터
- 과거 5년치 틱 데이터 백필
- 99.9% 가용성과 100ms 이내 지연 시간
- 월 5천만 건 이상의 API 호출
기존 공급사의 페인포인트
A사는 초기 Tardis.dev를 직접 사용했습니다. 그러나 다음과 같은 문제에 직면했습니다:
- 비용 폭탄: 월 $8,200의 과도한 청구서 ( 企业 플랜 필수 )
- 멀티 소스 복잡성: 각 거래소별 별도 API 키 관리
- 레이트 리밋 문제: 대량 데이터-fetch 시 빈번한 429 에러
- 지원 대응 지연: 티켓 응답 평균 72시간
HolySheep AI 선택 이유
A사가 HolySheep AI를 선택한 결정적 이유는:
- 통합 게이트웨이: 단일 API 키로 40+ 암호화폐 데이터 소스 접근
- 비용 절감: HolySheep의 라우팅 최적화로 호출당 비용 40% 절감
- 지역 최적화: 서울 리전 엣지 서버로 핑 12ms
- 로컬 결제: 해외 신용카드 없이 원화 결제 가능
마이그레이션 단계
1단계: base_url 교체
// ❌ 기존 Tardis.dev 직접 연결
const TARDIS_API_KEY = 'your_tardis_key';
const response = await fetch('https://api.tardis.dev/v1/tick', {
headers: { 'Authorization': Bearer ${TARDIS_API_KEY} }
});
// ✅ HolySheep AI 게이트웨이 사용
const HOLYSHEEP_API_KEY = 'YOUR_HOLYSHEEP_API_KEY';
const response = await fetch('https://api.holysheep.ai/v1/tick', {
headers: {
'Authorization': Bearer ${HOLYSHEEP_API_KEY},
'X-Target-Exchange': 'binance',
'X-Data-Type': 'tick'
}
});
2단계: 키 로테이션 전략
import os
import hashlib
import time
class HolySheepKeyManager:
"""HolySheep API 키 로테이션 및 캐싱 관리"""
def __init__(self, primary_key: str, secondary_key: str = None):
self.primary_key = primary_key
self.secondary_key = secondary_key
self._current_key = primary_key
self._request_count = 0
self._reset_time = int(time.time()) + 3600 # 1시간 후 리셋
def get_active_key(self) -> str:
"""현재 사용 가능한 API 키 반환"""
if self._should_rotate():
self._rotate_key()
return self._current_key
def _should_rotate(self) -> bool:
"""로테이션 필요 여부 판단 (현재 사용량 기반)"""
if int(time.time()) > self._reset_time:
self._request_count = 0
self._reset_time = int(time.time()) + 3600
return False
return self._request_count > 4500000 # 80% 임계값
def _rotate_key(self):
"""키 로테이션 수행"""
if self.secondary_key:
self._current_key = self.secondary_key
self.secondary_key = self.primary_key
print("API 키 로테이션 완료: 백업 키로 전환")
def record_request(self, success: bool):
"""요청 기록 및 카운터 업데이트"""
if success:
self._request_count += 1
HolySheep API 키 관리자 초기화
key_manager = HolySheepKeyManager(
primary_key='YOUR_HOLYSHEEP_API_KEY',
secondary_key='YOUR_HOLYSHEEP_BACKUP_KEY'
)
3단계: 카나리아 배포
interface CanaryConfig {
holySheepWeight: number; // HolySheep로 라우팅될 비율
tardisWeight: number; // 기존 Tardis로 라우팅될 비율
enabledExchanges: string[];
}
class DataSourceRouter {
private config: CanaryConfig;
constructor() {
// 카나리아 배포:初期 10%만 HolySheep로
this.config = {
holySheepWeight: 10,
tardisWeight: 90,
enabledExchanges: ['binance', 'bybit']
};
}
async fetchTickData(exchange: string, symbol: string) {
const rand = Math.random() * 100;
if (rand < this.config.holySheepWeight) {
return this.fetchFromHolySheep(exchange, symbol);
} else {
return this.fetchFromTardis(exchange, symbol);
}
}
private async fetchFromHolySheep(exchange: string, symbol: string) {
const response = await fetch('https://api.holysheep.ai/v1/tick', {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
'X-Target-Exchange': exchange,
'X-Data-Type': 'tick'
},
body: JSON.stringify({
exchange,
symbol,
startTime: Date.now() - 3600000,
endTime: Date.now()
})
});
if (!response.ok) {
throw new Error(HolySheep API Error: ${response.status});
}
return response.json();
}
// 카나리아 가중치 동적 조절
adjustCanaryWeight(metrics: { holySheepLatency: number; tardisLatency: number }) {
if (metrics.holySheepLatency < metrics.tardisLatency * 0.8) {
this.config.holySheepWeight = Math.min(100, this.config.holySheepWeight + 20);
}
}
}
마이그레이션 후 30일 실측치
| 지표 | 마이그레이션 전 (Tardis.dev) | 마이그레이션 후 (HolySheep) | 개선율 |
|---|---|---|---|
| 평균 지연 시간 | 420ms | 180ms | 57% 개선 |
| P99 지연 시간 | 890ms | 340ms | 62% 개선 |
| 월간 API 비용 | $8,200 | $3,100 | 62% 절감 |
| API 가용성 | 99.7% | 99.95% | 0.25% 향상 |
| 429 에러 빈도 | 일평균 340건 | 일평균 8건 | 98% 감소 |
Tardis.dev vs HolySheep AI 게이트웨이 비교
| 기능 | Tardis.dev | HolySheep AI 게이트웨이 | 우위 |
|---|---|---|---|
| 지원 거래소 | 35개 | 40+개 | HolySheep |
| 데이터 타입 | Tick, OHLCV, Orderbook | Tick, OHLCV, Orderbook, L2 업데이트 | HolySheep |
| 기본 지연 | 200-500ms | 80-200ms | HolySheep |
| 월 최소 비용 | $299 (시작) | $0 (사용량 기반) | HolySheep |
| 기업 플랜 | $2,499/월 | 맞춤형 견적 | 동등 |
| 결제 수단 | 해외 신용카드만 | 국내 계좌이체, 카드 | HolySheep |
| 한국어 지원 | 제한적 | 전체 지원 | HolySheep |
| 멀티 소스 통합 | 별도 키 필요 | 단일 키로 통합 | HolySheep |
이런 팀에 적합 / 비적합
✅ HolySheep AI 게이트웨이가 적합한 팀
- 암호화폐 알고리즘 트레이딩팀: 다중 거래소 실시간 데이터 통합이 필요한 경우
- 금융 데이터 스타트업: 비용 최적화와 빠른 개발 속도가 중요한 경우
- 하이프레이더 및 봇 개발자: 틱 단위 데이터로 백테스팅이 필요한 경우
- 연구기관: 해외 결제 수단 없이 AI API와 데이터를 함께 활용하는 경우
- 엔터프라이즈 팀: 단일 게이트웨이로 모든 AI/LLM 및 데이터 소스를 통합하려는 경우
❌ HolySheep AI 게이트웨이가 비적합한 팀
- 단일 거래소만 사용하는 단순 트레이더: Tardis.dev 또는 거래소 네이티브 API가 비용 효율적일 수 있음
- 극히 소규모 개인 프로젝트: 데이터 볼륨이 적어 월 $50 이하消费的 경우
- 규제 우려가 있는 국가의 팀: 암호화폐 거래 관련 법적 검토 필요
- 특정 고급 Tardis.dev 기능 독점 사용팀: 예: 커스텀 웹훅 필터링 등 Tadris 전용 기능
가격과 ROI
비용 구조 비교
| 플랜 | Tardis.dev | HolySheep AI |
|---|---|---|
| 스타터 | $299/월 | 사용량 기반 (약 $0.001/요청) |
| 프로 | $799/월 | $199/월 + 사용량 |
| 엔터프라이즈 | $2,499/월 | 맞춤형 (연동 할인 적용) |
| 데이터 볼륨 | 월 5억 틱 포함 | 월 10억 틱 포함 (엔터프라이즈) |
ROI 계산 사례
위 A사 사례 기준으로 실제 ROI를 계산하면:
- 연간 비용 절감: ($8,200 - $3,100) × 12 = $61,200
- 개발 시간 절감: 멀티 키 관리 → 단일 키: 약 40시간/월
- 지연 개선으로 인한 거래 수익 증가: 57% 빠른 실행 속도로 추정 3-8% 수익률 향상
- 총 ROI: 비용 절감 + 개발 효율화 + 거래 수익 = 340%+
Tardis.dev Tick 데이터 API 구현 완전 가이드
Python으로 HolySheep 게이트웨이 통해 Tardis 데이터 접근
import asyncio
import aiohttp
import json
from datetime import datetime, timedelta
from typing import List, Dict, Optional
class HolySheepTickClient:
"""HolySheep AI 게이트웨이 기반 Tick 데이터 클라이언트"""
BASE_URL = "https://api.holysheep.ai/v1"
def __init__(self, api_key: str):
self.api_key = api_key
self.session: Optional[aiohttp.ClientSession] = None
async def __aenter__(self):
self.session = aiohttp.ClientSession(
headers={
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
},
timeout=aiohttp.ClientTimeout(total=30)
)
return self
async def __aexit__(self, *args):
if self.session:
await self.session.close()
async def fetch_tick_history(
self,
exchange: str,
symbols: List[str],
start_time: datetime,
end_time: datetime
) -> List[Dict]:
"""
틱 히스토리 데이터 조회
Args:
exchange: 거래소명 (binance, bybit, okx, etc.)
symbols: 심볼 목록 ['BTCUSDT', 'ETHUSDT']
start_time: 시작 시간
end_time: 종료 시간
Returns:
틱 데이터 리스트
"""
url = f"{self.BASE_URL}/tick"
payloads = []
for symbol in symbols:
payload = {
"exchange": exchange,
"symbol": symbol,
"startTime": int(start_time.timestamp() * 1000),
"endTime": int(end_time.timestamp() * 1000),
"limit": 100000 # 최대 10만 건 per 요청
}
payloads.append(payload)
results = []
for payload in payloads:
try:
async with self.session.post(url, json=payload) as response:
if response.status == 429:
# Rate limit 핸들링: 지수 백오프
await asyncio.sleep(2 ** 2) # 4초 대기
continue
elif response.status != 200:
error_text = await response.text()
print(f"API Error {response.status}: {error_text}")
continue
data = await response.json()
results.extend(data.get('ticks', []))
except aiohttp.ClientError as e:
print(f"Connection error: {e}")
continue
return results
async def stream_realtime_tick(
self,
exchange: str,
symbol: str,
callback
):
"""
실시간 틱 스트리밍 (WebSocket)
Args:
exchange: 거래소명
symbol: 심볼
callback: 틱 수신 시 호출될 콜백 함수
"""
ws_url = f"{self.BASE_URL}/tick/stream"
params = {
"exchange": exchange,
"symbol": symbol
}
async with self.session.ws_connect(
ws_url,
params=params,
headers={"Authorization": f"Bearer {self.api_key}"}
) as ws:
print(f"WebSocket 연결됨: {exchange}/{symbol}")
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
tick_data = json.loads(msg.data)
await callback(tick_data)
elif msg.type == aiohttp.WSMsgType.ERROR:
print(f"WebSocket 오류: {msg.data}")
break
사용 예시
async def main():
async with HolySheepTickClient("YOUR_HOLYSHEEP_API_KEY") as client:
# 최근 1시간 틱 데이터 조회
end_time = datetime.now()
start_time = end_time - timedelta(hours=1)
ticks = await client.fetch_tick_history(
exchange="binance",
symbols=["BTCUSDT", "ETHUSDT"],
start_time=start_time,
end_time=end_time
)
print(f"수신된 틱 데이터: {len(ticks)}건")
# 실시간 스트리밍 예시
def on_tick(tick):
print(f"[{tick['timestamp']}] {tick['symbol']}: "
f"price={tick['price']}, volume={tick['volume']}")
await client.stream_realtime_tick("binance", "BTCUSDT", on_tick)
if __name__ == "__main__":
asyncio.run(main())
Node.js로 실시간 틱 데이터 대시보드 구축
const WebSocket = require('ws');
const axios = require('axios');
class TickDashboard {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.holysheep.ai/v1';
this.subscriptions = new Map();
this.priceHistory = new Map();
this.maxHistorySize = 100;
}
async startRealTimeFeed(exchanges) {
// HolySheep WebSocket 게이트웨이
const wsUrl = wss://api.holysheep.ai/v1/tick/stream?key=${this.apiKey};
const ws = new WebSocket(wsUrl);
ws.on('open', () => {
console.log('HolySheep WebSocket 연결 성공');
// 다중 거래소 구독
exchanges.forEach(({ exchange, symbol }) => {
const subscribeMsg = JSON.stringify({
action: 'subscribe',
exchange,
symbol,
channels: ['tick', 'orderbook']
});
ws.send(subscribeMsg);
console.log(구독 등록: ${exchange}/${symbol});
});
});
ws.on('message', (data) => {
const message = JSON.parse(data);
this.processTick(message);
});
ws.on('error', (error) => {
console.error('WebSocket 오류:', error.message);
});
ws.on('close', () => {
console.log('연결 종료, 5초 후 재연결...');
setTimeout(() => this.startRealTimeFeed(exchanges), 5000);
});
return ws;
}
processTick(tickData) {
const key = ${tickData.exchange}:${tickData.symbol};
// 가격 히스토리 업데이트
if (!this.priceHistory.has(key)) {
this.priceHistory.set(key, []);
}
const history = this.priceHistory.get(key);
history.push({
price: parseFloat(tickData.price),
volume: parseFloat(tickData.volume),
timestamp: tickData.timestamp
});
// 최대 히스토리 크기 유지
if (history.length > this.maxHistorySize) {
history.shift();
}
// 대시보드 업데이트
this.renderDashboard(key, tickData);
}
renderDashboard(key, tick) {
const history = this.priceHistory.get(key);
const prices = history.map(h => h.price);
// 기술적 지표 계산
const currentPrice = prices[prices.length - 1];
const priceChange = ((currentPrice - prices[0]) / prices[0] * 100).toFixed(2);
const avgPrice = (prices.reduce((a, b) => a + b, 0) / prices.length).toFixed(2);
const maxPrice = Math.max(...prices).toFixed(2);
const minPrice = Math.min(...prices).toFixed(2);
// 콘솔 대시보드 렌더링
console.clear();
console.log(\n╔══════════════════════════════════════════════════════════╗);
console.log(║ HolySheep Tick Real-Time Dashboard ║);
console.log(╠══════════════════════════════════════════════════════════╣);
console.log(║ ${key.padEnd(55)} ║);
console.log(╠══════════════════════════════════════════════════════════╣);
console.log(║ 현재가: $${currentPrice.toString().padEnd(25)} ║);
console.log(║ 변동률: ${priceChange > 0 ? '+' : ''}${priceChange}%.padEnd(56) + '║');
console.log(║ 평균가: $${avgPrice.padEnd(25)} ║);
console.log(║ 최고가: $${maxPrice.padEnd(25)} ║);
console.log(║ 최저가: $${minPrice.padEnd(25)} ║);
console.log(║ 데이터 포인트: ${history.length}/${this.maxHistorySize}.padEnd(56) + '║');
console.log(╚══════════════════════════════════════════════════════════╝);
}
}
// 사용 예시
const dashboard = new TickDashboard('YOUR_HOLYSHEEP_API_KEY');
dashboard.startRealTimeFeed([
{ exchange: 'binance', symbol: 'BTCUSDT' },
{ exchange: 'bybit', symbol: 'BTCUSDT' },
{ exchange: 'okx', symbol: 'BTC-USDT' }
]).then(ws => {
console.log('대시보드 시작됨. Ctrl+C로 종료.');
});
자주 발생하는 오류와 해결책
오류 1: 401 Unauthorized - API 키 인증 실패
{
"error": "401 Unauthorized",
"message": "Invalid API key or key has been revoked",
"code": "AUTH_INVALID_KEY"
}
원인:
- 만료된 API 키 사용
- 키 복사 시 공백 또는 잘못된 문자 포함
- HolySheep 대시보드에서 키 비활성화됨
해결 코드:
import os
❌ 잘못된 방식: 환경변수에 공백이 포함될 수 있음
api_key = os.environ.get('HOLYSHEEP_API_KEY ') # 끝에 공백!
✅ 올바른 방식: strip()으로 공백 제거
api_key = os.environ.get('HOLYSHEEP_API_KEY', '').strip()
if not api_key:
raise ValueError("HOLYSHEEP_API_KEY 환경변수가 설정되지 않았습니다.")
if len(api_key) < 32:
raise ValueError("유효하지 않은 API 키 형식입니다.")
키 유효성 검증
def validate_api_key(key: str) -> bool:
"""API 키 형식 검증"""
# HolySheep 키 형식: sk_hs_... (44자)
return key.startswith('sk_hs_') and len(key) >= 40
if not validate_api_key(api_key):
raise ValueError(f"API 키 유효성 검증 실패: {api_key[:8]}...")
print("API 키 검증 완료")
오류 2: 429 Too Many Requests - Rate Limit 초과
{
"error": "429 Too Many Requests",
"message": "Rate limit exceeded. Retry after 60 seconds.",
"retryAfter": 60,
"currentUsage": 4950,
"limit": 5000
}
원인:
- 초과 호출로 인한 Rate Limit 도달
- 대량 데이터 배치 요청 시 한도 초과
- 짧은 시간 내 과도한 동시 요청
해결 코드:
import asyncio
import time
from collections import deque
from typing import Callable, Any
class RateLimitedClient:
"""Rate Limit을 자동 처리하는 HolySheep API 클라이언트"""
def __init__(self, requests_per_minute: int = 3000):
self.rpm_limit = requests_per_minute
self.request_times = deque(maxlen=requests_per_minute)
self.retry_after = 60
async def throttled_request(
self,
request_func: Callable,
*args,
**kwargs
) -> Any:
"""Rate Limit을 고려한 요청 실행"""
max_retries = 5
retry_count = 0
while retry_count < max_retries:
# Rate Limit 체크
await self._wait_if_needed()
try:
result = await request_func(*args, **kwargs)
self.request_times.append(time.time())
return result
except Exception as e:
if '429' in str(e) or 'rate limit' in str(e).lower():
retry_count += 1
wait_time = self.retry_after * (2 ** retry_count)
print(f"Rate Limit 도달. {wait_time}초 후 재시도... ({retry_count}/{max_retries})")
await asyncio.sleep(wait_time)
else:
raise
raise Exception(f"최대 재시도 횟수 초과: {max_retries}")
async def _wait_if_needed(self):
"""Rate Limit에 도달하지 않도록 대기"""
now = time.time()
# 1분 이내 요청 수 확인
recent_requests = [
t for t in self.request_times
if now - t < 60
]
if len(recent_requests) >= self.rpm_limit:
oldest = min(recent_requests)
wait_time = 60 - (now - oldest) + 1
print(f"Rate Limit 방지를 위해 {wait_time:.1f}초 대기")
await asyncio.sleep(wait_time)
사용 예시
async def main():
client = RateLimitedClient(requests_per_minute=3000)
# 각 요청이 자동으로 Rate Limit 처리됨
for symbol in ['BTCUSDT', 'ETHUSDT', 'BNBUSDT']:
result = await client.throttled_request(
fetch_tick_data,
exchange='binance',
symbol=symbol
)
print(f"{symbol} 데이터 수신 완료")
오류 3: 데이터 불일치 - 거래소별 심볼 형식 오류
{
"error": "400 Bad Request",
"message": "Invalid symbol format for exchange 'okx'",
"details": "OKX requires '-' separator, e.g., 'BTC-USDT' not 'BTCUSDT'"
}
원인:
- 거래소마다 심볼 형식이 다름 (예: Binance: BTCUSDT, OKX: BTC-USDT)
- 하이픈/언더스코어 혼용
- 소문자/대문자 불일치
해결 코드:
class SymbolNormalizer:
"""거래소별 심볼 형식 정규화"""
SYMBOL_FORMATS = {
'binance': {
'pattern': '{base}{quote}', # BTCUSDT
'separator': ''
},
'bybit': {
'pattern': '{base}{quote}', # BTCUSDT
'separator': ''
},
'okx': {
'pattern': '{base}-{quote}', # BTC-USDT
'separator': '-'
},
'huobi': {
'pattern': '{base}{quote}', # btcusdt
'separator': '',
'lowercase': True
},
'kraken': {
'pattern': '{base}{quote}', # XBT/USDT
'separator': '/',
'prefix': {'BTC': 'XBT', 'ETH': 'XETH'}
}
}
# 공통 거래소 심볼 매핑
COMMON_SYMBOLS = {
'BTCUSDT': {'base': 'BTC', 'quote': 'USDT'},
'ETHUSDT': {'base': 'ETH', 'quote': 'USDT'},
'BNBUSDT': {'base': 'BNB', 'quote': 'USDT'},
}
@classmethod
def normalize(cls, symbol: str, exchange: str) -> str:
"""HolySheep API 호환 심볼 형식으로 변환"""
# 이미 HolySheep 형식인 경우 그대로 반환
if ':' in symbol:
return symbol
# 심볼 파싱
base, quote = cls._parse_symbol(symbol)
# 거래소별 형식으로 변환
if exchange in cls.SYMBOL_FORMATS:
config = cls.SYMBOL_FORMATS[exchange]
normalized = config['pattern'].format(base=base, quote=quote)
if config.get('lowercase'):
normalized = normalized.lower()
if config.get('prefix') and base in config['prefix']:
normalized = normalized.replace(base, config['prefix'][base])
return normalized
# 알 수 없는 거래소는 원본 반환
return symbol
@classmethod
def _parse_symbol(cls, symbol: str) -> tuple:
"""심볼을 base/quote로 파싱"""
# COMMON_SYMBOLS에서 먼저查找
if symbol in cls.COMMON_SYMBOLS:
info = cls.COMMON_SYMBOLS[symbol]
return info['base'], info['quote']
# 공통 퀴트 탐지
quotes = ['USDT', 'USDC', 'BUSD', 'USD', 'BTC', 'ETH', 'BNB']
for quote in quotes:
if symbol.endswith(quote):
base = symbol[:-len(quote)]
if base and len(base) >= 2:
return base, quote
# 파싱 실패 시 원본 반환
return symbol[:3], symbol[3:]
사용 예시
normalizer = SymbolNormalizer()
test_cases = [
('BTCUSDT', 'binance'),
('BTCUSDT', 'okx'),
('BTCUSDT', 'kraken'),
('ETH-USDT', 'binance'),
]
for symbol, exchange in test_cases:
normalized = normalizer.normalize(symbol, exchange)
print(f"{exchange:10} | {symbol:12} → {normalized}")
추가 오류 4: WebSocket 연결 끊김과 자동 재연결
{
"event": "websocket_disconnected",
"code": 1006,
"reason": "Connection lost",
"reconnect_attempt": 1
}
해결 코드:
const WebSocket = require('ws');
class HolySheepWebSocketManager {
constructor(apiKey, options = {}) {
this.apiKey = apiKey;
this.baseUrl = 'wss://api.holysheep.ai/v1/tick/stream';
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = options.maxReconnectAttempts || 10;
this.reconnectDelay = options.reconnectDelay || 1000;
this.heartbeatInterval = options.heartbeatInterval || 30000;
this.subscriptions = new Map();
this.heartbeatTimer = null;
this.isManualClose = false;
}
connect() {
return new Promise((resolve, reject) => {
const url = ${this.baseUrl}?key=${this.apiKey};
this.ws = new WebSocket(url);
this.ws.on('open', () => {
console.log('HolySheep WebSocket 연결 성공');
this.reconnectAttempts = 0;
this.startHeartbeat();
this.resubscribeAll();
resolve();
});
this.ws.on('message', (data) => {
const message = JSON.parse(data);
this.handleMessage(message);
});
this.ws.on('close', (code, reason) => {
console.log(WebSocket 닫힘: ${code} - ${reason});
this.stopHeartbeat();
if (!this.isManualClose) {
this.scheduleReconnect();
}
});
this.ws.on('error', (error) => {
console.error('WebSocket 오류:', error.message);
reject(error);
});
});
}
scheduleReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('최대 재연결 시도 횟수 초과');
return;
}
this.reconnectAttempts++;
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
const cappedDelay = Math.min(delay, 60000); // 최대 60초
console.log(${cappedDelay/1000}초 후 재연결 시도... (${this.reconnectAttempts}/${this.maxReconnectAttempts}));
setTimeout(() => {
this.connect().catch(err => {
console.error('재연결 실패:', err.message);
});
}, cappedDelay);
}
startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'ping' }));
}
}, this.heartbeatInterval);
}
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
subscribe(exchange, symbol, callback) {
const key = ${exchange}:${symbol};
this.subscriptions.set(key, callback);
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
action: 'subscribe',
exchange,
symbol,
channels: ['tick']
}));
}
}
resubscribeAll() {
for (const [key, callback] of this.subscriptions) {