웹에서 실시간 데이터를 처리하는 방법

2025. 3. 4. 18:05CS/네트워크

실시간 데이터란 ?

실시간 데이터란 무엇일까요? 다들아시겠지만 다시한번 정의를 봐봅시다.

 

실시간 이라는 단어의 뜻은 실제 흐르는 시간과 같은 시간. 이라고 합니다.

 

그렇다면 실시간 데이터는 왜 필요할까요 ? 어떤 부분에서 사용될까요 ?

 

 

실시간 데이터가 쓰이는 곳

사실 컴퓨터와 가까운 우리라면 공감할만한 예시를 들고왔습니다.

 

예를들어 총을 쏘는 게임을 하고 있다고 생각해봅시다.

 

 

그러면 나는 상대방의 위치를 정확하게 알고 있고 상대방에게 총을 쏘면 상대방은 총을 맞아야 합니다. 이게 우리가 원하는 실시간 데이터 입니다.

 

 

 

만약 데이터가 실시간으로 처리되지 않는다면 상대방은 죽지 않고 나만 죽는 불상사가 발생하게 됩니다.

 

또 예를 들어 보겠습니다. 이번엔 한국 주식을 예로 들겠습니다.

 

 

한국 주식을 보고 있으면 회사의 가치가 1초마다 오르락 내리락 하고 있는 것을 알 수 있습니다.

 

주식 어플을 이용하는 사용자들은 최신정보를 초당 아니 어쩌면 밀리 초 단위로 보기를 원할겁니다.

 

본인들의 재산이 걸려 있으니 우리들은 예민하게 주가 그래프를 쳐다보고 있겠죠.

 

 

 

만약 실제 금액과 보이는 주가창의 금액이 다르게 표시되고 있다면 우리들은 아주 화가 나고 타이밍을 놓쳐 매도 및 매수를 하지 못한다면 막대한 손실을 입을 수도 있을것 같습니다.

 

진짜 아찔하죠 ?

 

실시간 데이터처리는 사람의 정신건강에 영향을 미칠정도로 아주 중요한 작업이 될것 같습니다. 그렇다면 웹에서 실시간 데이터를 처리하려면 어떻게 해야할까요 ?

 

 

어떻게 실시간으로 데이터를 보여줄까 ?

웹에서 쓰이는 단방향 HTTP프로토콜에서 생각해봅시다.

 

클라이언트는 서버에게 요청을 보낼 수 있습니다. 그리고 서버는 클라이언트에게 응답을 줄 수 있죠

 

 

응답에서 데이터를 처리해서 클라이언트에 보여주는 역할을 합니다.

 

그러면 실시간으로 데이터를 받아오려면 요청을 계속 날리면 어떻게 해야할까요 ?

 

예를 들어 1초마다 데이터가 바뀌어야 하는 상황이면 어떨까요 ?

 

 

일정한 주기로 요청하기 : Polling 방식

 

  • 클라이언트에서 요청을 합니다.
  • 서버에서 응답을 받습니다.
  • 이 동작을 일정한 주기로 (100ms) 마다 반복합니다.

이를 polling 요청이라고 합니다.

 

구현 하는 방법

  • 일정한 주기를 가지는 타이머 함수를 이용해서 구현할 수 있습니다.
    setInterval(()=> {
      fetch("실시간 가격정보");
    }, []);

장점

  • 일정한 주기로 클라이언트가 요청합니다.
  • 구현이 쉽고 실시간 처리와 비슷한 효과를 얻습니다.
  • 데이터 변동성이 높은 경우 선택하면 좋을것 같습니다.

 

단점

  • 클라이언트가 많아지면 서버의 부담이 선형적으로 증가합니다.
  • 진짜 실시간이라고 말하기는 어렵습니다.
  • 불필요한 요청이 많아 서버 부하가 늘어날 수 있고, 이로 인해 HTTP 오버헤드가 발생할 수도 있습니다.

 

 

그렇다면 어떻게 polling을 개선시키고 실시간 처럼 데이터를 가져올 수 있을까요 ?

 

Polling 방식 개선하기 : Long Polling 방식

