Rocky 's Blog

WebSocket부터 이해하자

  • Socket
  • WebSocket
2025. 12. 27.
게시글 썸네일

Socket 이란


Socket 의 개념

Socket 의 사전적 의미는 다른 부분이 들어갈 수 있도록 만들어 놓은 구멍 또는 서로 다른 것이 연결될 수 있도록 마련된 창구 를 뜻한다.

Notion image

네트워크에서 Socket 은 컴퓨터 프로그램이 네트워크를 통해 데이터를 주고받을 수 있도록 하는 연결점이다. 벽에 콘센트가 없으면 전기 플러그를 꽂아 전기를 사용할 수 없듯이, 네트워크 통신에서도 송신 측 (Client Socket) 과 수신 측 (Server Socket) 모두에 소켓이 열려 있어야 그 통로를 통해 데이터가 오갈 수 있다.

Socket Programming

프로세스 간 통신 (IPC) 을 위해 소켓을 활용하는 네트워크 프로그래밍 기법이다. 서로 동일한 구조의 소켓을 사용하지만, Client SocketServer Socket은 역할에 따라 동작 흐름이 다르다.

Notion image

Client Socket 의 처리 흐름

단계함수설명
1소켓 생성- 연결 대상 정보(IP, Port 등)가 없는 빈 소켓을 생성
2연결 요청 (connect)- 통신 대상의 IP 주소와 포트 번호를 지정하여 연결을 요청 - 요청이 수락되어야 connect() 실행이 완료됨
3-1데이터 송수신 (send)- 클라이언트가 데이터를 보낼 시점과 크기를 직접 제어 가능
3-2데이터 수신 (recv)- 상대가 데이터를 언제, 얼마나 보낼지 알 수 없으므로, 일반적으로 별도의 스레드에서 처리
4소켓 종료 (close)- 통신이 완료되면 소켓을 닫아 연결을 종료

Server Socket 의 처리 흐름

단계함수설명
1소켓 생성 (socket)- 연결 대상 정보가 없는 빈 소켓을 생성
2소켓과 포트 바인딩 (bind)- 생성된 소켓에 포트 번호를 연결 - 여러 프로세스가 동시에 실행될 수 있으므로 서로 다른 포트를 사용해야 데이터가 올바른 프로세스에 전달됨 - 하나의 프로세스가 같은 포트를 공유하는 여러 소켓을 가질 수도 있음 예를 들어 채팅 서버가 여러 사용자와 동시에 통신 가능
3연결 요청 대기 (listen)- 클라이언트의 연결 요청을 받을 준비 - 요청이 들어오기 전까지 대기 상태로 있으며, 요청이 감지되면 반환됨
4연결 수락 (accept)- accept()는 실제 연결을 수립하는 단계 - 연결 요청이 수락되면 새로운 소켓을 생성하여 해당 클라이언트와의 통신 전용으로 사용 - 기존 Server Socket은 요청 수락 기능만 담당하고, 데이터 송수신은 새로 생성된 소켓이 담당
5데이터 송수신 (send, recv)- 클라이언트 소켓과 동일하게 동작 - 특히 수신은 비동기적으로 처리하기 위해 별도의 스레드에서 운영하는 경우가 많음
6소켓 종료 (close)- Server Socket은 자신의 소켓뿐 아니라, 클라이언트 연결 시 새로 생성된 소켓도 함께 관리 및 종료해야 함
Port 의 역할

네트워크에서 데이터를 교환할 때, 운영체제는 각 프로세스에 고유한 포트 번호를 부여하여 통신 대상을 식별한다. 데이터 전송 시 목적지 포트를 지정하면, 수신 측에서 해당 포트 번호를 가진 프로세스가 데이터를 받아 처리한다.

WebSocket 프로토콜이란


HTTP 프로토콜의 기본 통신 방식

일반적으로 가장 널리 사용되는 HTTP 프로토콜은 요청과 응답으로 구성된 단방향 통신 방식을 따른다. 클라이언트가 요청을 보내면 서버는 이를 처리한 뒤 응답을 반환하며, 응답이 완료되면 클라이언트와 서버 간 연결은 즉시 종료된다.

