WebSocket은 TCP 소켓 연결을 통해 클라이언트와 서버 간의 양방향 통신을 가능하게 합니다. 일반적인 HTTP 통신에서는 클라이언트가 요청을 보내고 서버가 응답을 반환하는 방식으로 동작합니다. 반면 WebSocket은 클라이언트가 연결을 초기화한 후 지속적으로 열린 연결을 유지하며 양측이 데이터를 자유롭게 보낼 수 있습니다.
최신 브라우저는 대부분 웹 소켓을 지원하며, 노드는 ws
나 Socket.IO
같은 패키지를 통해 웹 소켓 사용 가능합니다.
WebSocket 통신은 처음에 HTTP 요청을 통해 시작됩니다. 이 과정은 "핸드셰이크"라고 불립니다. 클라이언트는 서버에 WebSocket 연결을 요청하는데, 이 과정은 다음과 같습니다.
클라이언트의 업그레이드 요청
Upgrade
헤더가 포함된 특별한 HTTP GET 요청을 보냅니다.Connection: Upgrade
, Upgrade: websocket
등이 명시됩니다.GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
서버의 응답
101 Switching Protocols
라는 상태 코드와 함께 WebSocket 연결을 승인합니다.Sec-WebSocket-Key
를 기반으로 새로운 값을 계산해 Sec-WebSocket-Accept
헤더로 반환합니다. 이를 통해 클라이언트는 서버의 응답이 유효한지 확인할 수 있습니다.HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
핸드셰이크가 완료되면 WebSocket 연결이 확립되며, 이 시점부터는 클라이언트와 서버가 기존의 HTTP 요청/응답 사이클을 벗어나 양방향으로 데이터를 주고받을 수 있게 됩니다.
지속적인 연결
프레임 기반 통신
양방향 통신
WebSocket은 두 가지 주요 데이터 유형인 텍스트 메시지와 바이너리 메시지를 지원합니다. 데이터는 프레임이라는 작은 조각으로 나뉘어 전송되며, 다음과 같은 구조로 전송됩니다.
WebSocket 연결은 클라이언트나 서버가 언제든지 종료할 수 있습니다. 종료 과정은 다음과 같습니다.
HTTP
💡 Polling
웹소켓 등장 이전에 웹 애플리케이션에서 서버와 실시간으로 데이터를 주고받기 위해 주로 사용하던 방식 중 하나로 HTTP의 가장 큰 특징이자 단점이 될 수 있는 단방향 통신을 해결하기 위해 HTTP 프로토콜을 활용해 억지로 양방향 통신을 구현한 것입니다. 이 방식은 클라이언트(브라우저)가 일정한 시간 간격으로 서버에 데이터를 요청하여 서버의 상태나 업데이트된 정보를 확인하는 방식입니다.
WebSocket
HTTP
WebSocket
HTTP
WebSocket
HTTP
WebSocket
HTTP
WebSocket
특성 | HTTP | WebSocket |
---|---|---|
통신 방식 | 요청-응답 | 양방향 통신 |
연결 유지 | 비연결형, 요청 시마다 새 연결 | 지속적 연결 |
통신 방향 | 단방향 (클라이언트 → 서버) | 양방향 (클라이언트 ↔ 서버) |
실시간성 | 폴링이나 롱 폴링으로 실시간 구현 가능 | 실시간 통신에 매우 적합 |
오버헤드 | 각 요청에 헤더 포함 | 핸드셰이크 이후 헤더 오버헤드 없음 |
활용 사례 | 정적 콘텐츠 제공, 요청 기반 데이터 | 실시간 채팅, 알림, 게임, IoT 등 |
WebSocket은 HTTP의 단방향성 한계를 극복하고, 실시간으로 양방향 소통이 필요한 상황에서 뛰어난 효율성과 성능을 제공합니다. 반면 HTTP는 간단한 요청-응답이 필요한 웹 콘텐츠 제공에 더 적합한 프로토콜입니다.
ws
는 Node.js에서 순수한 WebSocket 프로토콜을 구현하기 위한 가장 기본적인 라이브러리입니다. 매우 경량이며, WebSocket 표준을 직접 사용합니다. 불필요한 부가 기능 없이 간단한 실시간 통신을 구현하고자 할 때 적합합니다.
WebSocket.Server
의 메서드와 이벤트
주요 객체
WebSocket.Server
): 서버를 생성하기 위한 객체입니다. 클라이언트와의 연결을 관리하며, 여러 이벤트 리스너를 설정하여 클라이언트와의 소통을 지원합니다.WebSocket
): 서버 또는 클라이언트 측에서 WebSocket 연결을 나타내는 객체입니다. 각 클라이언트와의 연결을 이 객체를 통해 처리합니다.WebSocket.Server
의 메서드와 이벤트
new WebSocket.Server(options)
: WebSocket 서버를 생성하는 생성자입니다. 여기서 options
객체를 통해 포트 번호와 같은 설정을 할 수 있습니다.const server = new WebSocket.Server({ port: 8080 });
server.on(event, callback)
: 특정 이벤트가 발생했을 때 실행할 콜백 함수를 설정합니다.event는 아래와 같이 구성됩니다:
connection
: 클라이언트가 연결될 때 발생합니다. 콜백 함수는 연결된 WebSocket 객체를 인자로 받습니다.
server.on('connection', (socket) => {
console.log('새 클라이언트가 연결되었습니다.);
});
close
: 서버가 종료되거나 연결이 종료될 때 발생합니다.
server.on('close', () => {
console.log('서버가 종료되었습니다.');
});
error
: 서버에서 오류가 발생했을 때 호출됩니다.
server.on('error', (error) => {
console.log(`에러가 발생했습니다: ${error.message}`);
});
socket.send(data)
: 서버 또는 클라이언트가 상대방에게 데이터를 전송합니다. data
는 문자열 또는 바이너리 형식일 수 있습니다.socket.send('안녕하세요, 클라이언트!');
socket.on(event, callback)
: 특정 이벤트에 대해 콜백을 설정합니다.event는 아래와 같이 구성됩니다:
message
: 상대방으로부터 메시지를 받을 때 발생합니다. 콜백 함수는 수신된 메시지를 인자로 받습니다.
socket.on('message', (message) => {
console.log(`받은 메시지: ${message}`);
});
close
: 연결이 닫힐 때 발생합니다.
socket.on('close', () => {
console.log('연결이 닫혔습니다.');
});
error
: 연결 중 오류가 발생할 때 호출됩니다.
socket.on('error', (error) => {
console.log(`에러가 발생했습니다: ${error.message}`);
});
socket.close()
: WebSocket 연결을 닫습니다. 연결이 닫히면 close
이벤트가 발생합니다.WebSocket
객체는 addEventListener
메서드를 통해 다양한 이벤트에 리스너를 추가할 수 있습니다. 이 방식은 이벤트 리스너를 설정하는 또 다른 방법으로, 이벤트 기반 통신에서 자주 사용됩니다. onmessage
, onopen
, onclose
등 속성을 설정하는 방법과 비슷하지만, 이 방식은 이벤트 리스너를 여러 개 추가할 수 있다는 장점이 있습니다.
주요 이벤트
open
: WebSocket 연결이 성공적으로 열렸을 때 발생하는 이벤트입니다.message
: 서버로부터 메시지가 도착했을 때 발생하는 이벤트입니다.close
: WebSocket 연결이 닫혔을 때 발생하는 이벤트입니다.error
: WebSocket 연결에서 오류가 발생했을 때 발생하는 이벤트입니다.// WebSocket 서버에 연결
const socket = new WebSocket('ws://localhost:3000');
// 'open' 이벤트 리스너 추가
socket.addEventListener('open', (event) => {
console.log('WebSocket 연결이 열렸습니다.');
socket.send('서버에 메시지를 전송합니다.');
});
// 'message' 이벤트 리스너 추가
socket.addEventListener('message', (event) => {
console.log(`서버로부터 받은 메시지: ${event.data}`);
});
// 'close' 이벤트 리스너 추가
socket.addEventListener('close', (event) => {
console.log('WebSocket 연결이 종료되었습니다.');
});
// 'error' 이벤트 리스너 추가
socket.addEventListener('error', (event) => {
console.error('WebSocket 오류 발생:', event);
});
ws
모듈을 사용하여 간단한 채팅을 구현한 예시입니다.
서버에 ws
모듈 설치하기
npm install ws
서버 코드
// backend/server.js
const WebSocket = require("ws");
// 포트 3000번에서 WebSocket 서버를 실행
const server = new WebSocket.Server({ port: 3000 });
// backend/server.js
server.on("connection", (ws) => {
console.log("클라이언트가 연결되었습니다.");
// 클라이언트로부터 메시지를 받았을 때
ws.on("message", (message) => {
console.log(`클라이언트로부터 받은 메시지: ${message}`);
// 연결된 모든 클라이언트에게 메시지를 브로드캐스트
server.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message.toString()); // 메시지를 문자열로 전송
}
});
});
// 연결이 종료되었을 때
ws.on("close", () => {
console.log("클라이언트가 연결을 종료했습니다.");
});
// 클라이언트에게 초기 메시지 전송
ws.send("웹소켓 서버에 연결되었습니다.");
});
클라이언트 코드
<!-- frontend/index.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Socket.IO</title>
</head>
<body>
<h1>Socket.IO</h1>
<div id="chat">
<div id="messages"></div>
<input id="messageInput" type="text" placeholder="메시지를 입력하세요" />
<button id="sendButton">전송</button>
</div>
<script>
// 서버와 연결
const socket = io('http://localhost:3000');
socket.on('connect', () => {
console.log('서버에 연결되었습니다.');
});
// 메시지 전송 버튼 클릭 시 메시지를 서버로 보냄
document.getElementById('sendButton').addEventListener('click', () => {
const messageInput = document.getElementById('messageInput');
const message = messageInput.value;
if (message.trim() !== '') {
socket.emit('chat message', message); // 서버로 메시지 전송
messageInput.value = ''; // 메시지 입력 필드 초기화
}
});
// 서버로부터 받은 메시지를 화면에 표시
socket.on('chat message', (message) => {
const messagesDiv = document.getElementById('messages');
const messageElement = document.createElement('div');
messageElement.textContent = message;
messagesDiv.appendChild(messageElement);
});
socket.on('disconnect', () => {
console.log('서버와의 연결이 종료되었습니다.');
});
</script>
</body>
</html>
Web Socket은 HTML5의 기술이기 때문에오래된 버전의 웹 브라우저는 Web Socket을 지원하지 않습니다. 특히 자동 업데이트가 되지 않는 익스플로러 구 버전 사용자들은 Web Socket으로 작성된 웹페이지를 볼 수 없습니다. 따라서 이를 해결하기 위해 나온 여러 기술 중 하나가 Socket.io
입니다.웹페이지가 열리는 브라우저가 웹소켓을 지원하면 웹소켓 방식으로 동작하고,지원하지 않는 브라우저라면 일반 http를 이용해서 실시간 통신을 흉내내는 것입니다.
Socket.io
는 자바스크립트를 이용하여 브라우저 종류에 상관없이 실시간 웹을 구현할 수 있도록 한 기술입니다. WebSocket뿐만 아니라 폴백 메커니즘을 사용하여 다양한 실시간 통신 방식(예: HTTP 폴링)을 지원합니다. WebSocket 프로토콜을 이용하지만, 추가적인 기능(브로드캐스팅, 룸, 자동 재연결 등)을 제공하여 실시간 통신을 구현하는 데 더 유연한 환경을 제공합니다.
주요 객체
Server
(io
): Socket.IO 서버 객체로, 여러 클라이언트와의 연결을 관리합니다.Socket
(socket
): 개별 클라이언트와의 연결을 나타내는 객체로, 클라이언트와 서버 간의 통신을 담당합니다.Server
객체의 메서드와 이벤트
require('socket.io')(port, options)
: 서버를 생성하며, 주어진 포트에서 실행합니다.const io = require('socket.io')(3000);
io.on(event, callback)
: 이벤트 리스너를 설정합니다.connection
: 새로운 클라이언트가 연결되었을 때 호출됩니다. 콜백 함수는 연결된 클라이언트의 socket
객체를 인자로 받습니다.
io.on('connection', (socket) => {
console.log('새 클라이언트가 연결되었습니다.');
});
io.emit(event, data)
: 연결된 모든 클라이언트에게 이벤트와 데이터를 전송합니다.io.emit('broadcast', '모든 클라이언트에게 메시지를 보냅니다.');
Socket
객체의 메서드와 이벤트
socket.on(event, callback)
: 특정 이벤트에 대해 콜백을 설정합니다.message
: 기본 이벤트로, 클라이언트가 서버에 데이터를 전송할 때 호출됩니다.socket.on('message', (data) => {
console.log(`클라이언트로부터 받은 메시지: ${data}`);
});
socket.on('chat message', (msg) => {
console.log(`받은 채팅 메시지: ${msg}`);
});
socket.emit(event, data)
: 현재 연결된 클라이언트에게 이벤트와 데이터를 전송합니다.socket.emit('response', '서버로부터 응답 메시지');
socket.broadcast.emit(event, data)
: 현재 연결된 클라이언트를 제외한 나머지 모든 클라이언트에게 이벤트와 데이터를 전송합니다.socket.broadcast.emit('new user', '새 사용자가 접속하였습니다.');
io.of(namespace)
): 네임스페이스는 서로 다른 목적의 통신 채널을 분리하기 위해 사용합니다.const chat = io.of('/chat');
chat.on('connection', (socket) => {
console.log('채팅 네임스페이스에 연결되었습니다.');
});
socket.join(room)
과 같은 메서드를 사용하여 여러 클라이언트를 그룹화합니다.socket.join('room1');
io.to('room1').emit('message', '룸1에 있는 모든 사용자에게 메시지 전송');
Socket.IO
모듈을 사용하여 간단한 채팅을 구현한 예시입니다.
Socket.IO
는 서버와 클라이언트 모두 설치가 필요합니다.
npm install socket.io socket.io-client
서버 코드
// backend/server.js
const io = require('socket.io')(3000, {
cors: {
origin: "*", // 모든 도메인에서 접근 허용 (개발용으로만 사용)
methods: ["GET", "POST"]
}
});
io.on('connection', (socket) => {
console.log('클라이언트가 연결되었습니다.');
// 클라이언트로부터 채팅 메시지를 받았을 때
socket.on('chat message', (message) => {
console.log(`클라이언트로부터 받은 메시지: ${message}`);
// 연결된 모든 클라이언트에게 메시지를 브로드캐스트
io.emit('chat message', message);
});
// 연결이 종료될 때
socket.on('disconnect', () => {
console.log('클라이언트가 연결을 종료했습니다.');
});
});
클라이언트 코드
<!-- frontend/index.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Socket.IO</title>
<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
</head>
<body>
<h1>Socket.IO</h1>
<div id="chat">
<div id="messages"></div>
<input id="messageInput" type="text" placeholder="메시지를 입력하세요" />
<button id="sendButton">전송</button>
</div>
<script>
// 서버와 연결
const socket = io('http://localhost:3000');
socket.on('connect', () => {
console.log('서버에 연결되었습니다.');
});
// 메시지 전송 버튼 클릭 시 메시지를 서버로 보냄
document.getElementById('sendButton').addEventListener('click', () => {
const messageInput = document.getElementById('messageInput');
const message = messageInput.value;
if (message.trim() !== '') {
socket.emit('chat message', message); // 서버로 메시지 전송
messageInput.value = ''; // 메시지 입력 필드 초기화
}
});
// 서버로부터 받은 메시지를 화면에 표시
socket.on('chat message', (message) => {
const messagesDiv = document.getElementById('messages');
const messageElement = document.createElement('div');
messageElement.textContent = message;
messagesDiv.appendChild(messageElement);
});
socket.on('disconnect', () => {
console.log('서버와의 연결이 종료되었습니다.');
});
</script>
</body>
</html>
ws
와 Socket.IO
는 모두 Node.js에서 WebSocket 프로토콜을 사용하여 클라이언트와 서버 간의 실시간 통신을 구현하는 라이브러리입니다. 그러나 이 둘은 기능적, 구조적, 사용 편의성 측면에서 차이가 있습니다.
ws
ws
는 순수한 WebSocket 프로토콜만을 구현하는 경량 라이브러리입니다. WebSocket 프로토콜만을 사용하기 때문에 직접 WebSocket 연결을 관리해야 합니다.Socket.IO
Socket.IO
는 WebSocket을 포함한 다양한 실시간 통신 방법을 자동으로 지원합니다. WebSocket을 기본으로 사용하지만, 클라이언트가 WebSocket을 지원하지 않는 경우 자동으로 HTTP 폴링 등의 방법으로 폴백(fallback)할 수 있습니다.ws
ws
는 순수한 WebSocket 연결만을 제공하기 때문에 매우 경량이고, 최소한의 기능만 포함되어 있습니다.Socket.IO
Socket.IO
는 다양한 부가 기능을 제공합니다Socket.IO
는 채팅 애플리케이션이나 게임과 같은 복잡한 실시간 애플리케이션을 구현할 때 매우 유용합니다.ws
ws
는 WebSocket 표준에 매우 충실하여 간단하고 경량입니다. 따라서 설정과 사용이 비교적 간단하며, 초기에 WebSocket 프로토콜을 잘 이해하고 있는 개발자라면 사용하기 수월합니다.Socket.IO
Socket.IO
는 설정이 다소 복잡할 수 있지만, 다양한 기능을 손쉽게 활용할 수 있는 장점이 있습니다. 예를 들어, 서버에서 여러 클라이언트를 연결하고 특정 그룹에만 메시지를 보낼 때 별도의 복잡한 설정 없이도 룸 기능을 사용하면 쉽게 구현할 수 있습니다.ws
Socket.IO
ws
ws
는 WebSocket 표준에 따른 프레임 단위의 전송을 사용하며, 데이터 오버헤드가 거의 없습니다. 매우 경량화된 프레임을 사용해 네트워크 부하를 줄일 수 있습니다.Socket.IO
Socket.IO
는 다양한 기능을 제공하는 만큼, 각 데이터 전송에 추가적인 프로토콜 오버헤드가 발생합니다. 이벤트의 이름이나 데이터 구조 관리 등의 부가적인 데이터를 포함해 전송하기 때문에 기본 ws
라이브러리에 비해 오버헤드가 큽니다.특성 | ws | Socket.IO |
---|---|---|
프로토콜 | WebSocket만 지원 | WebSocket + 폴백 메커니즘 (HTTP 폴링 등) |
기능 | 기본적인 WebSocket 기능 제공 | 이벤트 기반 통신, 자동 재연결, 룸, 네임스페이스 등 |
설정 편의성 | 간단하고 경량 | 설정이 다소 복잡하나 다양한 기능 지원 |
호환성 | 브라우저에서 WebSocket만 사용 가능 | 폴백 기능을 통해 호환성 높음 |
오버헤드 | 최소한의 데이터 오버헤드 | 부가적인 데이터 오버헤드 발생 |
사용 예시 | 간단한 실시간 데이터 전송 | 채팅, 게임 등 복잡한 실시간 애플리케이션 |
ws
는 단순하고 가벼운 실시간 애플리케이션에 적합하고, Socket.IO
는 더 복잡한 기능을 손쉽게 구현할 수 있어 다양한 요구사항을 가진 실시간 시스템에서 유리합니다.