웹소켓 설정
백엔드
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").setAllowedOrigins("http://localhost:3000").withSockJS();
}
}
@MessageMapping("/chat.sendMessage")
public void sendMessage(final @DestinationVariable String roomId, final Message message) {
messagingTemplate.convertAndSend("/topic/messages", message);
}
프론트엔드
'use client'
import { useEffect, useState, useRef } from 'react';
import { Stomp } from '@stomp/stompjs';
import SockJS from 'sockjs-client';
export default function Chat({ roomId }) {
const [stompClient, setStompClient] = useState(null);
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
const messagesEndRef = useRef(null);
useEffect(() => {
const socket = new SockJS(`${process.env.NEXT_PUBLIC_BASE_URL}/chat`);
const client = Stomp.over(socket);
client.connect({}, (frame) => {
console.log('Connected: ' + frame);
client.subscribe('/topic/messages', (message) => {
console.log('Received: ' + message.body);
setMessages((prevMessages) => [...prevMessages, JSON.parse(message.body)]);
});
});
setStompClient(client);
}, []);
const sendMessage = () => {
if (stompClient && stompClient.connected) {
const chatMessage = {
content: message,
type: 'CHAT',
};
stompClient.send('/app/chat.sendMessage', {}, JSON.stringify(chatMessage));
setMessages((prevMessages) => [...prevMessages, chatMessage]);
setMessage('');
}
};
...
}
- 메시지 전송 과정
- 유저가 메시지를 입력하고 전송 버튼을 누르면,
sendMessage
함수가 호출되고 stompClient.send
메서드를 사용해서 메시지를 서버로 전송한다.
메시지는 JSON 형식으로 변환되고, /app/chat.sendMessage
경로로 전송된다.
이는 클라이언트가 서버의 @MessageMapping("/chat.sendMessage")
에 매핑된 메서드를 호출하고자 하는 것이다.
- 백엔드에서
ChatController
의 sendMessage
메서드가 호출되고, 전송받은 메시지(Message
객체)를 /topic/messages
로 다시 전송하기 위해 SimpMessagingTemplate
의 convertAndSend
메서드를 사용한다.
- 메시지 수신 과정
- 클라이언트는 초기화 할 때
/topic/messages
를 구독하도록 설정되어 있다.
이는 client.subscribe("/topic/messages", ...)
호출을 통해 이루어진다.
- 백엔드에서 메시지가
/topic/messages
로 전송될 때, 이 토픽을 구독하고 있는 모든 클라이언트에게 메시지가 전달된다.(메시지를 전송한 클라이언트도 포함됨)
- 클라이언트에서는
client.subscribe
에서 설정한 콜백 함수가 호출되고, message.body
를 통해 전송받은 메시지 내용을 확인할 수 있다.
이 메시지는 setMessages
를 사용해서 클라이언트의 메시지 목록에 추가된다.
채팅방 생성
return (
<div className="flex flex-col h-[80vh] max-w-2xl mx-auto border border-gray-300 mt-32">
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((msg, idx) => (
<div key={idx} className="break-words p-2 rounded-lg bg-blue-100 max-w-xs ml-auto">
{msg.content}
</div>
))}
<div ref={messagesEndRef} />
</div>
<div className="p-4 border-t border-gray-200 flex items-center">
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
className="w-full p-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="텍스트를 입력하세요..."
/>
<button
onClick={sendMessage}
className="ml-4 px-5 py-2 bg-blue-500 text-white rounded-lg float-right focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 hover:bg-blue-600"
>
Send
</button>
</div>
</div>
);