2025-08-11 21:37

Tags:

개발자 필독 웹소켓(WebSocket) 핸드북

목차

  1. 웹소켓, 왜 만들어졌을까?: HTTP의 한계와 웹소켓의 탄생 배경

  2. 구조와 작동 원리: 연결은 어떻게 맺고 데이터를 주고받는가

  3. 기본 사용법: 클라이언트와 서버 코드 예시

  4. 심화 탐구: 더 깊은 이해를 위한 개념들

  5. 활용 사례: 언제 웹소켓을 사용해야 할까?

1. 웹소켓, 왜 만들어졌을까?

웹소켓의 탄생 이유를 이해하려면 먼저 기존의 HTTP(HyperText Transfer Protocol) 통신 방식의 한계를 알아야 함.

  • HTTP의 근본적인 한계: 요청과 응답 모델

    • HTTP는 기본적으로 클라이언트가 서버에 **요청(Request)**을 보내면, 서버가 그에 대해 **응답(Response)**하는 구조.

    • 클라이언트의 요청 없이는 서버가 먼저 클라이언트에게 데이터를 보낼 수 없음. 이는 ‘실시간’ 통신이 필요한 현대 웹 애플리케이션에 치명적인 단점.

  • 과거의 임시방편과 그 문제점

    1. 폴링(Polling): 클라이언트가 일정한 주기(예: 1초)마다 서버에 “새로운 데이터 있나요?”라고 계속 물어보는 방식.

      • 단점: 실제 데이터 변경이 없어도 불필요한 요청과 응답이 계속 발생하여 서버와 네트워크에 큰 부담.
    2. 롱 폴링(Long-Polling): 클라이언트가 요청을 보내면 서버는 새로운 데이터가 생길 때까지 응답을 대기시킴. 데이터가 생기면 즉시 응답하고 연결을 닫음. 클라이언트는 응답을 받자마자 다시 요청을 보냄.

      • 단점: 폴링보다는 효율적이지만, 요청과 응답에 따른 헤더 정보가 여전히 크고, 데이터 교환이 잦을 경우 결국 폴링과 비슷해지는 문제.

이러한 비효율을 해결하고, 하나의 연결을 통해 서버와 클라이언트가 자유롭게 데이터를 주고받는 **‘진정한 양방향 통신’**을 위해 웹소켓이 표준으로 등장.

2. 구조와 작동 원리

웹소켓은 HTTP와 완전히 다른 프로토콜이지만, 연결의 시작은 HTTP를 통해 이루어짐.

  • ① 시작은 HTTP로부터: 핸드셰이크(Handshake)

    • 클라이언트는 일반적인 HTTP 요청을 보내지만, 헤더에 특별한 정보를 담아 보냄. 이는 “지금부터 HTTP 대신 웹소켓으로 통신 방식을 바꾸고 싶습니다”라는 신호.

    • 주요 헤더 정보:

      • Upgrade: websocket: 통신 프로토콜을 웹소켓으로 업그레이드 요청.

      • Connection: Upgrade: 연결 방식을 업그레이드하겠다는 의미.

      • Sec-WebSocket-Key: 클라이언트가 생성한 임의의 키.

    • 서버는 이 요청을 받고 웹소켓을 지원한다면, 응답 헤더에 Sec-WebSocket-Accept라는 값을 담아 보냄. 이 값은 클라이언트가 보낸 Sec-WebSocket-Key를 기반으로 생성된 암호화된 값이므로, 클라이언트는 서버가 웹소켓 요청을 정확히 이해했음을 확인함.

    • 비유: 일반 전화로 상대방에게 전화를 걸어 “이제부터 우리 둘만 아는 주파수의 무전기로 대화하자”라고 약속하고 채널을 맞추는 과정과 유사.

  • ② 한 번 맺은 연결은 계속: 영속적인 TCP 연결

    • 핸드셰이크가 성공적으로 완료되면, 기존의 HTTP 연결은 웹소켓 연결로 전환됨.

    • 이 TCP 연결은 한쪽에서 명시적으로 닫기 전까지 계속 유지됨.

    • 이제 클라이언트와 서버는 이 통로를 통해 언제든지 서로에게 메시지를 보낼 수 있음.

  • ③ 효율적인 데이터 전송: 프레임(Frame)

    • 웹소켓으로 데이터를 주고받을 때는 **프레임(Frame)**이라는 작은 단위로 데이터를 쪼개서 보냄.

    • 각 메시지마다 무거운 HTTP 헤더를 붙일 필요가 없어 오버헤드가 매우 적음. (단 몇 바이트 수준)

    • 텍스트 데이터뿐만 아니라 바이너리 데이터(이미지, 동영상, 오디오 등)도 효율적으로 전송 가능.