기존의 polling은 1요청 -> 1응답으로 클라이언트에서 요청이 오면 무조건 응답을 보내줬습니다.

 

그러면 어떻게 이를 개선 시킬 수 있을까요 ?

 

바로 응답을 보내줄 필요가 있을 때만 응답을 보내 비용을 줄이는 방법입니다.

  1. 클라이언트가 (구독) 요청을 보냅니다.
  2. 클라이언트 - 서버는 (connection) 상태로 계속 대기합니다.
  3. 이벤트가 발생하면
  4. 응답을 보냅니다.
  5. 그리고 클라이언트는 바로 요청을 보냅니다.

이를 Long Polling 요청이라 합니다.

 

구현 하는 방법

async function subscribe() {
  let response = await fetch("/subscribe");

  if ("timeout error") {
    await subscribe();
  } else if ("error") {
    showMessage(response.statusText);
    // 1초 대기 후 다시 연결
    await new Promise(resolve => setTimeout(resolve, 1000));
    await subscribe();
  } else {
    // 응답 메시지를 가져옴
    let message = await response.text();
    showMessage(message);
    // 응답이 오면 다시 구독
    await subscribe();
  }
}

subscribe();

 

장점

  • 항상 연결이 유지 되어 있습니다.
  • 변경에 매우 민감하게 반응한다. 사실상 실시간으로 통신이 가능하다.
  • HTTP 오버헤드가 개선됐습니다.
  • 마찬가지로 구현이 쉽습니다.
  • 데이터 변동성이 적은 경우 유리합니다.

 

단점

  • 폴링 방식과 비슷한 단점을 가지고 있습니다.
  • 클라이언트가 많아지면 서버의 부하가 늘어납니다.

 

클라이언트만 요청을 보내야 할까요 ? 서버에서 데이터를 보내는것이 가능한 방법이 있습니다.

 

 

SSE : sever-sent-event 서버에서도 주도적으로 송신 가능하다 !

 

MDN의 설명

Server-Sent Events 방식으로 웹페이지의 요청 없이도 언제든지 서버가 새로운 데이터를 보내는 것이 가능합니다. 이렇게 보내진 메시지는 웹페이지 안에서 _Events + 데이터_로 다룰 수 있습니다.

 

  • 클라이언트는 서버와 커넥션을 맺습니다.
  • 서버는 메세지를 클라이언트에게 보낼 수 있습니다.
  • 클라이언트는 실시간으로 서버의 메세지를 수신합니다.

구현하는 방법
https://developer.mozilla.org/ko/docs/Web/API/Server-sent_events/Using_server-sent_events

  • EvnetSouce 인스턴스를 사용하여 구현이 가능합니다.

특징

  • HTTP Keep-Alive를 통해 커넥션을 계속해서 연결합니다.
  • utf-8인코딩, 텍스트 데이터의 스트림 사용하여 메시지를 수신합니다.
  • 클라이언트에서는 연결 요청과 수신을, 서버에서만 데이터를 보낼 수 있습니다.
  • 이 때 서버 측 스크립트는 MIME 타입 text/event-stream을 이용해서 송신해야합니다.

 

장점

  • 서버에서 데이터를 수신하여 실시간 데이터 처리가 가능해집니다.
  • utf-8 이벤트 스트림을 다루기 때문에 텍스트의 데이터를 다룰 때 유리합니다.
  • SSE는 웹소켓에 비해 구현이 간단합니다.
  • HTTP 프로토콜을 사용하기 때문에 HTTP2와 호환이 잘됩니다.

 

단점

  • 서버에서 송신하는 단방향 데이터 스트림입니다.
  • HTTP/1을 사용하는 경우 연결의 제한이 생깁니다.

 

 


 

이쯤 되면 진짜 실시간데이터 처리를 할 수 있을것 같은데요 ? 하지만 아직까지는 서버에서만 데이터를 받을 수 있고, 텍스트로만 통신할 수 있다는 한계가 있습니다. 

 

최종적으로 양방향 통신이 가능한 프로토콜이 나왔습니다 !!

 

WebSocket : 우리는 양방향으로 주고 받는데 ?

 

