|
|
@@ -2,112 +2,141 @@
|
|
|
|
|
|
import { createContext, useContext, useEffect, useState, useRef } from 'react';
|
|
|
import * as signalR from '@microsoft/signalr';
|
|
|
-import useAuth from '@/hooks/useAuth';
|
|
|
+import { fetchLogout } from '@/lib/api/auth';
|
|
|
|
|
|
const SignalRContext = createContext<{
|
|
|
- connection: signalR.HubConnection | null;
|
|
|
- connected: boolean;
|
|
|
- stopConnection: () => void;
|
|
|
- reConnection: () => void;
|
|
|
+ cryptoConnection: signalR.HubConnection | null;
|
|
|
+ chatConnection: signalR.HubConnection | null;
|
|
|
+ cryptoConnected: boolean;
|
|
|
+ chatConnected: boolean;
|
|
|
+ stopConnections: () => Promise<void>;
|
|
|
+ reconnectChat: (accessToken?: string | null) => Promise<void>;
|
|
|
}>({
|
|
|
- connection: null,
|
|
|
- connected: false,
|
|
|
- stopConnection: () => {},
|
|
|
- reConnection: () => {}
|
|
|
+ cryptoConnection: null,
|
|
|
+ chatConnection: null,
|
|
|
+ cryptoConnected: false,
|
|
|
+ chatConnected: false,
|
|
|
+ stopConnections: async () => {},
|
|
|
+ reconnectChat: async () => {}
|
|
|
});
|
|
|
|
|
|
type Props = {
|
|
|
children: React.ReactNode;
|
|
|
accessToken: string|null;
|
|
|
- signalRUrl: string;
|
|
|
+ signalRCryptoUrl: string;
|
|
|
+ signalRChatUrl: string;
|
|
|
}
|
|
|
|
|
|
-export function SignalRProvider({ children, accessToken, signalRUrl }: Props) {
|
|
|
- const connection = useRef<signalR.HubConnection|null>(null);
|
|
|
- const [connected, setConnected] = useState<boolean>(false);
|
|
|
- const { logout } = useAuth();
|
|
|
+export function SignalRProvider({ children, accessToken, signalRCryptoUrl, signalRChatUrl }: Props) {
|
|
|
+ const cryptoConnectionRef = useRef<signalR.HubConnection|null>(null);
|
|
|
+ const chatConnectionRef = useRef<signalR.HubConnection|null>(null);
|
|
|
+ const [cryptoConnected, setCryptoConnected] = useState<boolean>(false);
|
|
|
+ const [chatConnected, setChatConnected] = useState<boolean>(false);
|
|
|
|
|
|
+ // 초기 렌더 시에만 전달됨. 토큰 갱신 시에는 reconnectChat()을 통해 수동으로 재연결 처리
|
|
|
useEffect(() => {
|
|
|
- initConnection();
|
|
|
+ initCryptoConnection();
|
|
|
+ initChatConnection(accessToken);
|
|
|
|
|
|
return () => {
|
|
|
- if (connection.current) {
|
|
|
- stopConnection();
|
|
|
- }
|
|
|
+ stopConnections();
|
|
|
};
|
|
|
}, []);
|
|
|
|
|
|
useEffect(() => {
|
|
|
- if (connected) {
|
|
|
- console.info('SignalR Connected');
|
|
|
- } else if(connection.current) {
|
|
|
- console.info('SignalR Disconnected');
|
|
|
- } else {
|
|
|
- console.info('SignalR Waiting...');
|
|
|
+ if (cryptoConnected) {
|
|
|
+ console.info('SignalR Crypto Connected');
|
|
|
+ }
|
|
|
+ }, [cryptoConnected]);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (chatConnected) {
|
|
|
+ console.info('SignalR Chat Connected');
|
|
|
}
|
|
|
- }, [connected]);
|
|
|
+ }, [chatConnected]);
|
|
|
|
|
|
- const initConnection = async () => {
|
|
|
+ const initCryptoConnection = async () => {
|
|
|
try {
|
|
|
- if (connection.current && connection.current.state !== signalR.HubConnectionState.Disconnected) {
|
|
|
+ if (cryptoConnectionRef.current && cryptoConnectionRef.current.state !== signalR.HubConnectionState.Disconnected) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- console.log('SignalR Connecting...');
|
|
|
+ const conn = new signalR.HubConnectionBuilder().withUrl(signalRCryptoUrl).withAutomaticReconnect().build();
|
|
|
+ await conn.start();
|
|
|
|
|
|
- const connectionOptions = accessToken ? { accessTokenFactory: async () => accessToken, withCredentials: true } : {};
|
|
|
- const conn = new signalR.HubConnectionBuilder().withUrl(signalRUrl, connectionOptions).build();
|
|
|
+ setCryptoConnected(true);
|
|
|
+ cryptoConnectionRef.current = conn;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('SignalR Crypto Connect Failed:', error);
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- if (conn.state === signalR.HubConnectionState.Disconnected) {
|
|
|
- console.warn('SignalR Connection is already disconnected');
|
|
|
- return;
|
|
|
+ const initChatConnection = async (accessToken?: string|null) => {
|
|
|
+ try {
|
|
|
+ if (chatConnectionRef.current && chatConnectionRef.current.state !== signalR.HubConnectionState.Disconnected) {
|
|
|
+ await chatConnectionRef.current.stop();
|
|
|
}
|
|
|
|
|
|
+ const connectionOptions = accessToken ? { accessTokenFactory: async () => accessToken, withCredentials: true } : {};
|
|
|
+ const conn = new signalR.HubConnectionBuilder().withUrl(signalRChatUrl, connectionOptions).withAutomaticReconnect().build();
|
|
|
await conn.start();
|
|
|
- setConnected(true);
|
|
|
+
|
|
|
+ setChatConnected(true);
|
|
|
|
|
|
conn.on('Connected', (message) => {
|
|
|
console.info(message);
|
|
|
});
|
|
|
+
|
|
|
conn.on('Logout', (message) => {
|
|
|
console.info(message);
|
|
|
});
|
|
|
+
|
|
|
conn.on('Kick', async () => {
|
|
|
- await logout();
|
|
|
+ await fetchLogout();
|
|
|
+ alert('관리자에 의해 강제 종료되었습니다.');
|
|
|
+ localStorage.setItem('rememberMe', 'false');
|
|
|
+ localStorage.removeItem('member');
|
|
|
+ location.replace('/');
|
|
|
});
|
|
|
|
|
|
- connection.current = conn;
|
|
|
+ chatConnectionRef.current = conn;
|
|
|
} catch (error) {
|
|
|
- console.error('SignalR Connect Failed:', error);
|
|
|
+ console.error('SignalR Chat Connect Failed:', error);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- const stopConnection = async () => {
|
|
|
- if (connection.current && connection.current.state === signalR.HubConnectionState.Connected) {
|
|
|
+ const stopConnections = async () => {
|
|
|
+ if (chatConnectionRef.current && chatConnectionRef.current.state === signalR.HubConnectionState.Connected) {
|
|
|
try {
|
|
|
- await connection.current.invoke('Logout');
|
|
|
- setConnected(false);
|
|
|
- } catch (error) {
|
|
|
- console.error('SignalR Disconnect Failed:', error);
|
|
|
- }
|
|
|
+ await chatConnectionRef.current.invoke('Logout');
|
|
|
+ setChatConnected(false);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('SignalR Chat Disconnect Failed:', error);
|
|
|
+ }
|
|
|
}
|
|
|
- };
|
|
|
-
|
|
|
- const reConnection = async () => {
|
|
|
- if (connection.current && connection.current.state === signalR.HubConnectionState.Connected) {
|
|
|
+ if (cryptoConnectionRef.current && cryptoConnectionRef.current.state === signalR.HubConnectionState.Connected) {
|
|
|
try {
|
|
|
- console.log('SignalR ReConnecting...');
|
|
|
- await connection.current.stop();
|
|
|
- await initConnection();
|
|
|
- console.log('SignalR ReConnected');
|
|
|
+ await cryptoConnectionRef.current.stop();
|
|
|
+ setCryptoConnected(false);
|
|
|
} catch (error) {
|
|
|
- console.error("SignalR ReConnection Failed:", error);
|
|
|
+ console.error('SignalR Crypto Disconnect Failed:', error);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ const reconnectChat = async (token?: string | null) => {
|
|
|
+ await initChatConnection(token);
|
|
|
+ };
|
|
|
+
|
|
|
return (
|
|
|
- <SignalRContext.Provider value={{ connection: connection.current, connected, stopConnection, reConnection }}>
|
|
|
+ <SignalRContext.Provider value={{
|
|
|
+ cryptoConnection: cryptoConnectionRef.current,
|
|
|
+ chatConnection: chatConnectionRef.current,
|
|
|
+ cryptoConnected,
|
|
|
+ chatConnected,
|
|
|
+ stopConnections,
|
|
|
+ reconnectChat
|
|
|
+ }}>
|
|
|
{children}
|
|
|
</SignalRContext.Provider>
|
|
|
)
|