3. 기본 사용법

실제 코드에서 웹소켓이 어떻게 사용되는지 간단한 예시로 확인.

  • 클라이언트 측 (JavaScript)

    // 1. 'ws://' 프로토콜을 사용하여 웹소켓 서버에 연결
    // 보안 연결(SSL)을 위해서는 'wss://' 사용
    const socket = new WebSocket('ws://localhost:8080');
    
    // 2. 연결이 성공적으로 맺어졌을 때 실행되는 이벤트
    socket.onopen = function(event) {
        console.log('서버와 연결되었습니다.');
        // 서버로 메시지 전송
        socket.send('안녕하세요, 서버!');
    };
    
    // 3. 서버로부터 메시지를 수신했을 때 실행되는 이벤트
    socket.onmessage = function(event) {
        console.log('서버로부터 받은 메시지: ', event.data);
    };
    
    // 4. 연결이 닫혔을 때 실행되는 이벤트
    socket.onclose = function(event) {
        if (event.wasClean) {
            console.log(`연결이 정상적으로 종료되었습니다. 코드: ${event.code}`);
        } else {
            console.error('연결이 비정상적으로 끊어졌습니다.');
        }
    };
    
    // 5. 에러가 발생했을 때 실행되는 이벤트
    socket.onerror = function(error) {
        console.error(`[error] ${error.message}`);
    };
    
  • 서버 측 (Node.js + ws 라이브러리)

    // ws 라이브러리 설치: npm install ws
    const WebSocket = require('ws');
    
    // 1. 8080 포트에 웹소켓 서버 생성
    const wss = new WebSocket.Server({ port: 8080 });
    
    // 2. 클라이언트가 연결을 요청했을 때 실행되는 이벤트
    wss.on('connection', function connection(ws) {
        console.log('클라이언트가 연결되었습니다.');
    
        // 3. 클라이언트로부터 메시지를 수신했을 때 실행되는 이벤트
        ws.on('message', function incoming(message) {
            console.log('클라이언트로부터 받은 메시지: %s', message);
    
            // 받은 메시지를 연결된 모든 클라이언트에게 다시 전송 (Broadcast)
            wss.clients.forEach(function each(client) {
                if (client !== ws && client.readyState === WebSocket.OPEN) {
                    client.send(String(message)); // Buffer를 string으로 변환
                }
            });
        });
    
        // 4. 클라이언트로 메시지 전송
        ws.send('안녕하세요, 클라이언트!');
    });
    
    console.log('웹소켓 서버가 8080 포트에서 실행 중입니다.');
    

4. 심화 탐구

  • 보안 (WSS: WebSocket Secure)

    • wss:// 프로토콜은 https://와 마찬가지로 TLS(Transport Layer Security) 암호화를 통해 데이터를 보호.

    • 중간자 공격(Man-in-the-middle attack) 등을 방지하기 위해 실제 서비스에서는 반드시 wss 사용을 권장.

  • 서브 프로토콜 (Sub-protocols)

    • 클라이언트와 서버가 웹소켓 연결 위에서 어떤 형식의 메시지를 주고받을지 미리 약속하는 방법.

    • 예를 들어, 한쪽은 JSON만 보내고 다른 쪽은 XML만 보낸다면 혼란이 발생할 수 있음.

    • 핸드셰이크 과정에서 Sec-WebSocket-Protocol 헤더를 통해 사용할 프로토콜을 명시하고 합의할 수 있음.

  • 연결 유지 확인 (Ping/Pong)

    • 네트워크 문제나 방화벽 설정 등으로 연결이 예기치 않게 끊어질 수 있음.

    • 웹소켓은 Ping 프레임을 보내고 상대방이 Pong 프레임으로 응답하는 ‘하트비트(heartbeat)’ 메커니즘을 내장하고 있어 연결 상태를 주기적으로 확인 가능.

5. 활용 사례

웹소켓은 **낮은 지연 시간(Low Latency)**과 높은 상호작용성이 요구되는 거의 모든 서비스에 적용 가능.

  • 실시간 채팅 애플리케이션: 카카오톡, 페이스북 메신저 등

  • 온라인 멀티플레이어 게임: 플레이어들의 움직임을 실시간으로 동기화

  • 금융 정보 서비스: 실시간 주식 시세, 암호화폐 가격 변동 표시

  • 위치 기반 서비스: 배달 기사나 공유 차량의 실시간 위치 추적

  • 실시간 협업 도구: 구글 독스, Figma 등 여러 사용자의 동시 편집 내용 공유

이제 웹소켓의 기본 원리와 가능성을 이해하셨을 겁니다. 웹소켓 기술을 활용하여 어떤 흥미로운 실시간 서비스를 만들어보고 싶으신가요?

References

소켓 웹소켓