본문 바로가기

FE

웹 소켓 (Web Socket) 제대로 이해하기

최근 진행하는 프로젝트에서, 발표자와 청중 사이에 pdf 페이지 동기화 및 청중 반응 전송을 구현하기 위해 웹소켓을 사용하고있습니다. 웹소켓 개념만 아는 상태였는데 더 깊게 알아보고자 글을 작성합니다


1) 웹소켓이란?

웹소켓은 브라우저와 서버가 한 번 연결(Handshake)하면 그 연결을 계속 유지하면서, 양쪽 모두가 자유롭게 메시지를 주고받을 수 있게 해주는 프로토콜입니다.

HTTP처럼 “요청-응답” 한 번 하고 끝나는 게 아니라, 전화 통화처럼 계속 이어집니다.
  • URL 스킴: http:// 대신 ws:// 또는 보안 연결인 wss://를 사용합니다.
    • ws:// 와 wss://: 의 차이 
      • ws://: 암호화되지 않은 일반적인 웹소켓 연결입니다. (http://와 비슷!!)
      • wss://: 데이터가 암호화되어 전송되는 보안 웹소켓 연결입니다. (https://와 비슷!!)
  • 연결 과정: 처음엔 HTTP로 웹소켓으로 소통할 것을 요청하고, 서버가 수락하면 웹소켓 전용 통로로 바뀝니다.
  • 최고의 장점: 실시간 소통이 가능하고 , 불필요한 요청을 줄여 네트워크와 서버의 부담을 낮춰줍니다.

 

  • SSE? SSE(Server-Sent Events)도 실시간 기술이지만, 서버가 클라이언트에게 일방적으로 데이터만 보낼 수 있습니다. 채팅이나 문서 동기화처럼 서로 데이터를 주고받아야 하는 양방향 통신에는 웹소켓이 훨씬 적합합니다.

2) 웹소켓이 쓰이는 곳

  • 실시간 슬라이드/문서 동기화 (발표자 페이지 변경 → 청중 화면 즉시 반영)
  • 채팅/메신저, 게임 상태 동기화, 증권사나 코인 거래소 서버에서 실시간으로 변하는 가격, 거래량 등의 데이터를 사용자에게 전달할 때 사용 
  • 협업 도구(편집 이벤트 공유), 알림/대시보드 실시간 업데이트

 

이번 서비스도 발표자의 PDF 슬라이드를 청중과 동기화하는 데 웹소켓을 사용합니다.

아래 연결 방법 의 예를 코드로 살펴보겠습니다. 

 

3) 연결 방법 _예시

3-1) 브라우저(클라이언트)

const ws = new WebSocket("wss://your-domain.com/ws"); 
ws.onopen = () => { console.log("connected"); 
ws.send(JSON.stringify({ type: "hello", payload: "안녕하세요!" })); }; 

ws.onmessage = (e) => { const msg = JSON.parse(e.data); console.log("message:", msg); };

ws.onerror = (e) => console.error("ws error:", e);
ws.onclose = (e) => console.log("closed:", e.code, e.reason);
 
 
  • 보내기: 문자열/바이너리 가능. 보통 JSON으로 규약을 만듭니다.
  • 받기: e.data 파싱 후 type 기반으로 분기

4) 서비스에 적용: 실시간 슬라이드 동기화

 

  • 발표자: PDF 페이지를 넘기면 서버에 slide_update(page) 전송
  • 서버: 같은 방 의 모든 청중에게 브로드캐스트
  • 청중: 메시지 수신 → PDF를 해당 페이지로 이동

4-1) 메시지 형식

{
  "type": "join | slide_update | reaction | question",
  "roomId": "abc123",
  "role": "presenter | audience",
  "payload": { "page": 7 }
}
 
 

4-2) 발표자 : 페이지 바뀌면 전송

// presenter.js
const roomId = new URLSearchParams(location.search).get("room") || "demo";
const ws = new WebSocket("wss://your-domain.com/ws");

ws.onopen = () => {
  ws.send(JSON.stringify({ type: "join", roomId, role: "presenter" }));
};

function onSlideChange(page) {
  ws.send(JSON.stringify({
    type: "slide_update",
    roomId,
    role: "presenter",
    payload: { page }
  }));
}


document.getElementById("next").addEventListener("click", () => {
  const nextPage = getCurrentPage() + 1;
  goToPage(nextPage);   
  onSlideChange(nextPage); // 모든 청중에게 알림
});
 

4-3) 청중 : 메시지 수신 → 페이지 이동

// audience.js
const roomId = new URLSearchParams(location.search).get("room") || "demo";
const ws = new WebSocket("wss://your-domain.com/ws");

ws.onopen = () => {
  ws.send(JSON.stringify({ type: "join", roomId, role: "audience" }));
};

ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (msg.type === "slide_update") {
    const page = msg.payload.page;
    goToPage(page); // 발표자 페이지와 동기화
  }
};


document.querySelectorAll("[data-emoji]").forEach(btn => {
  btn.addEventListener("click", () => {
    ws.send(JSON.stringify({
      type: "reaction",
      roomId,
      role: "audience",
      payload: { emoji: btn.dataset.emoji } 
    }));
  });
});
 

