웹소켓 채팅 구현 전에, 원리 발표하기
HTTP 통신
웹 환경에서는 HTTP 요청을 통해 유저, 즉 클라이언트와 서버가 상태와 데이터를 주고받는다.
HTTP는 TCP/IP 프로토콜 스택의 응용 계층에서 동작하는 프로토콜
방식
클라이언트가 HTTP 요청을 전송하면, 서버는 해당 데이터를 반환한다.
서버는 클라이언트의 요청이 있어야만 데이터를 응답하며, 데이터 반환 후 통신이 종료된다.
한계
과거에는 모든 서비스를 HTTP로 개발할 수 있었으나,
현재는 실시간 네트워킹, 대규모 사용자 처리 등 복잡한 개념이 필요함.
ex. 실시간 채팅이나 게임 등
이러한 상황을 구현하기 위해 주기적으로 서버에 요청을 보내는 방법이 사용되었다. HTTP
HTTP 실시간 통신 구현 방법
HTTP는 다음 세 가지 방법으로 실시간 통신을 구현했다:
- Polling: 클라이언트가 주기적으로 서버에 GET 요청을 보내고, 서버가 데이터를 응답한다. 자원 효율성이 떨어짐.
- Long Polling: 서버에서 새로운 이벤트가 준비될 때까지 대기하고, 준비되면 응답한다.
- Streaming: 대용량 비디오-오디오 처리에 유리하지만, 클라이언트는 첫 요청 이외에 추가 요청을 할 수 없다.
이 모든 방법이 HTTP 통신이기에 헤더가 크다는 문제점이 있음.
웹소켓 프로토콜
위 문제점을 해결하기 위해 웹소켓을 사용한다.
HTML5에서 나온 표준 기술로, TCP 기반의 양방향 통신을 지원한다.
웹소켓을 사용하려면 핸드쉐이크를 통해 클라이언트와 서버 간의 연결을 설정해야 한다.
초기 접속은 HTTP를 사용하고, 이후 데이터 통신은 웹소켓 프로토콜을 사용한다.
HTML5 이전 브라우저 지원
HTML5 이전의 브라우저에서는 웹소켓을 지원하지 않기 때문에 Socket.io, SockJS 등의 기술을 사용해 실시간 웹을 구현한다.
핸드쉐이크 과정
웹소켓 핸드쉐이크는 이 과정으로 연결됨:
- 클라이언트가 웹소켓 연결을 위한 HTTP GET 요청을 전송
- 서버가 요청을 통해 프로토콜을 웹소켓으로 업그레이드
- 웹소켓이 열리면서 연결이 유지
1. 클라이언트 -> 서버 : 핸드쉐이크 요청 HTTP request
요청의 헤더
- Host: 요청을 보낼 서버의 호스트
- Upgrade: 웹소켓 프로토콜로의 전환 요청
- Connection: 연결을 업그레이드 요청
- WebSocket Key: 클라이언트가 생성한 인증 키
- Version: 클라이언트가 지원하는 웹소켓 프로토콜 버전
2. 서버의 핸드쉐이크 응답
서버는 클라이언트의 핸드쉐이크 요청에 대해 다음과 같은 헤더로 응답한다.
이 응답 이후 http 요청 통신에서 웹소켓 통신으로 업그레이드 한다.
- Switching Protocols: 프로토콜 전환 승인
- Upgrade: 웹소켓 프로토콜로의 전환 확인
- Connection: 연결이 업그레이드 되었음을 명시
- WebSocket Accept: 클라이언트의 인증 키 확인
3. 웹소켓 연결
웹소켓 서버로 클라이언트와 서버는 연결
연결된 상태를 open이라고 하며, 필요 시 close를 통해 통신이 종료
웹소켓 서버에서 데이터 송수신? STOMP
연결된 상태에서 HTTP 요청은 웹소켓 프로토콜로 전환되어 보내진다.
`HTTP(s)` -> ws(s) 전환
서버는 업데이트된 데이터를 자유롭게 전송한다. 클라이언트의 따로 요청이 없어도 원할 때 전송할 수 있다.
하나의 웹소켓 서버에 연결되어 통신한다.
데이터 전송 형식
웹소켓 통신에 사용되는 데이터는 UTF-8 인코딩을 통해 메시지 단위로 전송 `0x00(데이터 UTF 8 인코딩 값)0xff`
데이터 UTF8 해독 형식을 제공하는 서브 프토로콜 STOMP(Simple Text Oriented Message Protocol) 을 사용해서 어플리케이션 단계에서 메시지를 해독한다.
예시 1 : 웹소켓 서버 연결 코드 - Socket.io 사용
server.js
//웹소켓 서버 열기
const io = socketIo(server);
//클라이언트 서버 연결될때 실행
io.on("connection", (socket) => {
console.log("클라이언트가 연결되었습니다.");
//클라이언트 > 서버 메시지 수신
socket.on("chat message", (msg) => {
console.log(`클라이언트에게 받은 메시지: ${msg}`);
//서버 > 클라이언트 메시지 전송
io.emit("chat message", msg);
});
//웹소켓 연결 close
socket.on("disconnect", () => {
console.log("클라이언트가 연결 해제되었습니다.");
});
});
index.html
//웹서버 입장
const socket = io();
//웹서버 close
function disconnect() {
socket.disconnect();
alert("연결이 해제되었습니다.");
}
//클라이언트 > 서버 메시지 전송 함수
function sendMessage() {
const input = document.getElementById("messageInput");
const message = input.value;
//소켓 전송
socket.emit("chat message", message);
input.value = "";
return false;
}
//서버 > 클라이언트 메시지 수신 함수
socket.on("chat message", (msg) => {
const item = document.createElement("li");
item.textContent = msg;
document.getElementById("messages").appendChild(item);
console.log(`서버에게 받은 메시지: ${msg}`);
});
예시 2 : 코테이토 사이트
코테이토 사이트에서 웹소켓을 통해 실시간으로 문제풀이를 진행할때 소켓통신 사용된다.
관리자가 문제를 제출하면, 서버가 실시간으로 유저에게 신호를 전달하고,
유저는 제출 버튼이 활성화되는 실행을 실시간으로 받게 됨.
클라이언트의 request 요청 없이,, 이벤트가 발생한 즉시 응답이 온다!
Socket.io 동작 방식
웹페이지가 열리는 브라우저는 인식해서, Socket.io는 웹소켓을 지원하면 웹소켓 방식으로 동작하고, 지원하지 않는 브라우저라면 일반 HTTP를 이용해 실시간 통신을 흉내낸다.
*참고 : 아래의 기술을 하나의 api로 융합해 적합한 기술을 선정한다고 한다.
WebSocket, FlashSocket, AJAX Long Polling, AJAX Multi part Streaming, IFrame, JSONP Polling
이외로, 웹소켓은 아니지만 아파치 kafka 도 같이 많이 사용한다고 한다.
대량의 데이터를 실시간으로 처리하는 분산 스트리밍 플랫폼
Socket.io보다 더 복잡하지만, 순서가 보장되는 부분이 있다는 장점이 있다.
웹소켓의 한계
웹소켓 사용 시 메모리 증가와 서버 부담 등의 문제가 발생한다.
-> 빠르게 포워딩해야하고, 사용자가 늘어나면 메모리가 늘어나고, 서버 비용이 증가함!
+ 서버가 내려가면 전체적으로 문제가 발생한다는 점
이때 서버에 부담이 가는 부분을 해결하기 위해
WebRTC(web RealTime Communication)와 같은 기술을 사용해 클라이언트끼리 데이터를 송수신할 수 있도록 하는 기술도 사용한다.
서버보다 빠르지만, 확장성에는 제약이 존재하기도 함.
결론
웹소켓은 실시간 통신에서 유용하지만,
이외에도 트래픽 양에 따른 비용 부담과 코드 복잡성 등의 단점이 있다. 에러 메시지의 디버깅에도 어려움이 많음.
따라서 프로젝트에 꼭 필요한 기술인지 고민하고 적용하는 것이 중요하다.
발표사진
이 발표를 통해 Kampus 유학생 커뮤니티 프로젝트에서 웹소켓을 도입하게 되었다.
실시간 통신의 동작원리를 이해함으로써 소켓통신의 세팅과 통신규칙을 정할 수 있었다.
2편 프로젝트에 웹소켓 도입기 : 적절한 위치