zioinfo-mail/workspace/guardia-messenger/hooks/useWebSocket.ts
DESKTOP-TKLFCPR\ython 777e027553 refactor(structure): move app -> workspace/guardia-messenger
Permission denied on git mv, used robocopy instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 23:53:57 +09:00

72 lines
2.0 KiB
TypeScript

import { useEffect, useRef, useState, useCallback } from 'react'
import * as SecureStore from 'expo-secure-store'
import { API_BASE } from '../constants/Config'
const WS_BASE = API_BASE.replace('https://', 'wss://').replace('http://', 'ws://')
export interface WSEvent {
event_type: string
data: Record<string, unknown>
timestamp: string
}
export function useWebSocket(channels: string[] = []) {
const [connected, setConnected] = useState(false)
const [lastEvent, setLastEvent] = useState<WSEvent | null>(null)
const [events, setEvents] = useState<WSEvent[]>([])
const ws = useRef<WebSocket | null>(null)
const retry = useRef<ReturnType<typeof setTimeout> | null>(null)
const connect = useCallback(async () => {
const token = await SecureStore.getItemAsync('grd_token')
if (!token) return
const url = `${WS_BASE}/ws/events?token=${token}`
try {
ws.current = new WebSocket(url)
ws.current.onopen = () => {
setConnected(true)
// 채널 구독 메시지 전송
if (channels.length > 0 && ws.current) {
ws.current.send(JSON.stringify({ subscribe: channels }))
}
}
ws.current.onmessage = (e) => {
try {
const evt: WSEvent = JSON.parse(e.data)
setLastEvent(evt)
setEvents(prev => [evt, ...prev].slice(0, 50)) // 최근 50개 유지
} catch {}
}
ws.current.onclose = () => {
setConnected(false)
// 5초 후 재연결
retry.current = setTimeout(connect, 5000)
}
ws.current.onerror = () => {
ws.current?.close()
}
} catch {}
}, [])
useEffect(() => {
connect()
return () => {
if (retry.current) clearTimeout(retry.current)
ws.current?.close()
}
}, [connect])
const send = useCallback((data: object) => {
if (ws.current?.readyState === WebSocket.OPEN) {
ws.current.send(JSON.stringify(data))
}
}, [])
return { connected, lastEvent, events, send }
}