본문 바로가기
Springboot

Springboot nginx wegsacket ssl 연결 통신 후 [WebSocket Error]java.net.ProtocolException: Expected HTTP 101 response but was '400 ' 에러해결법

by JunsC 2024. 10. 14.
728x90

springboot 에서 웹소켓으로 채팅을 하려고 했는데 자꾸 에러가 난다..

웹소켓은 뭔가 ??

여기에서 WebSocket 에 대해 우선 알아보도록 하자.

 


1. WebSocket 이란 ?

Spring Boot WebSocket은 양방향(실시간) 통신을 가능하게 하는 프로토콜을 Spring Boot에서 지원하는 기능이다.
일반적인 HTTP 요청/응답 방식과 달리 WebSocket은 지속적인 연결을 유지하면서 실시간 데이터를 주고받을 수 있는 프로토콜 이다. 

■ WebSocket의 특징

실시간 양방향 통신
→ 서버와 클라이언트가 한 번 연결을 맺으면 지속적으로 데이터를 주고받을 수 있음
→ 채팅, 알림, 실시간 주식 데이터, 게임 등에 활용

HTTP보다 효율적인 데이터 전송
→ HTTP는 매 요청마다 새로운 연결을 맺지만, WebSocket은 한 번 연결하면 유지됨
→ 네트워크 리소스를 줄이고 속도를 향상시킴

Full-Duplex 통신
→ 클라이언트와 서버가 동시에 데이터를 주고받을 수 있음

Event-Driven 방식
→ 이벤트가 발생할 때마다 데이터가 푸시됨 (예: 새로운 채팅 메시지 도착 시 바로 표시)

 

WebSocket 사용 사례

실시간 채팅
실시간 알림 (푸시 알림, 이메일 알림 등)
라이브 데이터 업데이트 (주식, 날씨, 스포츠 경기 등)
온라인 멀티플레이 게임

 

 

 방식 정리

WebSocket 핸들러 TextWebSocketHandler를 사용하여 WebSocket 구현
STOMP + SockJS 메시징 프로토콜(STOMP)과 SockJS를 활용해 보다 안정적으로 WebSocket 지원
SockJS WebSocket을 지원하지 않는 환경에서도 대체할 수 있도록 제공

Spring Boot WebSocket은 실시간 기능이 필요한 서비스에서 강력한 도구이다.
STOMP + SockJS를 함께 사용하는 것이 일반적이며, @MessageMapping과 @SendTo를 활용하면 보다 편리하게 메시지를 주고받을 수 있다.

어떤 방식으로 구현할지 고민 중이라면?
👉 채팅 서비스나 알림 시스템이라면 STOMP + SockJS를 추천! 🚀

 

 


 

하지만 나는 일반적인 WebSocket 핸들러를 사용했다. 우선 처음 찾아보는 방법이 웹소켓 핸들러였고 이미 웹소켓 핸들러에 대한 환경설정을 미리 끝마쳤기 때문이다.

하지만 테스트 했을때 로컬에서는 잘 되었지만 실제 서버 업로드 후 릴리즈 모드로 테스트 해보았는데 웹소켓 nginx 리다이렉트 부분에서 문제가 생긴 듯 하다

그래서 구글링을 해보았다.

 

문제는 분명 nginx 에서 발생하는 것 같았다.. 왜냐하면 로컬에서는 잘 되었으니깐..!!

그래서 nginx 설정 관련해서 집중적으로 찾아보았다 

 


2. Nginx 란 ?

**Nginx(엔진엑스)**는 고성능의 HTTP 웹 서버이자 리버스 프록시(reverse proxy) 서버이다.
처음에는 높은 동시 접속 처리를 위해 개발되었으며, 현재는 로드 밸런서, 캐시 서버, 스트리밍 서버 등 다양한 용도로 활용된다.

 

 Nginx의 주요 특징

빠르고 가벼운 웹 서버
→ C언어로 작성되어 성능이 뛰어나고, 낮은 리소스로도 많은 요청을 처리 가능

비동기 & 이벤트 기반 구조
→ 하나의 프로세스가 여러 요청을 동시에 처리할 수 있어 동시 연결(Concurrency) 처리에 강함

리버스 프록시 기능
→ 클라이언트 요청을 백엔드 서버(Apache, Node.js, Spring Boot 등)로 전달하는 리버스 프록시 역할 수행

로드 밸런싱 지원
→ 여러 서버에 요청을 분배하여 부하를 균등하게 분산할 수 있음

정적 파일 제공 최적화
→ HTML, CSS, JS, 이미지 등의 정적 콘텐츠를 빠르게 제공

SSL/TLS 지원
→ HTTPS 트래픽을 처리하고 Let's Encrypt 등의 SSL 인증서 적용 가능

