<aside> 💡 이 글에서는 스프링을 이용해 저장된 채팅 내용을 불러오고 WebSocket으로 실시간 채팅을 구현한다.

</aside>

WebSocketConfig

@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-websocket").withSockJS();
    }
}

엔티티 클래스

@Data
@Entity
public class ChatMessage {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String content;
    private LocalDateTime timestamp;
}

컨트롤러

@Controller
public class ChatController {

    private final ChatMessageRepository chatMessageRepository;

    @Autowired
    public ChatController(ChatMessageRepository chatMessageRepository) {
        this.chatMessageRepository = chatMessageRepository;
    }

    @GetMapping("/chat")
    public String getChatPage(Model model) {
        List<ChatMessage> messages = chatMessageRepository.findAll();
        model.addAttribute("messages", messages);
        return "chat";
    }

    @MessageMapping("/chat.sendMessage")
    @SendTo("/topic/publicChatRoom")
    public ChatMessage sendMessage(ChatMessage chatMessage) {
        chatMessageRepository.save(chatMessage);
        return chatMessage;
    }

    @MessageMapping("/chat.addUser")
    @SendTo("/topic/publicChatRoom")
    public ChatMessage addUser(ChatMessage chatMessage) {
        chatMessageRepository.save(chatMessage);
        return chatMessage;
    }

    @MessageMapping("/chat.getChatHistory")
    @SendTo("/topic/publicChatRoom")
    public List<ChatMessage> getChatHistory() {
        // 이전 채팅 내용
        return chatMessageRepository.findAll();
    }
}

리포지토리

public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long> {}

chat.html (thymeleaf, SockJS, STOMP)

<!DOCTYPE html>
<html xmlns:th="<http://www.thymeleaf.org>">
<head>
    <title>채팅</title>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="/webjars/stomp-websocket/stomp.min.js"></script>
		<script>
				let stompClient = null;
				
				const connect = () => {
				    const socket = new SockJS('/chat-websocket');
				    stompClient = Stomp.over(socket);
				    stompClient.connect({}, (frame) => {
				        stompClient.subscribe('/topic/publicChatRoom', (chatMessage) => {
				            showMessage(JSON.parse(chatMessage.body));
				        });
				
				        // 채팅방에 입장할 때 이전 채팅 내용 가져오기
				        stompClient.send("/app/chat.getChatHistory", {}, '');
				    });
				};
				
				const disconnect = () => {
				    if (stompClient !== null) {
				        stompClient.disconnect();
				    }
				};
				
				const sendMessage = () => {
				    const content = document.getElementById('messageInput').value;
				    const chatMessage = {
				        content: content
				    };
				    stompClient.send("/app/chat.sendMessage", {}, chatMessage);
				    document.getElementById('messageInput').value = '';
				};
				
				const showMessage = (chatMessage) => {
				    const messageElement = document.createElement('li');
				    messageElement.innerHTML = chatMessage.content;
				    document.getElementById('chatMessages').appendChild(messageElement);
				};
				
				window.onload = connect;
				window.onunload = disconnect;
		</script>
</head>
<body onload="connect()" onunload="disconnect()">
    <h1>Chat</h1>
    
    <ul id="chatMessages">
        <li th:each="message : ${messages}" th:text="${message.content}"></li>
    </ul>

    <form>
        <input type="text" id="messageInput" required/>
        <button type="button" onclick="sendMessage()">전송</button>
    </form>
</body>
</html>