[React + Nginx + Spring] WS Handshake 오류: "Handshake failed due to invalid Upgrade header: null"

Jinny·2023년 10월 9일
1

Trouble Shooting

목록 보기
5/8
post-custom-banner

WS Handshake 오류

상황

현재 프로젝트의 개발 서버 환경은 EC2로, Spring Boot + NGINX로 구성되어 있다.
프론트가 개발 서버로 Stomp 통신을 위해 Web Socket Handshake 요청을 보내는데 문제가 생겼다.


문제

  1. 프론트에서 Handshake 요청 시 백엔드로 넘어오기 전에 Fail 오류 발생
    (웹브라우저에서 오류 로그를 볼 수 있으나 백엔드 서버에서는 아무런 로그가 찍혀있지 않았음)
  2. 1번 문제를 해결하고 나니 "Handshake failed due to invalid Upgrade header: null" 오류 발생
    (백엔드 서버에 로그 찍힘)

고민

일단 상황을 돌아보면서 원인의 근원지를 추측해보았다.

  • 프론트의 문제인지, 프론트 -> 백엔드로 넘어오는 사이에 모종의 이유로 유실된 것인지 파악이 필요
  • local에서 Apic으로 테스트 했을 때는 문제가 없었는데 서버로 올라갔을 때 문제가 발생했음

그래서 일단 프론트 문제가 아니고 프론트 -> Nginx -> 백엔드로 요청하면서 발생한 문제로,
1번 문제는 로그가 안 찍혀서 Nginx 이슈이고, 2번 문제는 백엔드 문제라고 생각이 들었다.


원인 및 해결

1. Handshake 요청 시 백엔드로 넘어오기 전에 Fail 오류 발생

원인
이 문제는 프론트 코드와도 관련이 있는 것으로 생각이 드는데, 예제 코드에는 대부분 다음과 같이 SockJS 연결 위에 Stomp 프로토콜을 설정했다.

그런데 우리 프론트는 Stomp 클라이언트를 설정하고 생성하는 코드밖에 없었다.

예제 코드)

var sockJS = new SockJS("/ws");
var ws = Stomp.over(sockJS);

우리 프론트 코드)

import { Client } from "@stomp/stompjs”;
const client = new Client();

해결
반면 백엔드 설정은 SockJS 연결 위에 Stomp 프로토콜을 설정하고 있었다.
프론트엔드 백엔드 환경이 달라서 에러가 발생한다고 생각이 들어 백엔드 설정을 바꿔보았다.

(프론트엔드 설정 바꿔보는 테스트도 해보고 싶었는데, 시간 부족으로 다음에 프론트분이랑 같이 테스트 해보기로 했다. 테스트한 결과가 나오면 블로그도 업데이트할 예정이다.)

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*");
                // .withSockJS(); // 해당 옵션 삭제
    }

2. "Handshake failed due to invalid Upgrade header: null" 오류

첫번째 문제 해결 후 handshake에 실패했다.
오류 로그를 보니 업그레이드 header가 없다는 것인데, trace 원인도 안찍히고 딸랑 저것만 찍혀있다.

지금까지의 경험상 로컬에서 문제가 없는데 배포하면서 문제가 발생했다면, 특히 http header 관련이면 Nginx 설정 관련 오류일 가능성이 높아서 공식 문서를 찾아보았다.

원인
Nginx 공식 문서에 따르면 다음과 같다.

"Upgrade"는 홉별 헤더이므로 클라이언트에서 프록시 서버로 전달되지 않습니다.
리버스 프록시에서는 클라이언트가 프록시 서버를 알지 못하며 프록시 서버에서 특별한 처리가 필요합니다.
프록시 서버가 클라이언트의 WebSocket으로의 프로토콜 전환 의도를 알기 위해서는 이러한 헤더를 명시적으로 전달해야 합니다.

location /chat/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

즉, "Upgrade" 및 "Connection"을 포함한 홉별 헤더는 클라이언트에서 프록시 서버로 전달되지 않기 때문에
handshake 과정에서 헤더를 찾지 못해 발생한 문제로 Nginx 설정에 위 내용을 추가하니 해결이 되었다.

참고

profile
블로그 이사갔어요. https://jinny-l.tistory.com/
post-custom-banner

0개의 댓글