Node로만 소켓 통신을 사용해오다가, Spring boot를 통해서 간단한 소켓 통신을 해보면 어떨까 생각 해봤다.
Spring Boot + SockJS + Stomp
SockJS : WebSocket과 유사한 객체를 생성해주는 라이브러리,
웹소켓을 형성하거나 웹소켓을 지원하지 않을 경우에는, Long 폴링 방식(확실하지 않음)을 통해서 소켓 통신을 구현.
Stomp : Simple (or Streaming) Text Orientated Messaging Protocol의 약자로써, 메시지로 소켓 통신을 구현하는 경우 좀 더 효율적으로 구성을 할 수 있게 해준다.
@Configuration
@EnableWebSocketMessageBroker
public class SocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/webSocket")
.setAllowedOrigins("http://localhost:3000")
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic","/queue");
registry.setApplicationDestinationPrefixes("/");
}
}
WebSocketMessageBrokerConfigurer를 implements 해서 소켓 통신에 대한 설정을 구현한다.
한가지 흥미로운 점은 아무래도 리액트와 구현을 하다보니 proxy서버를 구성하지 않는 이상, cross origin이 프론트 단에서 발생할 수 밖에 없다.
그래서 처음에는 WebMvcConfig 부분에서 setAllowedOrgins를 설정해주면 되겠지라고 생각했는데, 소켓은 registerStompEndpoints 메소드에서 설정해줘야 한다는 점
만약 Cross Origin 문제가 발생한다면 참고 바람.
@Getter
@Setter
public class Message {
private String username;
private String content;
private Date date;
public Message(String username, String content, Date date) {
this.username = username;
this.content = content;
this.date = date;
}
}
Lombok을 이용해서 데이터 송/수신을 받을 객체를 Model로 작성한다.
@RestController
public class ChatController {
@MessageMapping("/hello")
@SendTo("/topic/roomId")
public Message boradCast(Message message){
return message;
}
}
나머지는 전처리 해준대로 설정하고 프론트 단에서 다음과 같은 코드를 작성해준다.
sendTo를 통해서 구독한 client에게 리턴 값을 전송하고
hello를통해서 사용자로부터 소켓 메시지를 받을 수 있다.
export type message = {
username: string;
content: string;
};
let sockJS = new SockJS("http://localhost:8080/webSocket");
let stompClient : Stomp.Client = Stomp.over(sockJS);
stompClient.debug= () => {};
export const ChatContainer = ({}) => {
const [contents, setContents] = React.useState<message[]>([]);
const [username, setUsername] = React.useState('');
const [message, setMessage] = React.useState("");
useEffect(()=>{
stompClient.connect({},()=>{
stompClient.subscribe('/topic/roomId',(data)=>{
const newMessage : message = JSON.parse(data.body) as message;
addMessage(newMessage);
});
});
},[contents]);
const handleEnter = (username: string, content: string) => {
const newMessage: message = { username, content };
stompClient.send("/hello",{},JSON.stringify(newMessage));
setMessage("");
};
const addMessage = (message : message) =>{
setContents(prev=>[...prev, message]);
};
return (
<div className={"container"}>
<ChatPresenter
contents={contents}
handleEnter={handleEnter}
message={message}
setMessage={setMessage}
username={username}
setUsername={setUsername}
/>
</div>
);
};
stompClient객체를 초기화 해준뒤에, Controller에서 제공하는 경로와 request형식에 맞게 보낸다면,
통신이 가능한 간단한 채팅창을 만들 수 있게 된다.
프론트, 백엔드 소스는 여기서 확인할 수 있다.
https://github.com/ehdrms2034/SpringBootChatting
에서 간단한 소스를 볼 수 있다.