diff --git a/packages/common/src/components/chat/Message.tsx b/packages/common/src/components/chat/Message.tsx index 07d776d8..bb7ffe34 100644 --- a/packages/common/src/components/chat/Message.tsx +++ b/packages/common/src/components/chat/Message.tsx @@ -29,7 +29,11 @@ export default function Message({

익명 {sender}

{isMyMessage &&

(나)

} -

{children}

+

+ {children} +

); } diff --git a/packages/common/src/constants/socket.ts b/packages/common/src/constants/socket.ts index c7626bdd..fbbbacec 100644 --- a/packages/common/src/constants/socket.ts +++ b/packages/common/src/constants/socket.ts @@ -5,6 +5,8 @@ export const CHAT_SOCKET_ENDPOINTS = { PUBLISH: '/app/chat.sendMessage', BLOCK: '/topic/block', NOTICE: '/app/chat.sendNotice', + SUBSCRIBE_CHAT_LIST: '/user/queue/chatHistory', + PUBLISH_CHAT_LIST: '/app/chat.getHistory', } as const; export const RACING_SOCKET_ENDPOINTS = { diff --git a/packages/common/src/utils/socket.ts b/packages/common/src/utils/socket.ts index b9f9ed9b..907edc01 100644 --- a/packages/common/src/utils/socket.ts +++ b/packages/common/src/utils/socket.ts @@ -1,7 +1,7 @@ import { Client, IFrame, IMessage, StompSubscription } from '@stomp/stompjs'; import SockJS from 'sockjs-client'; -export type SocketSubscribeCallbackType = (data: unknown, messageId: string) => void; +export type SocketSubscribeCallbackType = (data: unknown) => void; export interface SubscriptionProps { destination: string; @@ -88,11 +88,7 @@ export default class Socket { const subscriptionProps = { destination, headers, - callback: (message: IMessage) => { - const messageId = message.headers['message-id']; - const data = JSON.parse(message.body); - callback(data, messageId); - }, + callback: (message: IMessage) => callback(JSON.parse(message.body)), }; const subscription = this.client.subscribe( diff --git a/packages/user/src/components/event/chatting/index.tsx b/packages/user/src/components/event/chatting/index.tsx index 26ed685d..acf0a4e4 100644 --- a/packages/user/src/components/event/chatting/index.tsx +++ b/packages/user/src/components/event/chatting/index.tsx @@ -1,8 +1,8 @@ import { ChatList } from '@softeer/common/components'; -import ChatInput from 'src/components/event/chatting/inputArea/input/index.tsx'; import { UseSocketReturnType } from 'src/hooks/socket/index.ts'; import Chat from './Chat.tsx'; import ChatInputArea from './inputArea/index.tsx'; +import ChatInput from './inputArea/input/index.tsx'; /** 실시간 기대평 섹션 */ diff --git a/packages/user/src/components/event/racing/index.tsx b/packages/user/src/components/event/racing/index.tsx index 51ad2e69..df4723db 100644 --- a/packages/user/src/components/event/racing/index.tsx +++ b/packages/user/src/components/event/racing/index.tsx @@ -7,23 +7,26 @@ import RacingRankingDisplay from './controls/index.tsx'; import RacingDashboard from './dashboard/index.tsx'; /** 실시간 레이싱 섹션 */ -const RealTimeRacing = memo(({ racingSocket }: Pick) => { - const { ranks, votes, onCarFullyCharged } = racingSocket; - const { isCharged, handleCharge } = useChargeHandler(onCarFullyCharged); +const RealTimeRacing = memo( + ({ + racingSocket: { ranks, votes, onCarFullyCharged }, + }: Pick) => { + const { isCharged, handleCharge } = useChargeHandler(onCarFullyCharged); - return ( -
-
- - -
- -
- ); -}); + return ( +
+
+ + +
+ +
+ ); + }, +); export default RealTimeRacing; diff --git a/packages/user/src/hooks/socket/index.ts b/packages/user/src/hooks/socket/index.ts index 2981108e..c30b62a3 100644 --- a/packages/user/src/hooks/socket/index.ts +++ b/packages/user/src/hooks/socket/index.ts @@ -4,26 +4,33 @@ import socketManager from 'src/services/socket.ts'; import useChatSocket from './useChatSocket.ts'; import useRacingSocket from './useRacingSocket.ts'; +export type UseSocketReturnType = ReturnType; + export default function useSocket() { const { token } = useAuth(); const chatSocket = useChatSocket(); const racingSocket = useRacingSocket(); - const { onReceiveMessage, onReceiveBlock, ...chatSocketProps } = chatSocket; + const { onReceiveMessage, onReceiveChatList, onReceiveBlock, ...chatSocketProps } = chatSocket; const { onReceiveStatus, ...racingSocketProps } = racingSocket; const isSocketInitialized = useRef(false); useLayoutEffect(() => { - if (!isSocketInitialized.current) { - socketManager.connectSocketClient({ - token, - onReceiveMessage, - onReceiveStatus, - onReceiveBlock, - }); - isSocketInitialized.current = true; - } + const connetSocket = async () => { + if (!isSocketInitialized.current) { + await socketManager.connectSocketClient({ + token, + onReceiveChatList, + onReceiveMessage, + onReceiveStatus, + onReceiveBlock, + }); + isSocketInitialized.current = true; + } + }; + + connetSocket(); }, [token, onReceiveMessage, onReceiveStatus, onReceiveBlock]); return { chatSocket: chatSocketProps, racingSocket: racingSocketProps }; diff --git a/packages/user/src/hooks/socket/useChatSocket.ts b/packages/user/src/hooks/socket/useChatSocket.ts index e989e77e..26fc2cb1 100644 --- a/packages/user/src/hooks/socket/useChatSocket.ts +++ b/packages/user/src/hooks/socket/useChatSocket.ts @@ -14,21 +14,20 @@ export default function useChatSocket() { const [storedChatList, storeChatList] = useChatListStorage(); const [chatList, setChatList] = useState(storedChatList); + const [isChatListSubscribed, setIsChatListSubscribed] = useState(false); + useEffect(() => storeChatList(chatList), [chatList]); const handleIncomingMessage: SocketSubscribeCallbackType = useCallback( - (data: unknown, messageId: string) => { - const parsedData = data as Omit; - const parsedMessage = { id: messageId, ...parsedData }; - setChatList((prevMessages) => [...prevMessages, parsedMessage] as ChatProps[]); + (data: unknown) => { + setChatList((prevMessages) => [...prevMessages, data] as ChatProps[]); }, - [], + [setChatList], ); const handleIncomingBlock: SocketSubscribeCallbackType = useCallback( (data: unknown) => { const { id, blockId } = data as { id: string; blockId: string }; - setChatList((prevMessages) => prevMessages.map((message) => (message.id === blockId ? { id, type: 'b' } : message)), ); @@ -36,28 +35,65 @@ export default function useChatSocket() { [setChatList], ); - const handleSendMessage = useCallback((content: string) => { - try { - const socketClient = socketManager.getSocketClient(); + const socketClient = socketManager.getSocketClient(); + + const handleSendMessage = useCallback( + (content: string) => { + try { + const chatMessage = { content }; + + socketClient.sendMessages({ + destination: CHAT_SOCKET_ENDPOINTS.SUBSCRIBE, + body: chatMessage, + }); + } catch (error) { + const errorMessage = (error as Error).message; + toast({ + description: + errorMessage.length > 0 ? errorMessage : '기대평 전송 중 문제가 발생했습니다.', + }); + } + }, + [socketClient], + ); - const chatMessage = { content }; + const handleIncomingChatHistory: SocketSubscribeCallbackType = useCallback( + (data: unknown) => { + setChatList(data as ChatProps[]); + }, + [setChatList], + ); - socketClient.sendMessages({ - destination: CHAT_SOCKET_ENDPOINTS.PUBLISH, - body: chatMessage, + const handleRequestForSendingChatHistory = useCallback(async () => { + try { + await socketClient.sendMessages({ + destination: CHAT_SOCKET_ENDPOINTS.PUBLISH_CHAT_LIST, + body: {}, }); + setIsChatListSubscribed(true); } catch (error) { const errorMessage = (error as Error).message; toast({ description: - errorMessage.length > 0 ? errorMessage : '기대평을 보내는 중 문제가 발생했습니다.', + errorMessage.length > 0 ? errorMessage : '기대평 내역을 불러오는 중 문제가 발생했습니다.', }); } - }, []); + }, [setIsChatListSubscribed, socketClient]); + + const handleReceiveChatList: SocketSubscribeCallbackType = useCallback( + (data: unknown) => { + if (!isChatListSubscribed) { + handleRequestForSendingChatHistory(); + } + handleIncomingChatHistory(data); + }, + [isChatListSubscribed], + ); return { onReceiveMessage: handleIncomingMessage, onReceiveBlock: handleIncomingBlock, + onReceiveChatList: handleReceiveChatList, onSendMessage: handleSendMessage, messages: chatList, }; diff --git a/packages/user/src/services/socket.ts b/packages/user/src/services/socket.ts index 05bca8d6..9b4e7bea 100644 --- a/packages/user/src/services/socket.ts +++ b/packages/user/src/services/socket.ts @@ -14,6 +14,8 @@ class SocketManager { private onReceiveBlock: SocketSubscribeCallbackType | null = null; + private onReceiveChatList: SocketSubscribeCallbackType | null = null; + private onReceiveStatus: SocketSubscribeCallbackType | null = null; constructor(token: string | null) { @@ -33,11 +35,13 @@ class SocketManager { onReceiveMessage, onReceiveBlock, onReceiveStatus, + onReceiveChatList, }: { token: string | null | undefined; onReceiveMessage: SocketSubscribeCallbackType; onReceiveBlock: SocketSubscribeCallbackType; onReceiveStatus: SocketSubscribeCallbackType; + onReceiveChatList: SocketSubscribeCallbackType; }) { if (this.socketClient) { await this.socketClient.disconnect(); @@ -45,6 +49,7 @@ class SocketManager { this.initializeSocketClient(token); + this.onReceiveChatList = onReceiveChatList; this.onReceiveMessage = onReceiveMessage; this.onReceiveBlock = onReceiveBlock; this.onReceiveStatus = onReceiveStatus; @@ -63,11 +68,19 @@ class SocketManager { onReceiveBlock: this.onReceiveBlock!, onReceiveMessage: this.onReceiveMessage!, onReceiveStatus: this.onReceiveStatus!, + onReceiveChatList: this.onReceiveChatList!, }); } subscribeToTopics() { if (this.socketClient && this.socketClient.client.connected) { + if (this.onReceiveChatList) { + this.socketClient.subscribe({ + destination: CHAT_SOCKET_ENDPOINTS.SUBSCRIBE_CHAT_LIST, + callback: this.onReceiveChatList, + }); + } + if (this.onReceiveMessage) { this.socketClient.subscribe({ destination: CHAT_SOCKET_ENDPOINTS.SUBSCRIBE,