국비지원 부캠 수업을 들을 때 Websocket 을 다루는 역할을 맡지 않아서 이번에 인턴하면서 Websocket 개념이랑 ...
몰라서 생긴 여러 트러블슈팅을 정리하고 넘어가기
이번에 프로젝트에서 사용한 이유는
crowd count, 영상 CCTV 처럼 송출 이 필요한데 그 때마다 client (dashboard) 에 즉시 알려줘야 하기 땜누에 HTTP polling 보다 효율적인 방식인 Websocket 사용이었다
특징
비교 대상: HTTP
| HTTP | 응답 이후 연결이 끊김 (매번 새로 요청, 검증) keyword: 비연결, 무상태 |
| WebSocket | 한 번 연결하면 계속 유지 server -> client 방향으로 먼저 데이터 보낼 수 있음 (양방향) |
ws:// 랑 wss:// 는 Http 랑 동일하게 암호화 여부 (s 가 붙은게 security)
만약 사용하는 서버가 https://tempurl.co.kr 로 서빙되면 반드시 wss:// 를 사용하기
아니면 브라우저가 Mixed Content 에러를 띄운다고 함 (Https page 에서 암호화 안 된 연결을 시도하는거니까!)
연결 과정을 Handshake 라고 부르는 듯 함 귀엽다.!! 악수라니
1. client 가 일반 HTTP 요청을 보낼 때 header 에 Upgrade: websocket 이 포함됨

프론트 코드에서 아래 한 줄을 실행하면
const ws = new WebSocket("wss://temp.url.co.kr/ws/crowd");
브라우저가
GET /ws/crowd HTTP/1.1
Host: tmep.url.co.kr
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xxxxx
Sec-WebSocket-Version: 13
요런 헤더가 붙은 HTTP 요청을 만들어서 보냄
이 설정이 NGINX 에서 해주는 아래 설정
location /ws/ {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Upgrade 요청을 붙여주기
2. 서버가 101 Switching Protocols 응답하면 그때부터 HTTP 연결이 WebSocket 연결로 upgrade 됨
3. 이후로 계속 열린 채널로 메시지 주고 받기
(매번 요청을 새로 쓸 필요 없어짐)
추가
기억해둘만한 WebSocket 객체의 기본 이벤트
ws.onopen = () => console.log("연결됨");
ws.onmessage = (event) => console.log("데이터 옴:", event.data);
ws.onerror = (error) => console.log("에러:", error);
ws.onclose = () => console.log("연결 끊김");
특히 onmessage 에서 event.data 가 문자열로 오기 때문에 JSON.parse(data) 해줘야함!!!
추가로 onclose 되면 재시도를 해줘야하기 때문에 특정 시간 이후에 재시도 하는 로직을 추가해주는게 좋다
ws.onclose = () => {
setTimeout(() => connectWebSocket(), 3000); // 3초 뒤 재시도
};
++ 백엔드 서버 하나랑만 연결 가능함 - 한 번 연결된 백엔드 인스턴스랑 계속 붙어있어야한다
WebSocket 프로토콜에는 ping/pong 이 있는데 java script 브라우저 에는 native ping 을 보내는 api 가 X
-> 근데 NGINX 의 proxy_read_timeout 은 backend 로부터 아무런 데이터가 안오는걸 기준으로 count 를 세기 때문에
가짜메시지... 평범한 메시지를 주기적으로 보내는 방식 사용
const ws = new WebSocket("wss://temp.url.co.kr/ws/crowd");
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "ping" }));
}
}, 30000); // 30초마다 살아있다고 알림
client (front) 에서 이렇게 보내주면
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
if (message.getPayload().contains("\"type\":\"ping\"")) {
session.sendMessage(new TextMessage("{\"type\":\"pong\"}"));
return;
}
// 나머지 로직...
}
Spring Boot 쪽 WebSocket Handler 가 이렇게 받아서... 연결이 안 끊기게 해줌