포스트

웹소켓

웹소켓

웹소켓 프로토콜은 HTTP와는 다른 통신 프로토콜로, 웹 서버와 웹 브라우저가 서로 실시간 메시지를 교환하는 데에 사용되는 양방향 통신 프로토콜이다. 첫 번째 핸드셰이크를 주고 받은 후 클라이언트와 서버가 동시에 통신하며 데이터를 교환하며, 메시지 별로 새로운 연결을 맺을 필요가 없어 빠르고 효율적이다.

웹소켓은 애플리케이션 계층에서 동작하며, 평문으로 전송되기에 SSL/TLS 보안 방식으로 암호화되어야 데이터 탈취를 방지할 수 있다. 웹소켓을 사용하면 채팅, 알림, 주식 거래 등 실시간 통신이 필요한 기능들을 구현할 수 있다.

  • 완전한 실시간 양방향 통신 채널

  • HTTP에서의 업그레이드 : 같은 TCP커넥션을 사용함

  • 쉽게 적용 가능 : 표준화되어 있으며 대부분의 웹 브라우저들이 지원함

support

  • 낮은 오버헤드 : 새로운 연결을 설정할 필요가 없음

  • 헤더는 첫 핸드셰이크에서만 전달

연결 과정

브라우저에서 소켓 통신을 이용하고자 하자면 앞서 말한것과 같이 첫 핸드셰이크를 주고 받음으로 소켓 통신이 가능한지를 확인해야 한다. 이 첫 핸드셰이크는 헤더를 포함하며, 여기에는 웹소켓에 관한 정보가 함께 전송된다.

1
2
3
4
5
6
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

서버에서 웹소켓 통신이 가능하다면, 이에 대한 상태코드로 응답을 클라이언트로 다시 돌려보낸다. 소켓을 통한 연결이 설정되면 양방향 통신이 진행되며, 프레임이라는 작은 단위로 데이터가 전송된다. 어느 한 쪽이 연결을 종료하고자 하면 ‘닫기’ 프레임을 보내어 연결을 종료한다.

코드

자바스크립트에서 웹소켓을 사용하고자 하면 다음과 같이 구현할 수 있다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const socket = new WebSocket('ws://excample.com/socket');

socket.onopen = function(event){
    console.log('Socket connection open');
    socket.send('Hello, server!');
};

socket.onmessage = function(event) {
    console.log('message received: ' + event.data);
};

socket.onclose = function(event) {
    console.log('WebSocket connection closed');
};

정글에서 진행했던 프로젝트 ‘Motion Beat’에서는 Socket.IO 라이브러리를 사용하여 구현했다.

아래는 ‘Motion Beat’에서 사용한 예시이다:

  1. 연결을 열어준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Server } from "socket.io";
import { createServer } from "http";
import {app} from "../app.js"

const httpServer = createServer(app);
const io = new Server(httpServer, {
    cors: {
        origin: "*",
        methods: ["GET", "POST", "PATCH"], // Specify the allowed HTTP methods
        allowedHeaders: ["Origin", "X-Requested-With", "Content-Type", "Accept", "Authorization"], // Specify the allowed headers
        credentials: true
    }
});

export {io, httpServer};
  1. socket.on을 사용하여 클라이언트가 보내는 메시지를 ‘듣고’, io.emit을 사용하여 클라이언트로 메시지를 보냈다.
    • 첫 부분은 실시간 채팅을 구현한 부분이며, 두번째 부분은 방을 나갔을 때 나머지 플레이어들이 이를 볼 수 있도록 구현한 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
socket.on("sendMessage", async (message, cb) => {
    try {
        const user = await userController.checkUser(socket.id);
        const room = await roomController.findRoomByPlayerNickname(user.nickname);
        if (!room) {
            return;
        }
        const newMessage = await chatController.saveChat(message, user, room.code);
        io.to(room.code).emit(`message`, newMessage);
        cb({ ok: true });
    } catch (err) {
        cb({ ok: false, error: err.message });
    }
})     

socket.on("joinRoom", async (receivedData, cb) => {
    const { nickname, code } = receivedData;
    socket.code = code;
    try {
        const roomPlayers = await roomController.getPlayerInfo(code);
        socket.join(code);
        io.emit(`players${code}`, roomPlayers);
        const room = await Room.findOne({ code })
        if (room.hostName !== nickname) {
            io.to(room.code).emit('allReady', false);
        }
        cb({ ok: true });
    } catch (err) {
        cb({ ok: false, error: err.message });
    }
});
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.