이러한 구조는 전통적인 웹사이트 구현에 매우 적합하다. 웹페이지를 제공하는 경우, 서버가 클라이언트와 연결을 지속적으로 유지할 필요가 없기 때문이다. 덕분에 서버는 적은 하드웨어 자원으로도 다수의 클라이언트 요청을 효율적으로 처리할 수 있다.

HTTP 의 실시간 통신 제약

HTTP 프로토콜로 실시간 양방향 상호작용 애플리케이션을 구현하려면 두 가지 주요 제약이 존재한다.

1. HTTP 에서는 항상 클라이언트가 연결을 시작한다.

서버는 클라이언트의 요청이 오기 전까지 아무런 데이터를 보낼 수 없으며, 서버에서 먼저 이벤트를 전달하는 것이 불가능하다. 이 때문에 서버 이벤트를 클라이언트에게 알리기 위해 Polling(주기적 요청)을 사용하지만, 이벤트가 드문 경우 불필요한 호출과 리소스 낭비가 발생한다.

2. HTTP 연결이 유지되지 않아 상태나 문맥을 유지하기 어렵다.

HTTP 연결은 요청-응답 후 즉시 종료되는 단방향 연결이므로, 상태나 문맥(Context)을 유지하기 어렵다. 또한 HTTP 헤더의 크기가 크기 때문에, 짧은 메시지를 자주 주고받는 서비스에서는 네트워크 대역폭 낭비가 심해진다.

HTTP 방식의 실시간 통신 시도

WebSocket 이 나오기 전에는 HTTP 를 사용하여 실시간 통신을 구현하려는 시도가 있었다. 하지만 모두 요청/응답 헤더가 불필요하게 크다는 단점이 있었다.

1. HTTP Polling

클라이언트가 1 초, 10 초 등 주기적으로 서버에 요청을 보내 새로운 이벤트를 확인한다.

Notion image
구분내용
장점- 클라이언트와 서버 모두 구현이 간단함
단점- 실시간성이 보장되지 않는다. 메시지가 도착하더라도 다음 요청 주기까지 대기해야 함 - 응답 여부와 무관하게 주기적으로 요청을 보내므로, 서버에 불필요한 부하가 발생
2. HTTP Long Polling

클라이언트가 서버로 요청을 보내고, 이벤트가 발생할 때까지 기다린다. 이벤트가 발생하면 서버가 응답을 보내고, 클라이언트는 즉시 다시 요청을 보내 연결을 유지한다.

Notion image
구분내용
장점- Polling에 비해 요청 횟수가 줄어들어 네트워크 트래픽이 감소함
단점- 이벤트 발생 빈도가 높을 경우, 결과적으로 Polling과 요청량이 큰 차이가 없음 - 여러 클라이언트에서 동시에 이벤트가 발생하면 서버에 부하가 집중될 수 있음
3. HTTP Streaming (SSE)

클라이언트가 서버로 요청을 보내 연결을 맺은 후, 이벤트가 발생해도 연결을 끊지 않는다.

Notion image
구분내용
장점- 서버는 지속적으로 클라이언트로 메시지를 전송 가능
단점- 클라이언트는 스트리밍 중 동일한 TCP 포트에서 읽기와 쓰기를 동시에 수행할 수 없어, 추가 요청을 보낼 수 없음 - 요청을 보내려면 별도의 포트를 사용해야 함 - 클라이언트 측에서 문제가 발생해도, 서버는 그 사실을 즉시 감지하기 어려움

WebSocket

웹소켓은 기존 통신 방식의 한계를 극복하고, 클라이언트와 서버 간 효율적인 실시간 양방향 통신을 제공하는 프로토콜이다. 이를 통해 하나의 서버가 여러 클라이언트와 동시에 데이터를 주고받을 수 있으며, 빠르고 안정적인 실시간 메시지 교환이 가능하다.

WebSocket은 이름에 Socket이 포함되어 있지만, 앞서 설명한 네트워크 소켓(TCP Socket)과는 개념적으로 다르다. WebSocket은 웹 환경에서 두 프로그램이 메시지를 교환하기 위한 응용 계층 프로토콜이며, 브라우저와 서버 간 실시간 통신을 위해 설계되었다.