캐싱 기능 제공
→ 반복되는 요청에 대해 캐싱을 설정하여 성능 향상

 

 

Nginx 기본적인 동작 방식

Nginx는 클라이언트 요청을 받아 백엔드 서버로 전달하거나 정적 파일을 직접 제공하는 방식으로 동작합니다.

(1) 기본적인 요청 흐름

클라이언트 요청 → Nginx → 정적 파일 응답 OR 백엔드 서버 전달 → 응답 반환

 

정적 파일 서빙

  • 클라이언트가 index.html 요청 시, Nginx가 직접 파일을 반환

리버스 프록시

  • 클라이언트 요청을 Nginx가 받고, 백엔드 서버(Node.js, Django, Spring Boot 등)로 전달

로드 밸런싱

  • 여러 백엔드 서버 중 하나로 요청을 분산하여 서버 부하를 줄임

 

 Nginx 사용 사례

정적 웹 사이트 배포 (HTML, CSS, JavaScript 서빙)
Spring Boot, Django, Node.js 등의 백엔드와 연동 (리버스 프록시)
로드 밸런싱을 통한 부하 분산
HTTPS(SSL/TLS) 적용하여 보안 강화
API Gateway 역할 수행

 

 

Nginx는 가볍고 빠른 웹 서버로, 리버스 프록시와 로드 밸런싱까지 지원하는 강력한 서버 소프트웨어이다.

📌 어떤 경우에 Nginx를 사용할까?

  • 정적 파일을 빠르게 서빙하고 싶을 때
  • Spring Boot, Django, Node.js 등의 API 서버 앞단에 리버스 프록시를 둘 때
  • 부하를 분산하는 로드 밸런서를 구축할 때
  • HTTPS(SSL/TLS) 적용이 필요할 때

 

이렇게 nginx 를 알아보았다. 그렇다면 나의 nginx 환경 설정은 어떻게 해야할까? 

 

location /bmt/chat { 
			proxy_pass http://localhost:8085/chat; # 실제 WebSocket 서버의 주소 
            proxy_http_version 1.1; proxy_set_header 
            Upgrade $http_upgrade; 
            proxy_set_header Connection "Upgrade"; 
            proxy_set_header Host $host; 
            proxy_set_header X-Real-IP $remote_addr; # 필요시 추가 
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 필요시 추가 
            
          }

 

위와 같은 해결방법을 알아내었다 

여기에서 위와 같이 

 

            Upgrade $http_upgrade; 
            proxy_set_header Connection "Upgrade"; 

 

이 2개의 구문을 추가해주었더니 잘 통신이 됐다 !!!

 

 

 

 

그럼 위의 2가지 는 무엇을 의미하는 것일까 !!?? 

 

1. Upgrade $http_upgrade;

proxy_set_header Upgrade $http_upgrade;

 

설명:

  • HTTP 요청 헤더의 Upgrade 값을 $http_upgrade 변수로 설정합니다.
  • $http_upgrade는 클라이언트가 보낸 Upgrade 헤더 값을 저장하는 변수입니다.
  • WebSocket 또는 HTTP/2 연결을 사용할 경우, 클라이언트가 **"이 연결을 업그레이드하고 싶다"**고 요청하면, 이를 백엔드 서버로 전달합니다.

예시:
WebSocket을 사용하면 클라이언트가 다음과 같은 요청을 보냅니다.

GET /websocket HTTP/1.1 Host: example.com Connection: Upgrade Upgrade: websocket
  • $http_upgrade 값은 "websocket"이 되고, 이를 백엔드 서버로 전달하는 역할을 합니다.

 

2. proxy_set_header Connection "Upgrade"

proxy_set_header Connection "Upgrade";
 

설명:

  • Connection 헤더를 "Upgrade"로 설정하여 클라이언트와 백엔드 서버 간의 업그레이드 요청을 유지합니다.
  • 일반적으로 HTTP는 요청을 보낸 후 연결을 닫지만, WebSocket은 지속적인 연결을 유지해야 하므로 업그레이드 요청을 명확히 전달해야 합니다.

📌 Connection 헤더는 기본적으로 "keep-alive"이지만, WebSocket에서는 "Upgrade"로 변경하여 지속적인 연결을 보장합니다.

 

이 설정이 필요한 이유

  • proxy_http_version 1.1; → WebSocket은 HTTP/1.1 이상에서만 지원됨
  • proxy_set_header Upgrade $http_upgrade; → 클라이언트가 요청한 Upgrade 헤더 값을 백엔드 서버로 전달
  • proxy_set_header Connection "Upgrade"; → 연결을 업그레이드하여 지속적인 통신 가능

 

그래서 결국 제대로 통신이 가능했던 것 같다. !!!

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."