현재 프로젝트의 개발 서버 환경은 EC2
로, Spring Boot + NGINX
로 구성되어 있다.
프론트가 개발 서버로 Stomp
통신을 위해 Web Socket Handshake
요청을 보내는데 문제가 생겼다.
Handshake
요청 시 백엔드로 넘어오기 전에 Fail 오류 발생"Handshake failed due to invalid Upgrade header: null"
오류 발생일단 상황을 돌아보면서 원인의 근원지를 추측해보았다.
그래서 일단 프론트 문제가 아니고 프론트 -> Nginx -> 백엔드로 요청하면서 발생한 문제로,
1번 문제는 로그가 안 찍혀서 Nginx 이슈이고, 2번 문제는 백엔드 문제라고 생각이 들었다.
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(); // 해당 옵션 삭제
}
첫번째 문제 해결 후 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 설정에 위 내용을 추가하니 해결이 되었다.