네트워크

[Network, TroubleShooting]Websocket은 무엇일까?

Jay_J 2024. 6. 14. 06:48

학부 연구 프로젝트를 진행하던 중, 다른 오류를 찾으려고 개발자 도구를 열어보니 아래와 같이 되어있었다.

나는 웹 소켓이 뭔지도 모르고, 이런 오류가 왜 발생했는지도 모르고, 애플리케이션은 아무런 오류 없이 원하던 대로 동작하니 사실 신경을 쓰지 않으려고 했다. 하지만 계속 보이는 게 거슬렸고, 내가 여기서 또 그냥 GPT한테 고쳐달라고 하고 넘어가면 될 일이지만, 항상 배우는 자세로 프로젝트에 임하기로 했기 때문에 오류가 뜬 김에(이 오류가 안 떴다면 난 웹 소켓이 뭔지 모르고 지나갔을 것이다. 운영체제 강의에서 잠깐 보고 넘어갔으나, 정확히 공부하고 넘어가지 않아 다시 한번 알아보기로 했다.

그렇다면,

 웹 소켓은 무엇일까?

웹 소켓(WebSocket)은 HTML5에 추가된 기술로, 웹 브라우저와 서버 간에 양방향 통신을 가능하게 하는 프로토콜이다. 일반적으로 HTTP 요청은 클라이언트가 요청을 보내고 서버가 응답하는 일방적인 통신 방식이지만, 웹 소켓을 사용하면 클라이언트와 서버가 자유롭게 메시지를 주고받을 수 있다. 이를 통해 실시간 데이터 전송이 필요할 때 매우 유용하게 사용할 수 있다.

웹 소켓의 특징

1. 양방향 통신:
   - 클라이언트와 서버가 양방향으로 데이터를 주고받을 수 있다. 즉, 클라이언트는 언제든지 서버에 데이터를 보낼 수 있고, 서버도 언제든지 클라이언트에 데이터를 보낼 수 있다.


2. 실시간 데이터 전송:
   - 주식 거래, 채팅 애플리케이션, 온라인 게임 등 실시간으로 데이터 전송이 필요한 애플리케이션에 적합하다.

3. 소켓 연결 유지:
   - 초기 연결이 설정된 후에는 클라이언트와 서버 간의 연결이 지속적으로 유지된다. 이를 통해 지속적으로 데이터 교환이 가능하다. 기본적으로 클라이언트 - 서버간의 통신은 HTTP프로토콜을 이용해 요청을 보내고 응답을 받는 식으로 서버와 원하는 데이터를 주고 받는다. 하지만, 지속적인 통신이 필요할때는(게임 채팅창, 주식 전광판), 이러한 요청 - 응답 조차 cumbersome하다. 그래서 websocket을 열어 양방향 통신이 실시간 가능하게한 연결 통로를 열어주는것이다.

4. 효율성:
   - HTTP 요청과 비교하여 오버헤드가 적다. 지속적인 연결을 유지하므로 매번 새로운 연결을 설정하는 비용이 없다.

웹 소켓의 사용 사례

1. 채팅 애플리케이션:
   - 사용자가 실시간으로 메시지를 주고받을 수 있는 채팅 애플리케이션에 적합하다.

2. 실시간 알림:
   - 이메일, 소셜 미디어, 뉴스 등에서 실시간 알림을 사용자에게 전달하는 데 사용된다.

3. 온라인 게임:
   - 게임 서버와 클라이언트 간의 실시간 통신을 통해 빠른 응답이 필요한 온라인 게임에 사용된다.

4. 실시간 데이터 피드:
   - 주식 가격, 스포츠 경기 점수, 날씨 정보 등 실시간 데이터를 제공하는 애플리케이션에 사용된다.

웹 소켓의 동작 원리

1. 연결 설정:
   - 클라이언트는 웹 소켓 서버에 연결을 요청한다. 이 요청은 일반적인 HTTP 요청을 통해 이루어지며, 업그레이드 헤더를 통해 웹 소켓 프로토콜로 전환을 요청한다.

2. 연결 수락:
   - 서버가 연결 요청을 수락하면, HTTP 연결이 웹 소켓 연결로 업그레이드된다.

3. 양방향 통신 시작:
   - 연결이 설정되면, 클라이언트와 서버는 자유롭게 메시지를 주고받을 수 있다.

4. 연결 종료:
   - 연결이 더 이상 필요하지 않거나, 클라이언트 또는 서버가 연결을 종료하고자 할 때, 연결을 종료할 수 있다.

아래는 동작을 나타낸 그림이다.

 

 

핸드셰이크 (Handshake):
브라우저: 웹소켓 연결을 시작하기 위해 브라우저는 HTTP 요청을 보낸다. 이 요청은 웹소켓 프로토콜로 전환하기 위한 업그레이드 요청을 포함한다. (HTTP Upgrade)
서버: 서버는 이 요청을 받고, 웹소켓 프로토콜로 전환할 준비가 되었다는 의미로 HTTP 101 상태 코드(프로토콜 스위칭)를 응답한다. 이를 통해 연결이 설정된다.


양방향 통신 (Bidirectional Communication):

1. 웹소켓 프레임 (WebSocket Frame): 

핸드셰이크가 성공적으로 이루어진 후, 브라우저와 서버 간에는 웹소켓 프레임을 통해 양방향으로 데이터를 주고받을 수 있다. 이 상태에서는 서버와 클라이언트가 자유롭게 데이터를 송수신할 수 있다.


2. 지속적인 통신: 양방향 통신은 연결이 유지되는 동안 계속된다. 실시간 채팅, 게임, 주식 가격 업데이트와 같은 실시간 애플리케이션에서 주로 사용된다.


통신 종료 (Connection Close):
한쪽에서 통신 종료 요청: 통신이 끝나면 브라우저나 서버 중 한쪽에서 연결 종료 요청을 보낸다. 그러면 다른 쪽에서도 연결 종료 응답을 보내고, 연결이 종료된다.

 

※TroubleShooting

자 그럼, 다시 본론으로 돌아와 내 애플리케이션에 왜 WebSocket connection failed 에러가 뜨는지 분석해 보자. 먼저 내가 제일 이해가 안 됐던 부분은 나는 내 애플리케이션에 WebSocket을 구현하지 않았다. 내 애플리케이션은 실시간으로 데이터를 주고받거나 하지 않기 때문에 개발을 진행하면서 WebSocket 때문에 문제를 겪던 적은 없었다.

 

다음은 이 에러를 유발 할 수 있는 몇가지 경우의 수 이다.

네트워크 문제:
원인: 불안정하거나 신뢰할 수 없는 네트워크 연결은 WebSocket 연결 실패로 이어질 수 있다. 여기에는 높은 지연 시간, 패킷 손실 또는 간헐적인 연결 문제 등이 포함된다.
해결 방법:
- 네트워크 연결을 확인하고 기기의 인터넷 접근을 보장한다.
- 방화벽이나 프록시 서버가 WebSocket 연결을 제한하는지 확인한다.
- 다른 네트워크 환경이나 기기에서 테스트하여 구성 문제를 식별한다.

SSL/TLS 구성:
원인: SSL/TLS 구성 문제는 안전한 WebSocket 연결을 방해할 수 있다.
해결 방법:
- SSL/TLS 인증서, 암호화 프로토콜 및 암호화 스위트를 확인한다.
- 클라이언트와 서버가 동일한 보안 연결 매개 변수를 지원하는지 확인한다.

서버 측 구성:
원인: 잘못된 서버 측 구성이나 WebSocket 연결 관리는 연결 실패를 초래할 수 있다.
해결 방법:
- WebSocket 서버가 올바른 포트에서 실행되고 있는지 확인한다.
- 서버 로그 파일에서 오류 메시지나 경고를 확인한다.
- 서버 관리자에게 구성 및 상태에 대한 자세한 내용을 문의한다.

WebSocket 프로토콜 불일치:
원인: 클라이언트와 서버에서 사용되는 WebSocket 프로토콜 버전의 불일치는 연결 실패로 이어질 수 있다.
해결 방법:
- 클라이언트와 서버 간에 WebSocket 프로토콜 버전의 일관성을 유지한다.
- 애플리케이션이 최신 WebSocket 사양과 호환되는지 확인한다.

 

보안 문제:
- 웹사이트/애플리케이션이 HTTPS를 사용하는지 확인한다.
- SSL 인증서 유효성을 검증하고 도메인과 일치하는지 확인한다.

WebSocket 코드 오류:
- JavaScript 코드에서 WebSocket 구문 오류를 검사한다.
- 올바른 WebSocket 객체 인스턴스화 및 URL 사용을 확인한다.

교차 출처 리소스 공유(CORS) 문제:
- WebSocket 요청이 교차 출처 정책에 의해 제한되는지 확인한다. WebSocket 연결에는 서버에 적절한 교차 출처 리소스 공유(CORS) 헤더 정보가 설정되어 있어야 한다.

리소스 제한:
- WebSocket 연결에 대한 서버 용량을 확인한다.
- 높은 동시성 시나리오를 위한 로드 밸런싱 및 클러스터링을 구현한다.

 

여러가지를 테스트 해보고 찾은 해결 방법은 아래와 같다.

 

-frontend 루트 디렉토리에 환경변수를 추가해 주었다. 나는 며칠동안 이 문제를 해결하려고 고민했는데 이렇게 간단한거였다니 살짝 허무하다. 내가 .env 파일에 추가한 내용은 아래와 같다.

WDS_SOCKET_HOST=0.0.0.0
WDS_SOCKET_PATH=/ws
WDS_SOCKET_PORT=443

 

 이제 왜 `.env` 파일에 환경 변수를 추가하니 문제가 해결되었는지, 그리고 해당 환경 변수가 어떻게 읽히는지 알아보자.

CRA와 환경 변수

`create-react-app`은 프로젝트를 설정할 때, `.env` 파일을 통해 환경 변수를 설정할 수 있도록 한다. 이 환경 변수는 개발 서버(예: `webpack-dev-server`)와 빌드 설정 등에 사용된다. `create-react-app`은 기본적으로 `.env` 파일에서 정의된 변수를 읽고, 이를 Webpack 및 다른 도구에 전달하여 개발 서버와 빌드 설정을 제어한다.

 환경 변수가 읽히는 과정

1. .env 파일 생성: `.env` 파일은 프로젝트의 특정 디렉토리에 위치하며, 이 파일은 환경 변수를 정의하는 데 사용된다.

2. react-scripts: `create-react-app` 프로젝트는 `react-scripts` 패키지를 사용하여 개발 서버를 실행하고 빌드를 관리한다. `react-scripts`는 프로젝트의 루트 디렉토리에서 `.env` 파일을 자동으로 찾고, 이를 로드한다.

3. webpack-dev-server 설정: `create-react-app`은 내부적으로 `webpack-dev-server`를 사용하여 개발 서버를 실행한다. 환경 변수를 통해 `webpack-dev-server`의 설정을 제어할 수 있다. 여기서 설정된 환경 변수가 `webpack-dev-server`의 특정 동작을 제어한다.

특정 환경 변수의 역할

`.env` 파일에 추가한 변수들은 `webpack-dev-server`의 WebSocket 설정을 제어한다. 이를 통해 WebSocket 연결이 실패하는 문제를 해결할 수 있다.

- WDS_SOCKET_HOST: WebSocket 서버의 호스트를 지정한다. 기본값은 `localhost`이지만, 이를 `0.0.0.0`으로 설정하면 모든 IP 주소에서 접근 가능하게 된다.
- WDS_SOCKET_PATH: WebSocket 서버의 경로를 지정합니다. 기본 경로는 `/sockjs-node`이지만, 이를 `/ws`로 설정하면 해당 경로로 WebSocket 요청을 처리하게 된다.
- WDS_SOCKET_PORT: WebSocket 서버의 포트를 지정한다. 기본값은 개발 서버 포트와 동일하지만, 이를 `443`으로 설정하면 HTTPS를 사용하는 경우 적절한 포트로 설정된다.

호스트 설정: WDS_SOCKET_HOST=0.0.0.0 설정을 통해 WebSocket 서버가 모든 IP 주소에서 접근 가능하게 되어, 네트워크 내의 다른 기기나 원격 서버에서도 접근할 수 있게 된다.


경로 설정: WDS_SOCKET_PATH=/ws 설정을 통해 클라이언트와 서버 간의 WebSocket 요청 경로가 일치하게 되어 경로 문제를 해결한다.


포트 설정: WDS_SOCKET_PORT=443 설정을 통해 WebSocket 서버가 HTTPS의 기본 포트인 443번 포트에서 수신하도록 하여 포트 문제를 해결한다.