'use client'; import { useEffect, useRef, useState, useCallback } from 'react'; import { io, Socket } from 'socket.io-client'; interface UseSocketOptions { onSistersUpdate?: (sisters: Sister[]) => void; onActivityNew?: (item: Activity) => void; } interface SistersUpdatePayload { sisters: Sister[]; } interface ActivityNewPayload { item: Activity; } export function useSocket(options: UseSocketOptions = {}) { const socketRef = useRef(null); const [connected, setConnected] = useState(false); useEffect(() => { // next.config.ts rewrites가 없는 경우를 위해 빈 origin 처리 // 브라우저에서 WebSocket은 rewrites 대상이 아니므로 직접 BE URL 필요 const wsUrl = process.env.NEXT_PUBLIC_WS_URL ?? window.location.origin; const token = localStorage.getItem('hanarang_access_token') ?? ''; const socket = io(`${wsUrl}/ws`, { path: '/socket.io', transports: ['websocket', 'polling'], reconnectionAttempts: 5, reconnectionDelay: 3000, auth: { token }, }); socket.on('connect', () => { setConnected(true); }); socket.on('disconnect', () => { setConnected(false); }); socket.on('sisters:update', (data: SistersUpdatePayload) => { options.onSistersUpdate?.(data.sisters); }); socket.on('activity:new', (data: ActivityNewPayload) => { options.onActivityNew?.(data.item); }); socketRef.current = socket; return () => { socket.disconnect(); }; }, []); // eslint-disable-line react-hooks/exhaustive-deps const ping = useCallback(() => { socketRef.current?.emit('ping'); }, []); return { connected, ping }; }