diff --git a/packages/user/src/components/event/chatting/index.tsx b/packages/user/src/components/event/chatting/index.tsx index bdaa5e59..07421be6 100644 --- a/packages/user/src/components/event/chatting/index.tsx +++ b/packages/user/src/components/event/chatting/index.tsx @@ -2,7 +2,7 @@ import { ChatList, ChatProps } from '@softeer/common/components'; import { CHAT_SOCKET_ENDPOINTS } from '@softeer/common/constants'; import { IMessage } from '@stomp/stompjs'; import { useEffect, useState } from 'react'; -import socketClient from 'src/services/socket.ts'; +import socketClient from 'src/services/socket/index.ts'; import Chat from './Chat.tsx'; import ChatInputArea from './inputArea/index.tsx'; diff --git a/packages/user/src/services/socket.ts b/packages/user/src/services/socket/index.ts similarity index 73% rename from packages/user/src/services/socket.ts rename to packages/user/src/services/socket/index.ts index 2d05d344..640c684b 100644 --- a/packages/user/src/services/socket.ts +++ b/packages/user/src/services/socket/index.ts @@ -1,5 +1,5 @@ -import { Socket } from '@softeer/common/utils'; import { SOCKET_BASE_URL } from 'src/constants/environments.ts'; +import Socket from 'src/services/socket/socket.ts'; const socketClient = new Socket(SOCKET_BASE_URL); export default socketClient; diff --git a/packages/user/src/services/socket/socket.ts b/packages/user/src/services/socket/socket.ts new file mode 100644 index 00000000..2ff947ef --- /dev/null +++ b/packages/user/src/services/socket/socket.ts @@ -0,0 +1,93 @@ +import { Client, IFrame, IMessage, StompSubscription } from '@stomp/stompjs'; +import SockJS from 'sockjs-client'; + +/** + * 임시로 error 해결 전까지 common 에 존재하는 Socket class 대신 사용 + * Error: Dynamic require of "crypto" is not supported + * */ +export default class Socket { + private client: Client; + + private subscriptions: Map = new Map(); + + constructor(url: string, token?: string | null) { + this.client = this.setup({ url, token }); + } + + private setup({ url }: { url: string; token?: string | null }): Client { + const stompClient = new Client({ + webSocketFactory: () => new SockJS(`${url}/ws`), + reconnectDelay: 5000, // Reconnect if the connection drops + }); + this.client = stompClient; + return stompClient; + } + + connect(callback?: (isConnected: boolean, options?: IFrame) => void) { + // TODO: 채팅 메시지 리스트 받아오기 + // this.client.onConnect = (options) => callback?.(true, options); + this.client.onConnect = () => callback?.(true); + + this.client.onStompError = (error) => { + console.error('STOMP Error', error); + callback?.(false); + }; + + this.client.activate(); + } + + disconnect() { + this.subscriptions.forEach((subscription) => subscription.unsubscribe()); + this.subscriptions.clear(); + + if (this.client.connected) { + this.client.deactivate(); + } + } + + sendMessages({ destination, body }: { destination: string; body: unknown }) { + if (!this.client.connected) { + this.connect(() => { + this.client.publish({ destination, body: JSON.stringify(body) }); + }); + } else { + this.client.publish({ destination, body: JSON.stringify(body) }); + } + } + + private createSubscription({ + destination, + callback, + }: { + destination: string; + callback: (messageId: string, message: IMessage) => void; + }) { + const subscription = this.client.subscribe(destination, (message: IMessage) => { + const messageId = message.headers['message-id']; + callback(messageId, message); + }); + this.subscriptions.set(destination, subscription); + } + + subscribe({ + destination, + callback, + }: { + destination: string; + callback: (messageId: string, message: IMessage) => void; + }) { + if (this.client.connected) { + this.createSubscription({ destination, callback }); + } else { + this.connect(() => this.createSubscription({ destination, callback })); + } + } + + unsubscribe(destination: string) { + const subscription = this.subscriptions.get(destination); + if (subscription) { + subscription.unsubscribe(); + this.subscriptions.delete(destination); + } + } +}