구분설명
Socket- TCP/IP 프로토콜을 기반으로 동작 - 클라이언트와 서버 간 지속적인 연결을 유지하는 실시간 통신 방식
WebSocket- HTTP 프로토콜을 사용하는 웹 환경에서 실시간 통신을 구현하기 위해 등장한 프로토콜 - 초기에는 HTTP 핸드셰이크를 수행한 후, 연결을 WebSocket 프로토콜로 업그레이드하여 양방향 실시간 통신을 가능하게 함

또한 OSI 7계층 구조에서 TCP는 4계층(전송 계층)에 위치하며, WebSocket은 7계층(응용 계층)에서 HTTP 핸드셰이크 이후 TCP 연결을 업그레이드하여 사용한다. 이 덕분에 WebSocket은 TCP의 순서 보장·재전송 등 신뢰성 있는 특성을 그대로 활용할 수 있다.

웹소켓의 한계

WebSocket은 실시간 양방향 통신을 위한 강력한 기술이지만, 몇 가지 제약과 한계를 가진다.

단점설명
구현 복잡성- HTTP 기반의 Polling, Long Polling, Streaming 방식보다 설계와 구현이 상대적으로 복잡하다.
호환성 문제- WebSocket은 HTML5 이후 등장한 기술이기 때문에, HTML5 이전 환경에서는 적용이 어렵다. - 예를 들어, Internet Explorer는 버전 10 이상부터 WebSocket을 지원한다.
Stateful 특성- WebSocket은 Stateful Protocol로, 클라이언트와 서버가 연결 상태를 지속적으로 유지해야 한다. - 연결을 유지하는 것만으로도 네트워크 및 서버 자원을 소모하므로, 일정한 운영 비용이 발생한다.
비정상 종료 대응 필요- 네트워크 장애나 브라우저 종료 등으로 인해 예기치 않게 연결이 끊어질 수 있으므로, 이에 대한 복구 및 재연결 전략이 반드시 필요하다.
Stateful과 Stateless
  • Stateless: 서버가 클라이언트의 상태 정보를 저장하지 않는다. 각각의 요청은 서로 독립적이며, 요청 1건당 응답 1건만 처리된다.
  • Stateful: 서버가 클라이언트의 상태 정보를 보관한다. 이전 상태가 현재 요청 처리에 영향을 주며, 지속적인 연결 관리가 필요하다.
  • 웹소켓 통신 과정


    핸드셰이크

    WebSocket은 HTTP와 완전히 별개의 프로토콜이 아니라, HTTP를 기반으로 업그레이드되는 형태의 통신이다.

    클라이언트가 WebSocket 연결을 시작할 때, 먼저 서버에 HTTP GET 요청을 보낸다. 이 요청에는 Connection: Upgrade 와 Upgrade: websocket 같은 같은 특수 헤더가 포함된다.

    GET /chat HTTP/1.1
    Host: www.test.com
    Connection: Upgrade
    Upgrade: websocket
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Sec-WebSocket-Version: 13

    서버가 WebSocket을 지원한다면, 다음과 같은 응답을 보내며 프로토콜 업그레이드(HTTP → WebSocket)를 완료한다.

    HTTP/1.1 101 Switching Protocols
    Connection: Upgrade
    Upgrade: websocket
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

    이 과정을 핸드셰이크(Handshake)라고 하며, 완료되는 즉시 클라이언트와 서버는 WebSocket 프로토콜을 이용한 양방향 통신을 시작할 수 있다.

    데이터 전송

    핸드셰이크가 완료되면, 클라이언트와 서버는 메시지를 Frame 단위로 주고받는다. 하나의 메시지는 여러 개의 데이터 조각(Frame)으로 구성될 수 있으며, 브라우저 환경에서는 send() 메서드를 통해 텍스트 또는 바이너리 데이터를 전송할 수 있다.

    Frame 의 종류
  • 텍스트 프레임: 텍스트 데이터가 담긴 프레임
  • 바이너리 데이터 프레임: 바이너리 데이터가 담긴 프레임
  • 핑/퐁 프레임: 커넥션이 유지되는지 확인하기 위한 프레임
  • 연결 종료

    데이터 송수신이 완료되면, 클라이언트와 서버 모두 연결을 종료하기 위한 Frame 을 전송할 수 있다. 연결을 종료하고 싶은 쪽에서 Close Frame 을 보내면, 상대쪽에서 응답으로 Close Frame 을 전송한다. 이렇게 WebSocket 연결이 종료된다.

    데이터 송수신이 완료되면, 클라이언트나 서버 중 어느 한쪽이 Close Frame을 전송해 연결 종료를 요청한다. 상대가 이에 대한 응답으로 Close Frame을 다시 보내면, WebSocket 연결이 완전히 종료된다.

    WebSocket 클라이언트


    브라우저 WebSocket 클라이언트

    대부분의 WebSocket 클라이언트는 JavaScript를 실행할 수 있는 브라우저 환경에서 동작한다. 브라우저에서는 WebSocket 표준 API를 사용해 손쉽게 클라이언트 코드를 작성할 수 있다.

    서버와 통신하려면 WebSocket 클래스를 이용해 객체를 생성하며, ws:// 또는 wss:// 프로토콜을 사용하는 서버 URL을 생성자에 전달한다.

    const socket = new WebSocket("ws://www.test.com/chat");

    객체가 성공적으로 생성되면 핸드셰이크가 완료되고 서버와 메시지 교환이 가능해진다.

    이벤트 처리

    웹소켓 객체로 서버에서 발생하는 4 가지 이벤트 (openmessageerrorclose) 를 처리할 수 있다. 특히 message 이벤트를 통해 서버 메시지를 자주 받는다.

    이벤트 핸들러는 addEventListener 메서드로 설정한다.

    socket.addEventListener("open", (event) => {
      console.log("서버와 연결을 맺었습니다.");
    });
    
    socket.addEventListener("message", (event) => {
      console.log("서버에서 받은 메시지:", event.data);
    });
    
    socket.addEventListener("error", (event) => {
      console.error("에러:", event);
    });
    
    socket.addEventListener("close", (event) => {
      console.log("서버와 연결을 끊었습니다.");
    });

    on<이벤트> 속성에 직접 함수를 할당하는 방법도 있다.

    socket.onopen = (event) => {
      console.log("서버와 연결을 맺었습니다.");
    };
    
    socket.onmessage = (event) => {
      console.log("서버에서 받은 메시지:", event.data);
    };
    
    socket.onerror = (event) => {
      console.error("에러:", event);
    };
    
    socket.onclose = (event) => {
      console.log("서버와 연결을 끊었습니다.");
    };

    서버로 메시지를 보낼 때는 send() 메서드를 사용한다.

    socket.send("하이 웹소켓!");

    브라우저 호환성

    WebSocket이 실시간 양방향 통신의 표준 기술로 자리 잡은 지는 오래되었으며, 현재 대부분의 모던 브라우저에서 WebSocket API를 기본적으로 지원한다.

    그러나 WebSocket을 지원하지 않는 구형 브라우저를 함께 대응해야 하는 경우에는 Socket.IO와 같은 호환성 라이브러리를 사용하는 것이 일반적이다. Socket.IO는 WebSocket이 가능한 환경에서는 WebSocket 방식으로 통신하고, 그렇지 않은 환경에서는 HTTP 기반 통신(Long Polling 등)을 이용해 유사한 실시간 동작을 구현한다.

    웹소켓 한계 극복 라이브러리


    WebSocket의 한계, 특히 HTML5 이전 기술 환경에서 사용할 수 없다는 제약을 보완하기 위해 여러 라이브러리가 등장했다. 그중 대표적인 기술이 Socket.IO이다.

    Socket.IO

    Socket.IONode.js 기반의 실시간 통신 라이브러리로, 브라우저 종류나 기술 지원 여부와 무관하게 일관된 실시간 통신 환경을 제공한다.

    즉, WebSocket을 지원하지 않는 구형 브라우저에서도 HTTP 기반의 대체 통신 방식(Long Polling 등)을 통해 유사한 동작을 구현할 수 있다.

    동작 방식
  • 브라우저가 WebSocket을 지원하는 경우, WebSocket 방식으로 통신
  • WebSocket을 지원하지 않는 경우, HTTP 프로토콜(Long Polling 등)을 사용해 실시간 통신을 흉내냄
  • 특징 및 제약사항
  • 서버는 Node.js 환경을 기본으로 한다. (대체 방안이 있지만 완전하지 않음)
  • 클라이언트는 JavaScript뿐 아니라 Java, C++ 등 다양한 언어를 지원한다.
  • 연결이 끊어져도 자동 재연결 기능이 내장되어 있어 안정적인 통신이 가능하다.
  • 여러 서버로 확장(스케일링)하여 모든 클라이언트에 이벤트를 효율적으로 브로드캐스트할 수 있다.
  • SockJS

    SockJS는 Socket.IO와 마찬가지로 브라우저 종류와 관계없이 실시간 통신을 구현할 수 있도록 설계된 라이브러리이다. 즉, WebSocket을 지원하지 않는 환경에서도 안정적인 통신을 유지할 수 있도록 다양한 메커니즘을 제공한다.

    동작 방식
  • 브라우저가 WebSocket을 지원하는 경우, WebSocket 방식으로 우선 동작
  • WebSocket을 사용할 수 없는 경우, 브라우저별로 대체 전송 프로토콜(Long Polling, Streaming 등)을 사용해 WebSocket과 유사하게 동작
  • Socket.IO 와의 차이점

    Spring Framework에서는 WebSocket 사용 시 구형 브라우저 대응을 위해 SockJS 프로토콜을 채택하고 있다. SockJS 프로토콜은 브라우저 내의 SockJS-client와 서버 측 SockJS-server 간 통신 규약으로, 브라우저 호환성을 최대한 확보하도록 설계되었다.

    따라서 백엔드에서 Spring Framework를 사용한다면, 프론트엔드 또한 SockJS-client를 사용하는 것이 자연스럽고 호환성이 높다.

    STOMP 프로토콜


    STOMP 란?

    STOMP는 이름 그대로 텍스트 기반의 간단한 메시징 프로토콜이다. WebSocket은 단순히 양방향 데이터 전송을 위한 통신 채널만 제공할 뿐, 메시지의 구조나 의미를 규정하지 않는다.

    따라서 WebSocket만으로 채팅 서비스를 구현하려면 다음과 같은 요소를 직접 정의해야 한다. 이러한 불편함을 해결하고 메시지 송수신 과정을 표준화하기 위해 만들어진 것이 STOMP이다.

  • 메시지가 어떤 요청을 의미하는지
  • 어떤 형식(포맷)으로 전달되는지
  • 특정 메시지를 어떻게 처리할지
  • STOMP 의 역할

    STOMP는 WebSocket 위에서 동작하며, 클라이언트와 서버 간 메시지의 형식, 유형, 처리 절차를 정의한다. 덕분에 복잡한 메시지 교환 과정을 단순화하고 효율적으로 관리할 수 있다.

    STOMP 의 장점

  • WebSocket 메시지의 형식을 명확히 정의해 구조화된 통신이 가능하다.
  • 세션 관리 기능을 제공하여 연결 상태를 체계적으로 유지할 수 있다.
  • 발행/구독 패턴을 도입해 메시지 전송 과정을 유연하게 설계할 수 있다.
  • 메시지에 헤더 정보를 추가할 수 있어, 인증 및 메타데이터 관리가 용이하다.
  • STOMP 규칙을 지키면 언어와 플랫폼 간 호환 메시징이 가능하다.
  • STOMP v5부터는 바이너리 데이터 전송을 지원하며, 이전 버전(v4)은 텍스트 기반만 지원한다.
  • StompJS

    StompJS 는 클라이언트 측에서 STOMP 프로토콜을 쉽게 사용할 수 있도록 구현된 JavaScript 라이브러리

    다. 이를 이용하면 브라우저나 프론트엔드 환경에서도 STOMP 기반 WebSocket 통신을 간단히 구현할 수 있다.

    참고자료

  • https://recipes4dev.tistory.com/153#google_vignette
  • https://www.daleseo.com/websocket/
  • https://mingule.tistory.com/60
  • https://www.youtube.com/watch?v=SwLKZUj9urY