WebSocket은 무엇인가
클라이언트와 서버 간의지속적인 전이중 연결을 제공하는 통신 프로토콜입니다. HTTP 프로토콜과 달리, WebSocket은 한번 연결이 설정되면 양방향으로 데이터를 자유롭게 교환할 수 있습니다. 이는 실시간 업데이트와 즉각적인 데이터 전송을 필요로 하는 애츨리케이션에 이상적입니다.
WebSocket의 원리
OSI 7계층과 WebSocket
OSI 모델은 네트워크 통신을 7계층으로 나눈 모델입니다. WebSocket은 주로 전송계층(TCP)과 응용계층(HTTP/HTTPS)에서 작동합니다. 초기 연결설정은 HTTP/HTTPS 프로토콜을 통해 이루어지고, 연결이 설정되면 전송계층의 TCP를 통해 양방향 통신이 이루어집니다.
WebSocket의 동작 원리
- 핸드쉐이크: 클라이언트는 HTTP 요청을 사용하여 서버에 WebSocket 연결을 요청합니다.
- 프로토콜 업그레이드: 서버는 연결 요청을 수락하고, HTTP 프로토콜에서 WebSocket 프로토콜로 업그레이드 합니다.
- 양방향 통신: 연결이 설정되면 클라이언트와 서버는 양방향 통신을 수행할 수 있습니다. 이는 풀 이중(Full-Duplex) 통신을 의미합니다.
- 데이터 전송: 클라이언트와 서버는 테그스 또는 바이너리 데이터를 프레임 형태로 교환합니다.
WebSocket의 초기 연결 설정(HTTP 핸드쉐이크)
1. 클라이언트 요청
클라이언트는 HTTP 요청을 사용하여 WebSocket 연결을 요청합니다. 이 요청은 특별한 Upgrade 헤더를 포함하여 서버에 WebSocket 연결을 요청하는데, 이는 HTTP 1.1 프로토콜을 사용합니다.
- Upgrade: websocket: HTTP 프로토콜을 WebSocket 프로토콜로 업그레이드하도록 요청
- Connection: Upgrade: 연결을 업그레이드할 것을 명시
- Sec-WebSocket-Key: 클라이언트가 생성한 랜덤 키로, 서버는 이 키를 기반으로 응답 키를 생성합니다.
- Sec-WebSocket-Version: 클라이언트가 지원하는 WebSocket 프로토콜의 버전을 명시
2. 서버 응답
서버는 클라이언트의 요청을 수락하고, HTTP 응답을 통해 WebSocket 연결을 설정합니다.
- 101 Switching Protocols: HTTP 상태 코드로, 프로토콜을 WebSocket으로 전환하고 있음을 나타냅니다.
- Upgrade: websocket: 프로토콜을 WebSocket으로 업그레이드한다는 것을 명시
- Connection: Upgrade: 연결이 업그레이드된다는 것을 명시
- Sec-WebSocket-Accept: 서버가 클라이언트의 Sec-WebSocket-Key를 기반으로 생성한 응답 키로, 클라이언트는 이를 검증하여 연결이 성공적으로 설정되었는지 확인합니다.
3. WebSocket 데이터 전송 (WebSocket 프로토콜)
초기 연결이 설정되면, WebSocket 프로토콜로 전환되어 양방향 통신이 가능해집니다. 이 단계에서는 HTTP가 더이상 사용되지 않으며, WebSocket 프레임을 사용하여 데이터를 주고받습니다.
- 프레임 구조: WebSocket은 데이터 전송을 위해 프레임을 사용합니다. 각 프레임은 데이터의 유형(텍스트, 바이너리 등), 길이, 데이터 자체 등을 포함합니다.
- 양방향 통신: 클라이언트와 서버는 동일한 연결을 통해 주유롭게 데이터를 주고받을 수 있습니다. 이는 풀 이중(Full-Duplex)통신을 가능하게 합니다.
WebSocket 예시
클라이언트 측
const socket = new WebSocket('ws://example.com/socket');
socket.onopen = function(event) {
console.log('Connected to the WebSocket server');
socket.send('Hello Server!');
};
socket.onmessage = function(event) {
console.log('Received message from server: ', event.data);
};
socket.onclose = function(event) {
console.log('WebSocket connection closed');
};
socket.onerror = function(error) {
console.error('WebSocket error: ', error);
};
서버측
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function(socket) {
console.log('Client connected');
socket.on('message', function(message) {
console.log('Received message from client: ', message);
socket.send('Hello Client!');
});
socket.on('close', function() {
console.log('Client disconnected');
});
});
y-websocket은 무엇인가
Yjs는 실시간 협업을 가능하게 하는 Javascript 라이브러리입니다. Yjs는 CRDT(Conflit-free-Replicated Data Type)을 기반으로 하여 여러 사용자가 동시에 문서를 편집할 수 있도록 합니다. y-websocket은 Yjs와 함께 사용되는 WebSocket 프로바이더로, 여러 클라이언트 간의 상태 동기화를 관리합니다.
y-websocket의 기능
- 실시간 협업: 여러 클라이언트가 동시에 문서를 편집할 수 있으며, 각 클라이언트의 변경 사항이 즉시 다른 클라이언트에 반영됩니다.
- 자동 병합: Yjs는 CRDT 알고리즘을 사용하여 충돌 없이 데이터 병합을 처리합니다.
- 쉽고 효율적: y-websocket을 사용하면 WebSocket을 통해 Yjs 문서를 쉽게 동기화 할 수 있습니다.
y-websocket의 예시
클라이언트 측
import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';
// Yjs 문서를 생성
const ydoc = new Y.Doc();
// WebSocket 서버에 연결
const provider = new WebSocketProvider('wss://demos.yjs.dev', 'my-roomname', ydoc);
// Yjs의 텍스트 타입을 생성합니다.
const ytext = ydoc.getText('my-text');
// 텍스트 변경 사항을 처리하는 이벤트 리스너를 추가합니다.
ytext.observe(event => {
console.log('텍스트 변경:', ytext.toString());
});
// 텍스트를 변경합니다.
ytext.insert(0, 'Hello, Yjs!');
이 예제는 Yjs 문서를 생성하고, y-websocket을 통해 서버에 연결한 후, 텍스트를 변경하고 이를 실시간으로 동기화하는 기본적인 흐름을 보여줍니다.
서버 측
y-websocket은 클라이언트 측 라이브러리이므로, 서버 측에서는 WebSocket 서버를 구현하고 이를 y-websocket과 연결하여 사용해야 합니다.
const WebSocket = require('ws');
const http = require('http');
const { setupWSConnection } = require('y-websocket/bin/utils');
const port = 1234;
const server = http.createServer();
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws, req) => {
setupWSConnection(ws, req);
});
server.listen(port, () => {
console.log(`WebSocket server is running on port ${port}`);
});
'TIL(Today I Learned)' 카테고리의 다른 글
[git] rebase (0) | 2024.07.24 |
---|---|
[JS]전역객체와 Node객체 (0) | 2023.03.19 |
React에서 함수형 컴포넌트를 장려하는 이유 (0) | 2023.03.15 |
[예외 처리3]new Error() vs new Promise.reject() (0) | 2023.03.11 |
[예외발생2]Error() vs new Error() (0) | 2023.03.11 |