학교 전공 동아리 프로젝트를 진행하면서 Web Socket에 대해 공부해야했다.
실시간 통신을 통한 코딩 배틀 서비스를 기획하고 있었으니 당연했다.
백엔드 친구의 권유로 WebSocket과 STOMP를 함께 사용하게 되어 백엔드 친구에게 STOMP 개념 강의를 들었다.
블로그도 찾아보며 이해한 내용을 바탕으로 개념을 정리해보자 한다!
⭐️ 서버와 클라이언트 간의 지속적인 연결을 제공하는 프로토콜이다.
위 사진과 같이 WebSocket은 초기 연결을 위한 하나의 연결만 사용한다.
그 후에는 모든 에플리케이션 메세지는 같은 tcp connection을 가지고 주고 받게 된다.
위 사진과 같은 요청을 받은 WebSocket을 지원하는 서버는 200 상태코드 대신 101 상태코드를 반환한다.
프로토콜이 http/https에서 ws/wss로 변경된다.
클라이언트 혹은 서버가 연결 종료를 위한 Close Frame을 전송하고 요청을 받은 쪽에서 응답으로 Close Frame을 전송해 WebSocket 연결을 종료한다.
💡 Frame 기반의 서브 프로토콜 (sub-protocol)이다.
메세지의 발행자와 구독자가 존재하고 메세지를 보내는 사람과 받는 사람이 구분되어 있다.
import { useEffect, useState, useRef } from "react";
import { Client, IMessage } from "@stomp/stompjs";
import SockJS from "sockjs-client";
const WebSocketComponent = () => {
const [message, setMessage] = useState<string>(""); // 서버에서 수신한 메시지
const [inputMessage, setInputMessage] = useState<string>(""); // 사용자 입력 메시지
const clientRef = useRef<Client | null>(null); // STOMP 클라이언트
const [isConnected, setIsConnected] = useState<boolean>(false);
// WebSocket 연결 설정
const connectWebSocket = () => {
const socket = new SockJS("http://localhost:8080/ws"); // WebSocket 서버 주소
const stompClient = new Client({
webSocketFactory: () => socket,
debug: (str) => {
console.log(str); // 디버그 로그 출력
},
onConnect: (frame) => {
console.log("Connected to WebSocket:", frame);
setIsConnected(true);
// 메시지 구독
stompClient.subscribe("/topic/messages", (msg: IMessage) => {
const body = msg.body;
console.log("Received message:", body);
setMessage(body); // 수신 메시지를 상태로 저장
});
},
onStompError: (frame) => {
console.error("STOMP error:", frame.headers.message);
},
});
stompClient.activate(); // WebSocket 연결 활성화
clientRef.current = stompClient; // 클라이언트 참조 저장
};
// WebSocket 연결 해제
const disconnectWebSocket = () => {
if (clientRef.current) {
clientRef.current.deactivate(); // WebSocket 연결 해제
console.log("Disconnected from WebSocket");
}
setIsConnected(false);
};
// 메시지 전송
const sendMessage = () => {
if (clientRef.current && inputMessage.trim() !== "") {
clientRef.current.publish({
destination: "/app/chat", // 서버에 메시지 보낼 경로
body: inputMessage, // 전송할 메시지
});
setInputMessage(""); // 입력 필드 초기화
}
};
useEffect(() => {
connectWebSocket(); // 컴포넌트가 마운트될 때 WebSocket 연결
return () => {
disconnectWebSocket(); // 컴포넌트가 언마운트될 때 WebSocket 연결 해제
};
}, []);
return (
<div>
<h1>WebSocket with STOMP Example</h1>
<div>
<p>Connection status: {isConnected ? "Connected" : "Disconnected"}</p>
</div>
<div>
<label>Message from Server: </label>
<div>{message}</div>
</div>
<div>
<input
type="text"
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
placeholder="Enter message"
/>
<button onClick={sendMessage} disabled={!isConnected}>
Send Message
</button>
</div>
<div>
<button onClick={disconnectWebSocket} disabled={!isConnected}>
Disconnect WebSocket
</button>
</div>
</div>
);
};
export default WebSocketComponent;
이해한 내용을 바탕으로 예제코드도 한번 보면 좋을 것 같아서 가져와 봤습니다.
코드도 어렵지 않으니 읽어보는 것을 추천드립니다!
백엔드 친구에게 들은 강의로 대충 흐름은 알고 있었지만 정리하고 또 공부하면서 메세지 브로커 라는 주체가 있다는 것을 처음 알았다.
STOMP를 처음 접했을때 정말 충격적이였다. 그와 동시에 WebSocket과 정말 편리하고 유용하게 사용될 것 같았다. 코드를 짤때도 수월했다.
신세계를 경험했다.
처음 해보는 실시간 통신인데 어렵지 않고 오히려 재밌는 경험을 한 것 같아 기분이 째진다.
이제 본격적인 실시간 통신 개발만 남았는데 으쌰으쌰 힘내서 다 끝내보자 화이띵!
https://innu3368.tistory.com/213
https://growth-coder.tistory.com/157
https://velog.io/@mw310/Stomp-WebSocket-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%ACver-Spring