Files
hanarang-dashboard/frontend/lib/useSocket.ts
narang-ai 2089283386 fix(sprint-006): Critical 보안 이슈 5건 수정
B1(Critical): JWT_SECRET 'changeme' fallback 3곳 전부 제거
- auth.service.ts / auth.module.ts / events.module.ts: 미설정 시 throw
- events.gateway.ts: 미설정 시 client.disconnect()

B2(Critical): User.role 기본값 'admin' → 'viewer'
- Prisma schema @default('viewer')
- RoleGuard 신규 생성 (Roles 데코레이터)
- AdminController / CostsController @Roles('admin') 적용

B3: WebSocket JWT 실패/미제공 시 client.disconnect()
- 토큰 없으면 즉시 disconnect
- 토큰 있지만 검증 실패해도 disconnect
- FE useSocket: access token을 auth.token으로 전달

B4: refresh endpoint throw Error → BadRequestException (400)

B5: register Throttle 추가 (3회/분, 초대코드 brute-force 방지)
2026-04-04 06:23:44 +00:00

58 lines
1.5 KiB
TypeScript

'use client';
import { useEffect, useRef, useState, useCallback } from 'react';
import { io, Socket } from 'socket.io-client';
interface UseSocketOptions {
onSistersUpdate?: (sisters: any[]) => void;
onActivityNew?: (item: any) => void;
}
export function useSocket(options: UseSocketOptions = {}) {
const socketRef = useRef<Socket | null>(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: { sisters: any[] }) => {
options.onSistersUpdate?.(data.sisters);
});
socket.on('activity:new', (data: { item: any }) => {
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 };
}