5) 웹소켓의 단점과 보완점

양방향 통신을 할 때 큰 장점을 가지고 있는 웹소켓은 어떤 단점이 있는지 알아보겠습니다.

단점 1) 서버 비용

  • 많은 사용자가 동시에 접속하면, 서버는 그 모든 연결을 계속 유지해야 합니다. 그 결과 서버의 메모리 사용량과 열려있는 소켓 수가 늘어나면서 부하가 커지고 비용도 증가하게 됩니다.

 

이 문제를 해결하기 위한 두가지 방법

  • 연결 정리하기 : 가장 먼저 안쓰이는 연결들을 주기적으로 정리해야 합니다. 사용자가 브라우저를 그냥 닫는 등 비정상적으로 접속을 종료하면 서버가 모를 수 있기 때문에 주기적으로 서버가 클라이언트에 잘있는지 확인하기위해 신호를 보내고, 클라이언트가 응답합니다. 만약 정해진 시간 안에 응답이 없다면, 그 연결은 끊어진 것으로 알고 정리하여 불필요한 자원 낭비를 막습니다.
  • 서버 확장 및 메시지 관리: 사용자가 더 많아지면 서버 간의 메시지를 원활하게 주고받기 위해 Redis의 Pub/Sub 같은 메시지 브로커를 활용하여 모든 사용자가 같은 정보를 공유하게 만들 수 있습니다. 또한, 한 번에 너무 많은 메시지가 몰려 서버가 다운되는 것을 막기 위해 메시지 전송량을 제한하는 방식도 있습니다.
setInterval(() => {
  wss.clients.forEach((ws) => {
    if (ws.isAlive === false) return ws.terminate();
    ws.isAlive = false;
    ws.ping();
  });
}, 30000);

wss.on("connection", (ws) => {
  ws.isAlive = true;
  ws.on("pong", () => (ws.isAlive = true));
});
 

단점 2) 불안정한 네트워크 환경

특히 모바일이나 와이파이 환경에서는 네트워크가 불안정해서 연결이 끊기는 경우가 많습니다.

 

이 문제를 해결하기 위한 두가지 방법

  • 자동 재연결 로직: 클라이언트 측에서 연결이 끊기면 무작정 재연결을 시도하는 게 아니라, 점차 대기 시간을 늘려서 재연결을 시도하는 로직을 구현합니다. 예를 들어, 처음엔 1초 뒤, 그다음엔 2초, 4초 이런 식으로 서버에 부담을 주지 않으면서 안정적으로 재연결을 하는것이다.
  • 상태 복구 및 동기화:  연결이 복구되었을 때 기존의 상태를 다시 동기화해주는 과정이 필요합니다. 예를 들어, 발표 자료 서비스라면 현재 보고 있던 슬라이드 번호를 서버에 다시 요청해서 받아와야합니다.
function connect(url, onMessage) {
  let ws, retry = 0;
  const open = () => {
    ws = new WebSocket(url);
    ws.onopen = () => { retry = 0; };
    ws.onmessage = (e) => onMessage(JSON.parse(e.data));
    ws.onclose = () => setTimeout(open, Math.min(1000 * 2 ** retry++, 15000));
    ws.onerror = () => ws.close();
  };
  open();
  return { send: (d) => ws?.readyState === 1 && ws.send(JSON.stringify(d)) };
}
 

단점 3) 보안 이슈

웹소켓 연결은 인증 절차 없이 열어두면 아무나 서버에 접속해서 악의적인 메시지를 보낼 수 있기 때문에 보안에 매우 취약합니다.

따라서 다음과 같은 여러 단계의 보안 장치를 마련해야 합니다.

  • 기본적인 보안 강화: 우선 데이터가 중간에 노출되지 않도록 암호화된 WSS 프로토콜을 사용하는 것이 중요합니다. 그리고, 연결을 시작할 때 세션 토큰을 검증해서 허용된 사용자인지 확인하고, 허용된 사이트에서 온 요청인지 확인하는 절차가 필요합니다.
  • 사용자 역할에 따라 권한을 세밀하게 제어: 예를 들면 발표자 역할을 가진 사용자만 슬라이드를 넘기는 메시지를 보낼 수 있도록 제한하도록 하는것입니다. 또한, 특정 채팅방이나 세션에 참여할 권한이 있는지도 확인하고, 필요하다면 관리자가 특정 사용자를 강제로 연결을 끊는 기능도 구현해야 안전한 서비스를 만들 수 있습니다.

마치며

실시간 통신을 할 때 쓰이는 웹소켓에 대한 글이었습니다

 

 

'FE' 카테고리의 다른 글

레이아웃 스래싱(Layout Thrashing)  (0) 2025.10.26
자바스크립트 기초: 객체, DOM, 이벤트 핸들링  (0) 2025.10.18
CSS 심화  (0) 2025.09.29
HTML, CSS 심화  (0) 2025.09.28
커스텀 훅 (Custom Hooks)  (0) 2025.09.27