Websocket 연결 실패 문제 해결

이영섭·2025년 6월 9일

bovo 프로젝트

목록 보기
7/10

문제의 발단

이번에는 고질적인 문제였던 토론방(채팅방) 입장시 WebSocket 연결 실패 문제를 해결했다. 해당 문제는 토론방 입장은 가능했으나 브라우저 콘솔 창에서 WebSocket 연결에 실패했다는 에러 문구가 출력되었다.

  • WebSocket 연결 실패 에러 출력 화면
    websocket 연결 실패 에러 문구

분명 ngrok으로 서버를 테스트 배포한 상황에서는 별 문제없이 채팅방이 작동했으나, 서버에서 AWS EC2로 실배포한 상태에서는 이러한 에러가 출력되었다.
물론 독서토론방에서 메시지 보내기나 독서 기록 공유 모달 버튼을 클릭해도 작동하지 않는 문제도 있었으나 이러한 문제의 근본적인 원인은 Websocket 연결 실패에 따른 결과라고 생각했다.

문제의 원인

자료 조사를 하다보니 문제의 원인은 Websocket 연결시 생성되는 주소에서 비롯된다는 것을 깨달을 수 있었다.

WebSocket

이 원인을 이해하기 위해서는 HTTP/HTTPS와 WebSocket URL 연결되는 규칙을 이해할 필요가 있다. 웹소켓(WebSocket)을 사용하면 TCP(Transmission Control Protocol)를 기반으로 서버와 브라우저 간 연결을 유지한 상태로 데이터를 교환할 수 있다. 이때 데이터는 ‘패킷(packet)’ 형태로 전달되며, 전송은 연결 중단과 추가 HTTP 요청 없이 양방향으로 이뤄진다.

패킷이란?
네트워크 통신에서 데이터를 작은 조각으로 나눠서 전송하는 단위를 말한다.

이 양방향 통신을 하기 위해서는 Websocket connection을 만들어야 하고, 이를 위해서는 new Websocket을 사용해야 하는데 이때 ws라는 프로토콜을 사용한다.
ws말고도 wss란 것도 존재하는데 이는 http와 https 관계와 유사하다.

RFC 6455 명세서 및 문제의 원인

wswss는 RFC 6455 명세서에 정의된 프로토콜 규칙을 따르는데 해당 내용이 너무 길어, 문제의 원인이 되는 부분만 발췌하면

  • 인용문구
  1. WebSocket URIs
    This specification defines two URI schemes, using the ABNF syntax
    defined in RFC 5234 [RFC5234], and terminology and ABNF productions defined by the URI specification RFC 3986 [RFC3986].
          ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
          wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]

          host = <host, defined in [RFC3986], Section 3.2.2>
          port = <port, defined in [RFC3986], Section 3.2.3>
          path = <path-abempty, defined in [RFC3986], Section 3.3>
          query = <query, defined in [RFC3986], Section 3.4>

The port component is OPTIONAL; the default for "ws" is port 80, while the default for "wss" is port 443.

The URI is called "secure" (and it is said that "the secure flag is set") if the scheme component matches "wss" case-insensitively.

The "resource-name" (also known as /resource name/ in Section 4.1)
can be constructed by concatenating the following:

o "/" if the path component is empty

o the path component

o "?" if the query component is non-empty

o the query component

Fragment identifiers are meaningless in the context of WebSocket URIs and MUST NOT be used on these URIs. As with any URI scheme, the character "#", when not indicating the start of a fragment, MUST be escaped as %23.

참조 사이트
RFC 6455

이 명세서를 기반으로 분석하면 ws스키마는 HTTP의 80 포트와 유사하고, wss 스키마는 HTTPS의 443 포트와 유사하며 wss가 보안 연결(TLS/SSL)이라는 것을 알 수 있다. 따라서 브라우저는 현재 페이지가 http://로 시작한다면, WebSocket 연결 시도 역시 ws:// 스키마를 사용하는 것을 선호한다. 반면 브라우저는 현재 페이지가 https://로 시작한다면, WebSocket 연결 시도 역시 wss:// 스키마를 사용하도록 강제하는데 이는 ws를 사용하게 되면 Mixed Content error를 발생시키기 때문이다.

즉, 쉽게 풀자면 ngrok으로 서버를 테스트 배포했을 당시의 브라우저 주소는 https로 시작하였기 때문에 wss 주소를 사용하여 api 경로를 wss url로 사용해도 채팅방 연결에 성공하였으나, 서버를 AWS EC2로 실배포하였을 때는 http로 시작하는 브라우저 주소를 사용하였기에 기존에 api 경로를 유지했던 wss 사용시 port 번호도 일치하지 않았기에 에러를 발생시키는 것이다.

해결 방법??

해결 방법은 간단하다. 서버를 AWS EC2로 배포하면서 http로 시작하는 api 경로로 변경되었기 때문에 이에 따라 api 요청경로를 wss가 아닌 ws로 시작하는 주소로 변경하면 해결된다.

다만 화면을 테스트 배포 플랫폼으로 netlify를 사용하였기 때문에 이 netlify도 활용하기 위해서는 toml파일을 변경해야 했다.(Netlify는 https로 시작하는 주소를 지녔기 때문에 서버의 http 주소를 사용하면 Mixed content 에러가 발생되며 이에 따라 Netlify 자체를 proxy 서버처럼 운용해야 되서 toml 파일 설정을 해주었다.)

  • netlify.toml
# WebSocket 프록시 추가
# 프론트엔드는 wss://your-netlify-app.netlify.app/ws-chat 으로 요청을 보내고,
# Netlify는 이 요청을 백엔드 ws://서버주소/ws-chat 으로 전달합니다.
[[redirects]]
  from = "/ws-chat/*" # 프론트엔드에서 사용할 WebSocket 경로 (예: /ws-chat)
  to = "ws://서버 주소/:splat" # 실제 백엔드 WebSocket 서버 주소
  status = 200
  force = true
profile
신입 개발자 지망생

0개의 댓글