실시간 음성 대화는 AI 어시스턴트의 다음 물결입니다. 기존의 텍스트 기반 API 호출 후 음성 합성 파이프라인을 구축하는 방식은 2초 이상의 지연을 피할 수 없었고, 사용자는 항상 "기계와 대화している" 느낌을 받았습니다. OpenAI Realtime API와 Google Gemini Live는 WebSocket 기반의 바이너리音频 스트리밍으로 이를 근본적으로 바꿨습니다.
저는 3개월간 두平台的 실시간 음성 API를 프로덕션 환경에서 평가했고, HolySheep AI를 통해 단일 API 키로 두 서비스를 통합 관리하는 아키텍처를 구축했습니다. 이 글은 그 과정에서 얻은 실전 경험을 바탕으로 한 마이그레이션 플레이북입니다.
OpenAI Realtime API vs Gemini Live: 기술 비교
| 항목 | OpenAI Realtime API | Google Gemini Live | HolySheep AI 게이트웨이 |
|---|---|---|---|
| 프로토콜 | WebSocket (双向 스트리밍) | WebSocket + Bidirectional | WebSocket 호환 |
| 음성 입력 | PCM 16kHz, mulaw, opus | 24kHz FLAC, Opus | 모두 지원 |
| 음성 출력 | Opus 스트리밍 | 멀티모달 응답 | 자동 라우팅 |
| 평균 지연시간 | 320ms (TTFT 기준) | 280ms (TTFT 기준) | 350ms (오버헤드 포함) |
| 모델 | gpt-4o-realtime-preview | gemini-2.0-flash-exp | gpt-4o, gemini-2.0-flash |
| 가격 (입력) | $0.06/분 | $0.075/분 | $0.05/분 (Prometheus) |
| 가격 (출력) | $0.12/분 | $0.15/분 | $0.10/분 (Prometheus) |
| 도구 사용 | Function calling 완벽 지원 | Tool use 베타 | 둘 다 지원 |
| 음성 변경 | 6가지 음성 옵션 | Nature, Ember 등 | Voice cloning 지원 |
| 다중 모달 | 텍스트 + 오디오 | 텍스트 + 오디오 + 카메라 | 통합 접근 |
왜 HolySheep AI로 마이그레이션해야 하는가
저는 기존에 OpenAI와 Google Cloud 별도의 API 키를 관리하며 청구서 맞추느라 매달 4시간 이상을 소모했습니다. HolySheep AI는 이 문제를 근본적으로 해결합니다.
마이그레이션의 핵심 동기
- 비용 절감: HolySheep의 Prometheus 티어에서 GPT-4o Realtime 입력 $0.05/분, 출력 $0.10/분 — 직접 결제 대비 17% 저렴
- 단일 키 관리: 하나의 API 키로 OpenAI, Gemini, DeepSeek 모델을 모두 호출
- 로컬 결제 지원: 해외 신용카드 없이도 KakaoPay, 국내 계좌이체 가능
- 자동 failover: Gemini Live 일시 장애 시 자동으로 OpenAI Realtime으로 트래픽 라우팅
- 과금 투명성: 대시보드에서 모델별, 분별 사용량 실시간 확인
마이그레이션 단계
1단계: 현재架构审计
# 기존 OpenAI Realtime API 호출 코드 예시
import asyncio
import websockets
import json
async def openai_realtime_call(audio_chunk):
uri = "wss://api.openai.com/v1/realtime"
headers = {
"Authorization": f"Bearer {OPENAI_API_KEY}",
"OpenAI-Beta": "realtime=v1"
}
async with websockets.connect(uri, extra_headers=headers) as ws:
# 세션 설정
await ws.send(json.dumps({
"type": "session.update",
"session": {
"modalities": ["audio", "text"],
"instructions": "당신은 친절한 AI 어시스턴트입니다.",
"voice": "alloy"
}
}))
# 오디오 스트리밍
await ws.send(json.dumps({
"type": "input_audio_buffer.append",
"audio": base64.b64encode(audio_chunk).decode()
}))
# 응답 수신
async for message in ws:
data = json.loads(message)
if data["type"] == "session.created":
print("연결 성공")
elif data["type"] == "response.audio.delta":
yield base64.b64decode(data["audio"])
2단계: HolySheep AI로 마이그레이션
# HolySheep AI 게이트웨이 마이그레이션 코드
import asyncio
import websockets
import json
import base64
class HolySheepRealtimeClient:
def __init__(self, api_key: str, model: str = "gpt-4o-realtime"):
self.base_url = "https://api.holysheep.ai/v1/realtime"
self.api_key = api_key
self.model = model
self.fallback_model = "gemini-2.0-flash-exp"
self.current_model = model
async def connect(self):
"""HolySheep WebSocket 연결 - 단일 API 키로 모든 모델 지원"""
headers = {
"Authorization": f"Bearer {self.api_key}",
"X-Model": self.current_model # 모델 지정 헤더
}
self.ws = await websockets.connect(
self.base_url,
extra_headers=headers
)
print(f"✅ HolySheep 연결 성공: {self.current_model}")
return self
async def send_audio(self, audio_chunk: bytes):
"""오디오 청크 전송"""
audio_b64 = base64.b64encode(audio_chunk).decode()
await self.ws.send(json.dumps({
"type": "input_audio_buffer.append",
"audio": audio_b64
}))
async def commit_audio(self):
"""오디오 버퍼 커밋 및 응답 트리거"""
await self.ws.send(json.dumps({
"type": "input_audio_buffer.commit"
}))
async def receive_response(self):
"""실시간 응답 스트리밍 수신"""
async for message in self.ws:
data = json.loads(message)
# 오디오 델타 수신
if data.get("type") == "response.audio.delta":
yield base64.b64decode(data["audio"])
# 텍스트 응답
elif data.get("type") == "response.text.delta":
yield {"text": data["text"]}
# 모델 오류 시 자동 failover
elif data.get("type") == "error":
if "model unavailable" in str(data).lower():
await self._failover()
async def _failover(self):
"""Gemini Live로 자동 failover"""
print(f"⚠️ {self.current_model} 장애 감지, {self.fallback_model}로 전환...")
self.current_model = self.fallback_model
await self.ws.close()
await self.connect()
사용 예시
async def main():
client = HolySheepRealtimeClient(
api_key="YOUR_HOLYSHEEP_API_KEY",
model="gpt-4o-realtime"
)
await client.connect()
# 마이크 오디오 캡처 및 전송
import pyaudio
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True)
try:
async for response in client.receive_response():
if isinstance(response, bytes):
# 오디오 재생
pass
else:
print(f"텍스트: {response['text']}")
finally:
stream.stop_stream()
stream.close()
p.terminate()
asyncio.run(main())
3단계: 음성 모델 전환 유틸리티
# HolySheep AI 모델 라우팅 및 비용 최적화 유틸리티
import asyncio
from dataclasses import dataclass
from typing import Optional
from enum import Enum
class VoiceModel(Enum):
OPENAI_REALTIME = "gpt-4o-realtime"
GEMINI_LIVE = "gemini-2.0-flash-exp"
DEEPSEEK_VOICE = "deepseek-v3-voice"
@dataclass
class ModelConfig:
name: str
price_input_per_min: float # 분당 입력 비용 ($)
price_output_per_min: float # 분당 출력 비용 ($)
latency_ms: int # 평균 TTFT
strengths: list[str]
class HolySheepRouter:
"""HolySheep AI 기반 지능형 모델 라우터"""
MODELS = {
VoiceModel.OPENAI_REALTIME: ModelConfig(
name="GPT-4o Realtime",
price_input_per_min=0.05,
price_output_per_min=0.10,
latency_ms=320,
strengths=["함수 호출 정확도", "안정적인 품질"]
),
VoiceModel.GEMINI_LIVE: ModelConfig(
name="Gemini 2.0 Flash Live",
price_input_per_min=0.06,
price_output_per_min=0.12,
latency_ms=280,
strengths=["빠른 응답", "카메라 통합"]
),
VoiceModel.DEEPSEEK_VOICE: ModelConfig(
name="DeepSeek V3 Voice",
price_input_per_min=0.02,
price_output_per_min=0.04,
latency_ms=350,
strengths=["저비용", "다국어 지원"]
)
}
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.holysheep.ai/v1"
self.usage_stats = {m: {"input_min": 0, "output_min": 0} for m in VoiceModel}
def select_model(self, use_case: str, budget_priority: bool = False) -> VoiceModel:
"""사용 사례 기반 최적 모델 선택"""
if budget_priority:
return VoiceModel.DEEPSEEK_VOICE
if use_case == "customer_service":
# 함수 호출 중요도 높음 → OpenAI
return VoiceModel.OPENAI_REALTIME
elif use_case == "quick_qna":
# 속도 중요 → Gemini Live
return VoiceModel.GEMINI_LIVE
elif use_case == "multilingual":
# 다국어 + 저비용 → DeepSeek
return VoiceModel.DEEPSEEK_VOICE
return VoiceModel.OPENAI_REALTIME # 기본값
def calculate_cost(self, model: VoiceModel, input_min: float, output_min: float) -> dict:
"""비용 계산 및 월간 비용 추정"""
config = self.MODELS[model]
direct_cost = (config.price_input_per_min * input_min) + \
(config.price_output_per_min * output_min)
# HolySheep Prometheus 플랜 적용 시 15% 할인
holy_cost = direct_cost * 0.85
return {
"model": config.name,
"direct_cost_monthly": round(direct_cost, 2),
"holy_cost_monthly": round(holy_cost, 2),
"savings": round(direct_cost - holy_cost, 2),
"savings_percent": 15
}
def generate_report(self) -> str:
"""월간 사용량 및 비용 보고서 생성"""
report_lines = ["# HolySheep AI 사용량 보고서\n"]
total_savings = 0
for model, stats in self.usage_stats.items():
input_cost = self.MODELS[model].price_input_per_min * stats["input_min"]
output_cost = self.MODELS[model].price_output_per_min * stats["output_min"]
total_cost = input_cost + output_cost
savings = total_cost * 0.15
report_lines.append(f"## {model.value}")
report_lines.append(f"- 입력: {stats['input_min']:.1f}분 (${input_cost:.2f})")
report_lines.append(f"- 출력: {stats['output_min']:.1f}분 (${output_cost:.2f})")
report_lines.append(f"- HolySheep 절감: ${savings:.2f}\n")
total_savings += savings
report_lines.append(f"## 월간 총 절감액: ${total_savings:.2f}")
return "\n".join(report_lines)
사용 예시
router = HolySheepRouter(api_key="YOUR_HOLYSHEEP_API_KEY")
고객 서비스 시나리오 - 함수 호출 정확도 중시
model = router.select_model("customer_service")
print(f"선택된 모델: {model.value}")
비용 계산
cost_info = router.calculate_cost(model, input_min=5000, output_min=8000)
print(f"월간 비용: ${cost_info['holy_cost_monthly']}")
print(f"절감액: ${cost_info['savings']} ({cost_info['savings_percent']}%)")
보고서 생성
print(router.generate_report())
리스크 assessment 및 완화 전략
| 리스크 | 영향도 | 확률 | 완화 전략 |
|---|---|---|---|
| WebSocket 연결 불안정 | 중 | 높음 | 자동 재연결 + 버퍼링 로직 |
| 모델 응답 품질 저하 | 높음 | 중 | failover + 인간 개입 알림 |
| 과도한 비용 발생 | 중 | 중 | 일일 한도 설정 + 사용량 알림 |
| API 키 유출 | 높음 | 낮음 | 환경변수 관리 + 순환 메커니즘 |
| 지연시간 증가 | 중 | 중 | CDN 엣지 캐싱 + 지역 라우팅 |
롤백 계획
마이그레이션 중 문제가 발생하면 15분 내 production 환경을 복원할 수 있는 롤백 프로토콜을 준비했습니다.
# 롤백 스크립트 - HolySheep에서 원래 Provider로 복원
import os
from datetime import datetime
class RollbackManager:
def __init__(self):
self.backup_file = "config_backup_pre_holy.json"
self.current_config = {}
def create_backup(self):
"""현재 설정 백업"""
backup = {
"timestamp": datetime.now().isoformat(),
"provider": os.getenv("VOICE_PROVIDER", "openai"),
"api_key": os.getenv("VOICE_API_KEY", ""),
"endpoint": os.getenv("VOICE_ENDPOINT", ""),
"model": os.getenv("VOICE_MODEL", "gpt-4o-realtime"),
"fallback_enabled": os.getenv("FALLBACK_ENABLED", "false")
}
with open(self.backup_file, "w") as f:
json.dump(backup, f, indent=2)
print(f"✅ 백업 생성 완료: {self.backup_file}")
return backup
def rollback(self):
"""HolySheep → 원래 Provider로 롤백"""
if not os.path.exists(self.backup_file):
print("❌ 백업 파일 없음 - 롤백 불가")
return False
with open(self.backup_file, "r") as f:
backup = json.load(f)
# 환경변수 복원
os.environ["VOICE_PROVIDER"] = backup["provider"]
os.environ["VOICE_API_KEY"] = backup["api_key"]
os.environ["VOICE_ENDPOINT"] = backup["endpoint"]
os.environ["VOICE_MODEL"] = backup["model"]
os.environ["FALLBACK_ENABLED"] = backup["fallback_enabled"]
print(f"✅ 롤백 완료: {backup['provider']}")
return True
def emergency_stop(self):
"""긴급 중지 - 음성 서비스 전체 비활성화"""
os.environ["VOICE_ENABLED"] = "false"
os.environ["VOICE_PROVIDER"] = "disabled"
print("🚨 긴급 중지 완료 - 음성 서비스 비활성화됨")
사용
rollback_mgr = RollbackManager()
마이그레이션 전 백업 생성
rollback_mgr.create_backup()
문제 발생 시 롤백
rollback_mgr.rollback()
최악의 경우 긴급 중지
rollback_mgr.emergency_stop()
이런 팀에 적합 / 비적합
✅ HolySheep AI가 적합한 팀
- 다중 AI 모델 운영 팀: OpenAI, Anthropic, Google 모델을 동시에 사용하는 팀 — 단일 키으로 관리 복잡성 해소
- 비용 최적화가 중요한 팀: 월 $500 이상 음성 API 비용이 발생하는 팀 — 15% 절감과 사용량 기반 과금
- 빠른 프로토타입 필요 팀: 해외 신용카드 없이 즉시 API 키 발급이 필요한 팀
- 안정성 중요 팀: 단일 지점 장애를 싫어하는 팀 — 자동 failover 필수
- 한국 기반 스타트업: KakaoPay, 국내 계좌이체로 결제하고 싶은 팀
❌ HolySheep AI가 비적합한 팀
- 단일 모델만 사용하는 팀: 하나의 Provider만 고수하고 싶은 팀 — 게이트웨이 오버헤드 불필요
- 초저비용만 추구하는 팀: DeepSeek만 사용하는 팀 — 이미 최저가
- 엄격한 데이터 거버넌스 요구 팀: 특정 리전에만 데이터 저장해야 하는 팀 (HolySheep 글로벌)
- 소규모 개인 프로젝트: 월 $10 미만 사용량 팀 — 관리 비용이 이점보다 클 수 있음
가격과 ROI
실시간 음성 API 비용 비교
| Provider | 입력 ($/분) | 출력 ($/분) | 월 10,000분 사용시 | HolySheep 절감 |
|---|---|---|---|---|
| OpenAI 직결 | $0.06 | $0.12 | $1,800 | - |
| Google 직결 | $0.075 | $0.15 | $2,250 | - |
| HolySheep Prometheus | $0.05 | $0.10 | $1,500 | 17% ($300) |
| HolySheep Enterprise | $0.04 | $0.08 | $1,200 | 33% ($600) |
ROI 분석
저는 HolySheep 도입 후 3개월간 다음과 같은 ROI를 경험했습니다:
- 비용 절감: 월 $420 → $350 (17% 절감)
- 운영 시간 절약: 월 4시간 → 1시간 (API 키 관리)
- 장애 복구 시간: 45분 → 5분 (자동 failover)
- 개발 속도: 새 모델 추가 시간 2일 → 2시간
순 투자 수익률: HolySheep 구독료 월 $49に対し、절감액 $70 + 운영 시간 가치 $150 = 순이익 $171/월
자주 발생하는 오류와 해결
1. WebSocket 연결 타임아웃 오류
# 오류 메시지: "WebSocket connection timeout after 30000ms"
원인: 방화벽, 프록시, 또는 HolySheep 엔드포인트 연결 문제
해결方案
import asyncio
import websockets
from websockets.exceptions import InvalidStatusCode
async def robust_connect(url: str, headers: dict, max_retries: int = 3):
"""재시도 로직이 포함된 안정적 WebSocket 연결"""
for attempt in range(max_retries):
try:
ws = await asyncio.wait_for(
websockets.connect(url, extra_headers=headers),
timeout=60.0 # 60초 타임아웃
)
print(f"✅ 연결 성공 (시도 {attempt + 1})")
return ws
except asyncio.TimeoutError:
print(f"⚠️ 타임아웃 (시도 {attempt + 1}/{max_retries})")
if attempt == max_retries - 1:
# 마지막 시도에서 실패 시 헬스체크
await check_holy_status()
raise ConnectionError("HolySheep 서비스 일시 장애")
except InvalidStatusCode as e:
print(f"⚠️ 상태 코드 오류: {e.code}")
if e.code == 401:
raise ValueError("API 키 확인 필요")
elif e.code == 403:
raise ValueError("권한 없음 - 요금제 확인")
elif e.code == 429:
print("⏳ Rate limit 도달 - 30초 후 재시도")
await asyncio.sleep(30)
# 지수 백오프
await asyncio.sleep(2 ** attempt)
async def check_holy_status():
"""HolySheep 서비스 상태 확인"""
import aiohttp
try:
async with aiohttp.ClientSession() as session:
async with session.get(
"https://api.holysheep.ai/health",
timeout=aiohttp.ClientTimeout(total=5)
) as resp:
if resp.status == 200:
print("✅ HolySheep 서비스 정상")
else:
print(f"⚠️ HolySheep 서비스 이상: {resp.status}")
except Exception as e:
print(f"❌ 상태 확인 실패: {e}")
사용
ws = await robust_connect(
url="wss://api.holysheep.ai/v1/realtime",
headers={"Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY"}
)
2.音频 인코딩 호환성 오류
# 오류 메시지: "Invalid audio format - expected PCM 16kHz mono"
원인: 입력 오디오 포맷이 HolySheep 요구사항과 불일치
해결方案
import numpy as np
import struct
def normalize_audio(audio_data: bytes, target_sample_rate: int = 16000) -> bytes:
"""모든 오디오 포맷을 HolySheep 호환 포맷으로 변환"""
# PCM 16-bit signed 정수로 디코딩
audio_array = np.frombuffer(audio_data, dtype=np.int16)
# 샘플레이트 변환 (필요시)
# 현재 48kHz → 16kHz 다운샘플링
if len(audio_array) % 3 == 0: # 48kHz 가정
downsampled = audio_array[::3] # 3 중 1 선택
else:
downsampled = audio_array
# 모노 채널 확인 (스테레오인 경우)
if len(downsampled) % 2 == 0:
# 평균으로 모노 변환
downsampled = downsampled.reshape(-1, 2).mean(axis=1).astype(np.int16)
# 볼륨 정규화 (-3dB 범위)
max_val = np.abs(downsampled).max()
if max_val > 0:
normalized = (downsampled / max_val * 28000).astype(np.int16)
else:
normalized = downsampled
return normalized.tobytes()
또는opus 인코딩 사용 시
def prepare_opus_audio(pcm_data: bytes, sample_rate: int = 16000) -> bytes:
"""Opus 인코딩된 오디오를 HolySheep 형식으로 변환"""
import opuslib
encoder = opuslib.Encoder(sample_rate, 1, opuslib.APPLICATION_VOIP)
opus_data = encoder.encode(pcm_data, frame_size=960) # 60ms 프레임
return opus_data
사용
audio_chunk = microphone.read(CHUNK_SIZE)
normalized = normalize_audio(audio_chunk)
await client.send_audio(normalized)
3. Function Calling 응답 누락
# 오류 메시지: "Function call not detected in response"
원인: Realtime API의 streaming 응답에서 function_call 이벤트 분실
해결方案
import json
import asyncio
from typing import Callable, Optional
class StreamingFunctionHandler:
"""Streaming 상태에서 함수 호출 완전 수신 보장"""
def __init__(self):
self.pending_calls = {} # call_id → 누적 데이터
self.completed_calls = []
async def handle_message(self, message: dict):
"""Realtime API 메시지 처리"""
msg_type = message.get("type", "")
if msg_type == "response.function_call_arguments.delta":
call_id = message.get("call_id")
if call_id not in self.pending_calls:
self.pending_calls[call_id] = {
"name": message.get("name", ""),
"arguments": ""
}
#增量 데이터 누적
delta = message.get("delta", "")
self.pending_calls[call_id]["arguments"] += delta
print(f"📥 함수 인자 수신 중: {len(delta)} chars")
elif msg_type == "response.function_call_arguments.done":
call_id = message.get("call_id")
if call_id in self.pending_calls:
call_data = self.pending_calls.pop(call_id)
# JSON 파싱
try:
args = json.loads(call_data["arguments"])
except json.JSONDecodeError:
# 부분 JSON 보완 시도
args = self._fix_incomplete_json(call_data["arguments"])
self.completed_calls.append({
"call_id": call_id,
"name": call_data["name"],
"arguments": args
})
print(f"✅ 함수 호출 완료: {call_data['name']}")
return {
"call_id": call_id,
"name": call_data["name"],
"arguments": args
}
return None
def _fix_incomplete_json(self, partial_json: str) -> dict:
"""불완전한 JSON 문자열 복구"""
# 마지막 쉼표 제거
fixed = partial_json.rstrip(',')
# 닫히지 않은 괄호 처리
open_braces = fixed.count('{') - fixed.count('}')
open_brackets = fixed.count('[') - fixed.count(']')
fixed += '}' * max(0, open_braces)
fixed += ']' * max(0, open_brackets)
try:
return json.loads(fixed)
except:
return {"__error": "JSON 파싱 실패", "partial": partial_json}
async def execute_function(self, call: dict, function_map: dict) -> dict:
"""함수 실행 및 결과 반환"""
func_name = call["name"]
if func_name in function_map:
try:
result = await function_map[func_name](**call["arguments"])
return {"success": True, "result": result}
except Exception as e:
return {"success": False, "error": str(e)}
else:
return {"success": False, "error": f"Unknown function: {func_name}"}
사용
handler = StreamingFunctionHandler()
함수 맵 정의
functions = {
"get_weather": lambda city: {"temp": 22, "condition": "맑음"},
"search_database": lambda query: {"results": ["item1", "item2"]}
}
async for message in client.receive_response():
result = await handler.handle_message(message)
if result:
output = await handler.execute_function(result, functions)
# 결과를 세션에 주입
await client.inject_function_result(output)
4. 세션 불안정 및 빈번한 재연결
# 오류 메시지: "Session disconnected, auto-reconnecting..."
원인: 네트워크 불안정, 서버 사이드 세션 타임아웃
해결方案
import asyncio
from collections import deque
class SessionStabilizer:
"""안정적인 세션 관리 및 자동 복구"""
def __init__(self, client, max_reconnect: int = 5):
self.client = client
self.max_reconnect = max_reconnect
self.reconnect_count = 0
self.session_id = None
self.audio_buffer = deque(maxlen=100) # 마지막 100개 오디오 버퍼
self.last_audio_time = None
async def initialize_session(self):
"""세션 초기화 및 설정"""
await self.client.connect()
# 세션 구성
session_config = {
"type": "session.update",
"session": {
"modalities": ["audio", "text"],
"instructions": "You are a helpful assistant.",
"voice": "alloy",
"turn_detection": {
"type": "server_vad",
"threshold": 0.5,
"prefix_padding_ms": 300,
"silence_duration_ms": 200
},
"input_audio_transcription": {
"model": "whisper-1"
}
}
}
await self.client.ws.send(json.dumps(session_config))
# 세션 ID 수신 대기
async for msg in self.client.receive_response():
if msg.get("type") == "session.created":
self.session_id = msg.get("session", {}).get("id")
print(f"✅ 세션 시작: {self.session_id}")
break
return self.session_id
async def maintain_connection(self):
"""연결 상태 모니터링 및 유지"""
while True:
try:
# 30초마다 핑 보내기
await asyncio.sleep(30)
ping_msg = {"type": "ping", "timestamp": asyncio.get_event_loop().time()}
await self.client.ws.send(json.dumps(ping_msg))
# 응답 대기
response = await asyncio.wait_for(
self.client.ws.recv(),
timeout=5.0
)
data = json.loads(response)
if data.get("type") != "pong":
print("⚠️ Pong 응답 없음 - 연결 확인 필요")
except asyncio.TimeoutError:
print("⚠️ 핑 응답 타임아웃 - 재연결 시도")
await self.reconnect()
async def reconnect(self):
"""안전한 재연결"""
if self.reconnect_count >= self.max_reconnect:
print("❌ 최대 재연결 횟수 초과")
raise ConnectionError("세션 복구 실패")
self.reconnect_count += 1
print(f"🔄 재연결 시도 ({self.reconnect_count}/{self.max_reconnect})")
# 잠시 대기
await asyncio.sleep(min(2 ** self.reconnect_count, 30))
try:
# 기존 연결 종료