[웹소켓] 트러블 슈팅 :Invalid frame header

2025. 2. 20. 22:32·Web/Node.js

TL;DR

node.js에서 하나의 http 서버를 공유하는 여러 웹소켓 서버를 생성하여 발생한 문제 (ws 모듈 사용)

 

문제상황

브라우저에서 웹소켓 연결 시 아래와 같이 Invalid frame header 에러 발생

 

개발자도구의 네트워크 탭을 확인해보니 아래와 같이 websocket으로 업그레이드는 잘 되었다고 나옴

 

원인

기존에 node.js에서 ws모듈을 사용해서 여러 웹소켓 서버를 각각 포트를 할당해서 띄우고 있었는데, 하나의 http 서버를 공유하도록 수정했더니 문제 발생

너무 안일하게 당연히 될 거라고 생각했다.

 

해결방법

아래의 ws 모듈 문서를 확인해보니 대략적으로 이런 내용이 있었다.

- 서버 인스턴스 생성시 포트를 지정하면 http 서버가 자동으로 생성되고 사용됨

- 외부 http서버를 사용하려면 서버를 지정하거나 noServer 옵션을 사용

- noServer 옵션은 웹소켓 서버와 http서버를 분리하기 때문에, 하나의 http서버에 여러 웹소켓 서버를 공유할 수 있음

 

즉, 하나의 http서버에 여러 웹소켓 서버를 공유하려면 noServer옵션으로 웹소켓 서버 인스턴스를 생성해야 함

 

https://github.com/websockets/ws/blob/master/doc/ws.md

Create a new server instance. One and only one of port, server or noServer must be provided or an error is thrown. An HTTP server is automatically created, started, and used if port is set. To use an external HTTP/S server instead, specify only server or noServer. In this case, the HTTP/S server must be started manually. The "noServer" mode allows the WebSocket server to be completely detached from the HTTP/S server. This makes it possible, for example, to share a single HTTP/S server between multiple WebSocket servers.

 

코드

❌ 틀린 코드

const http = require('http');
const WebSocket = require('ws');
const server = http.createServer();

//웹소켓 서버 하나만 열거면 이렇게 사용해도 됨
const wsServerMain = new WebSocket.Server({ server, "/ws" });
const wsServerCam = new WebSocket.Server({ server, "/ws/camera" });

 

아래와 같이 수정

const http = require('http');
const WebSocket = require('ws');
const server = http.createServer();

//noServer 옵션으로 웹소켓 서버 인스턴스 생성
const wsServerMain = new WebSocket.Server({ noServer: true });
const wsServerCam = new WebSocket.Server({ noServer: true });

//http 요청이 upgrade인 경우
server.on('upgrade', (req, socket, head) => {
  //요청 URL에 따라 적절한 웹소켓 서버로 업그레이드 처리
  if (req.url === '/ws') {
    wsServerMain.handleUpgrade(req, socket, head, (ws) => {
      //업그레이드 완료 후 'connection' 이벤트 발생 시킴
      wsServerMain.emit('connection', ws, req);
    });
  } else if (req.url === '/ws/camera') {
    wsServerCam.handleUpgrade(req, socket, head, (ws) => {
      wsServerCam.emit('connection', ws, req);
    });
  } else {
    socket.destroy();
  }
});

 

 

 

------- 이런식으로 사용하면 될듯 --------

//Core.ts
  public start(server: http.Server): void {
    this.websocketServer.init(this);
    this.websocketServerForCamera.init(this);
    
    server.on("upgrade", (req, socket, head) => {
      const url = req.url || "";
      if (url.startsWith("/ws/camera")) {
        this.websocketServerForCamera.handleUpgrade(req, socket, head);
      } else if (url.startsWith("/ws")) {
        this.websocketServer.handleUpgrade(req, socket, head);
      } else {
        socket.destroy();
      }
    });
 ...
  
abstract class BaseWebsocketServer<T extends WsClient = WsClient> {
  protected core: Core;
  protected wss: WebSocket.Server;
  protected clients: T[] = [];

  constructor() {}
  
  init(core: Core) {
    this.core = core;

    this.wss = new WebSocket.Server({ noServer: true });
    this.wss.on("connection", (ws: WebSocket, req: IncomingMessage) => {
      this.logWsClientConnnection(req, "connected");
      const client = this.createClient(ws);
      ws.on("message", (message) => {
        this.onMessage(client, message);
      });
      ws.on("close", () => {
        this.onClose(client);
        this.clients = this.clients.filter((c) => c.ws !== ws);
        this.logWsClientConnnection(req, "disconnected");
      });
      ws.on("error", (err) => {
        logger.error(`[ws] error on ws: ${err}`);
      });
    });
  }

  handleUpgrade(req: IncomingMessage, socket: internal.Duplex, head: Buffer) {
    this.wss.handleUpgrade(req, socket, head, (ws) => {
      this.wss.emit("connection", ws, req);
    });
  }
  ...

 

여담

node 서버를 https -> http로 변경하면서 웹소켓도 wss에서 ws 프로토콜로 변경하게 되었다.

그리고 nginx를 도입하였고

웹소켓 서버를 여러 개 띄워야 하는 요구사항이 생겨서 추상화까지 해버렸다.

당연히 잘 돌겠지~ 하면서 프론트엔드 수정하다보니 웹소켓 연결이 안돼서 원인을 찾는데 시간이 조금 걸렸다....

너무 안일하게 생각했다....

 

'Web > Node.js' 카테고리의 다른 글

[Docker] node:alpine에서 bcrypt 사용 시 앱이 죽는 문제 해결  (0) 2025.05.21
Nest.js vs Spring에 대한 생각  (0) 2025.03.25
'Web/Node.js' 카테고리의 다른 글
  • [Docker] node:alpine에서 bcrypt 사용 시 앱이 죽는 문제 해결
  • Nest.js vs Spring에 대한 생각
Danny.Song
Danny.Song
  • Danny.Song
    일기장
    Danny.Song
  • 전체
    오늘
    어제
    • 분류 전체보기 (65)
      • Web (13)
        • React.js (2)
        • Next.js (1)
        • Node.js (3)
        • Web 전반 (7)
      • Unity Robotics (8)
      • ROS2 (7)
      • SW 이것저것 (3)
      • 일상 (2)
      • etc (4)
        • 프로젝트 (1)
        • 주식일기 (1)
        • Unity (1)
        • 하루 계획 (0)
        • 배움 (0)
        • 소프트웨어공학 (0)
        • 운영체제 (1)
        • etc... (0)
      • Java (26)
        • 프로젝트 (8)
        • cmd에서 자바실행 (2)
        • 통신 (3)
        • xml (6)
        • Database (3)
        • 코딩테스트 (0)
        • 디자인패턴 (1)
        • 기타 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    self signed ssl 인증서 발급
    ros2 qos
    Dom
    nav2
    goal_checker
    지뢰찾기
    날짜 관련
    Unity
    decontruct
    node-alpine
    Schema
    telegram chatbot
    pointcloud_to_laserscan
    ai navigation
    ros2
    invalid frame header
    java
    XML Schema
    minesweeper
    amcl
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Danny.Song
[웹소켓] 트러블 슈팅 :Invalid frame header
상단으로

티스토리툴바