또 만났다. NGINX
Nginx는 도대체 뭐때문에 날 이렇게 괴롭히는 것인지?
현재 프로젝트 서버 마이그레이션중으로 to-be 서버에 Next.js 프로젝트를 배포했다.
화면은 정상적으로 나오지만, 브라우저 콘솔에는 Websocket오류가 계속 발생했다. 🤦♀️ 그만 좀 떠라 제발..
회사 url이 있기 때문에 사진 첨부는 어렵지만, 대략 아래와 같은 메세지가 반복해서 나왔다.
🚨 WebSocket connection to 'wss:// [domain]: 8080/_next/webpack-hmr' failed
사실 화면도 잘 뜨고 프로젝트도 잘 돌아가니까, 굳이 해야하나 싶었다.
그치만 콘솔창 들어갈때마다 보이는 에러가 보기 싫어서 결국 구글링 및 삽질해서 해결했다.
해결은 역시나 nginx.conf
😊
우선 해당 프로젝트 스펙을 간단하게 정리해본다.
WebSocket을 사용하지 않는 프로젝트다.
next.js v12는 서버 커넥션 유지를 위해 WebSocket을 활용하기 시작했다.
이에 따라 Next.js 버전 11에서 12로 업그레이드한 사용자 중에 해당 에러가 빈번히 발생하는 문제가 많음을 발견했다.
여기서 나는 Next.js에서 웹소켓 연결을 시도하는 것이 문제라고 판단해 next.config.js
를 수정하고자 했다.
몇몇 사용자는 v11로 내리니 에러가 사라졌다고 하나, 프로젝트는 처음부터 v12버전을 사용하고 있었기때문에 11로 버전을 내릴 생각은 없었다.
그래서 next.js next.config.js
에서 HMR(Hot Module Replacement) 관련 설정을 비활성하려고 시도했다.
실제 운영중인 프로젝트이기때문에 소스코드를 그대로 적을 수 없어 추가한 설정만 남겨놓겠다.
module.exports = withTM({
publicRuntimeConfig,
trailingSlash: true,
reactStrictMode: false,
experimental: {
esmExternals: false,
jsconfigPaths: true
},
webpack: (config) => {
config.resolve.alias = {
...config.resolve.alias,
...
}
config.optimization = {
...config.optimization,
runtimeChunk: false,
splitChunks: false
}
return config;
},
webSocketTransport: false,
async rewrites() {
return rewrites
}
})
결과 실패. 😢
계속 같은 에러가 발생했고, WebSocket 연결 시도가 계속되어졌다.
WebSocket 연결 실패가 CORS(Cross-Origin Resource Sharing) 정책 위반일 수 있다고 판단했다.
브라우저 보안 정책으로 인한 차단일 수 있어서 CORS header 추가를 시도했다.
async headers() {
return [
{
source: '/_next/webpack-hmr',
headers: [
{
key: 'Access-Control-Allow-Origin',
value: '*'
},
{
key: 'Access-Control-Allow-Methods',
value: 'GET, POST, PUT, DELETE, OPTIONS'
},
{
key: 'Access-Control-Allow-Headers',
value: 'X-Requested-With, Content-Type, Upgrade, Connection'
},
{
key: 'Connection',
value: 'Upgrade'
},
{
key: 'Upgrade',
value: 'websocket'
}
]
}
]
}
결과 역시 실패 😢😢
CORS Header를 여러개 추가하고 삭제도 해보고 해봤지만, 같은 에러가 발생했다.
이에따라 단순한 CORS 문제가 아님을 깨달았다.
next.config.js
설정만으로는 해결이 안되어 프로세스 실행 환경(pm2)에서 WebSocket을 비활성하려고 시도했다.
환경변수를 통해서 Node.js
레벨에서 WebSocket 연결을 막을 수 있을 것이라 예상했다.
module.exports = {
apps: [
{
name: "",
...
env: {
...,
NEXT_HAS_CLIENT_WEBSOCKETS: "false",
DISABLE_WEBSOCKET: "true"
},
}
]
}
결과로는 실패했다. 😢😢😢
환경변수 설정 적용이 안되었고, 여전히 같은 에러가 발생했다.
사실 이 이외에도 다양한 설정들을 주고 적용해봤지만 결과는 여전히 실패였다..
에러 메세지가 wss://
로 시작하는 것을 보고 혹시 SSL/TLS 관련 문제이지 않을까? 라고 추측했다.
기존 프로젝트의 경우, SSL은 nginx에서 처리했다. 하지만 To-be 서버에서는 LB(Load Balancer)에서 SSL을 처리하도록 변경되었다.
이로 인해 WebSocket의 프로토콜 업그레이드가 제대로 되지 않는걸까? 라는 추측에서 시작되었다.
또한, nginx 프록시 서버로 작동하면서 WebSocket 연결 요청을 제대로 처리하지 못하는 걸수도 있다고 추측했다.
(실제로는 여러 시도가 실패하다 보니 '이쯤 되면 nginx 문제겠지...'하는 마음 99.99999% .... 👉👈)
현재 프로젝트는 8081 포트로 접속한다. 그래서 당연히 nginx.conf에서8081 포트에 WebSocket 설정을 수정해줬다.
server {
listen 8081;
server_name name~;
location / {
proxy_pass http://localhost:8090;
}
location /_next/webpack-hmr {
proxy_pass http://localhost:8090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
결과로는 실패했다. 😢😢😢😢
계속 같은 에러가 발생했다. nginx 도 문제가 아닌걸까... 하다가 다시 에러 메세지를 확인했다.
🚨 WebSocket connection to 'wss:// [domain]: 8080/_next/webpack-hmr' failed
에러메세지를 자세히보니 8080 포트가 언급되어있다.
즉, 클라이언트가 8080포트로 WebSocket 연결을 시도하고 있다고 판단해서 위의 설정을 8080포트에도 설정해줬다.
server {
listen 8080;
....
location / {
proxy_pass http://localhost:8090;
}
location /_next/webpack-hmr {
proxy_pass http://localhost:8090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
결과 성공 🎉🎉
더 이상 해당 에러가 콘솔에서 보이지 않았다.
next.js
는 서버 상태 유지를 위해 WebSocket을 사용하는데, nginx
에 관련 설정이 없어서 연결이 계속 끊어지고 있었다.
또한, 클라이언트는 8080 포트로 접속을 시도하는데, 나는 8081 포트만 설정해둔 것이 문제였다.
따라서 클라이언트가 접속하는 8080 포트에 WebSocket 설정을 추가하여 해결할 수 있었다.
여기까지 느낀 결과: 에러 메세지를 잘 읽자. 😊
라고 했으나 내가 8080 포트로 들어가서 그랬던 것.... ^^