웹소켓은 서버와 클라이언트 간의 양방향 통신 채널을 제공합니다. 즉 서버에서도 송수신이 가능 클라이언트에서도 송수신이 가능합니다 !

 

RFC : 6455

TCP와 WebSocket API을 결합하여 양방향 통신을 제공합니다. 이것이 WebSocket 프로토콜입니다.

        GET /chat HTTP/1.1
        Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
        Origin: http://example.com
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13

 

HTTP 헤더에 Upgrade: websocket , Connection: Upgrade 등 웹 소켓에 관한 헤더를 같이 보내면 Opening Handshake 시작됩니다.

 

  1. 연결 수립
  • 클라이언트가 HTTP Upgrade 요청을 통해 WebSocket 연결을 요청합니다.
  • 서버가 이를 수락하면 HTTP에서 WebSocket 프로토콜로 전환됩니다.

 

 

  1. 핸드셰이크
  • 클라이언트는 Sec-WebSocket-Key 헤더를 포함한 요청을 보냅니다.
  • 서버는 이 키를 사용해 Sec-WebSocket-Accept 헤더를 생성하여 응답합니다.

 

 

  1. 전이중 통신
  • 연결이 수립되면 클라이언트와 서버 모두 언제든 메시지를 주고받을 수 있습니다.

 

 

  1. 연결 종료
  • 정상적인 종료를 위한 closing handshake를 정의합니다.

 

 

구현하는 방법
https://developer.mozilla.org/ko/docs/Web/API/WebSocket

  • 웹 소켓 객체 WebSocket 를 사용해서 구현할 수 있습니다.

 

특징

  • WS 프로토콜을 사용합니다 (ws://) > 한 번 연결이 수립되면 계속 유지되어 실시간 데이터 교환이 가능합니다.
  • 데이터는 텍스트(UTF-8), 바이너리, 컨트롤 프레임 등 여러 타입으로 전송가능합니다.

 

장점

  • HTTP polling에 비해 효율적으로 서버 리소스와 대역폭을 효율적으로 사용합니다.
  • HTTP에 비해 훨씬 적은 오버헤드 (2바이트)로 통신이 가능합니다.
  • 실시간성: 서버에서 클라이언트로 즉시 데이터를 푸시할 수 있어 실시간 애플리케이션에 적합합니다.

 

단점

  • 소켓은 계속 서버랑 계속 handshake를 하고 있습니다. 따라서 기기의 전력이나 네트워크를 일부분 차지 하고 있습니다.
  • 복잡성: HTTP에 비해 구현과 관리가 더 복잡할 수 있습니다.
  • 브라우저 지원 문제: 일부 구형 브라우저에서는 지원되지 않을 수 있습니다.

sse와 Web Socket 비교 하기

 

SSE는 서버에서만 데이터를 전송할 수 있는 특징을 지녔고, WebSocket은 양방향으로 송수신이 가능하다는 특징이 있습니다.

특징을 잘 이해 하고 각 사용처에서 왜 사용되는지 생각해보는 과정을 거쳐보면 재밌는 주제가 될것 같네요.

 

SSE 사용처 WebSocket 사용처
실시간 뉴스 피드 실시간 채팅 애플리케이션
주식 시세 업데이트 온라인 멀티플레이어 게임
소셜 미디어 피드 협업 도구 (실시간 문서 편집)
실시간 알림 시스템 IoT 디바이스 실시간 모니터링 및 제어
스포츠 경기 실시간 점수 업데이트 실시간 화상 통화
시스템 모니터링 대시보드 실시간 데이터 시각화

 

참고

https://developer.mozilla.org/ko/docs/Web/API/Server-sent_events
https://ko.javascript.info/long-polling
http://velog.io/@melt/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-Long-Polling
https://developer.mozilla.org/ko/docs/Web/API/Server-sent_events/Using_server-sent_events
https://developer.mozilla.org/ko/docs/Web/API/WebSocket
https://f-lab.kr/insight/real-time-data-processing-in-web-development
https://surviveasdev.tistory.com/entry/%EC%9B%B9%EC%86%8C%EC%BC%93-%EA%B3%BC-SSEServer-Sent-Event-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B3%A0-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0
https://datatracker.ietf.org/doc/html/rfc6455