JS, TS

Vite, nginx, docker 환경에서 무한 reload 현상 고치기

Mitchell 2023. 11. 15. 22:52

Vite devServer를 nginx proxy 하기

사내 개발환경에서 백엔드서버와 프론트엔드 서버들을 Docker 위에서 nginx의 리버스 프록시로 띄워서 사용하고 있었습니다. 그 중 Vite로 빌드한 React App과 devSever도 포함되었습니다. devServer와의 연결을 위해서 nginx에선 HTTP를 WebSocket으로 업그레이드 하느 설정이 반드시 필요합니다. 설정은 아래와 같이 간단합니다.

server {
    listen 80;
    client_max_body_size 50M;

    location /api {
        proxy_pass http://backend;
    }

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

 

그런데 어떤 문제가?

그리고 브라우저에서 앱을 실행해보면 처음엔 정상적으로 작동하는 듯 싶었으나, 아래와 같은 로그가 뜨면서 페이지가 주기적으로 새로고침 되는 현상이 발견되었습니다.

console.log

 

원인이 무엇일까 머리를 싸매다가 결국 Vite의 코드를 직접 파헤쳐 원인을 알아내기 시작했고 아래 코드를 통해서 그러한 동작이 발생함을 알 수 있었습니다.

function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {
    const socket = new WebSocket(`${protocol}://${hostAndPath}`, 'vite-hmr');
    let isOpened = false;
    socket.addEventListener('open', () => {
        isOpened = true;
        notifyListeners('vite:ws:connect', { webSocket: socket });
    }, { once: true });
    // Listen for messages
    socket.addEventListener('message', async ({ data }) => {
        handleMessage(JSON.parse(data));
    });
    // ping server
    // 여기에서 관련 로그를 뿌려주고
    // 페이지를 리로드 합니다.
    socket.addEventListener('close', async ({ wasClean, reason }) => {
        console.log(`[vite] Why server connection lost: `, reason)

        if (wasClean)
            return;
        if (!isOpened && onCloseWithoutOpen) {
            onCloseWithoutOpen();
            return;
        }
        notifyListeners('vite:ws:disconnect', { webSocket: socket });
        console.log(`[vite] server connection lost. polling for restart...`);
        await waitForSuccessfulPing(protocol, hostAndPath);
        location.reload();
    });
    return socket;
}

 

  • socket에서 'close' 이벤트가 발생한다.
  • '[vite] server connection lost. polling for restart...' 로그를 찍는다.
  • location.reload()로 페이지를 새로고침 한다.

 

문제의 원인

여기서 문제의 원인은 의도치 않은 'close' 이벤트의 발생이라는 것을 알게 되었습니다. 첫 로드에는 정상적으로 연결되었다가 30~60초 정도 이후에 랜덤으로 연결이 끊어지는 현상이였는데요. 알고보니 nginx에서는 WebSocket과의 연결 이후 60초(기본값) 동안 아무 통신이 없으면 연결을 자동으로 끊어버린다는 것을 알게 되었습니다.

 

그러나 개발환경에서는 페이지를 열어두고 변경 사항을 체크해야하기 때문에 더 긴 시간동안 연결이 유지되어 원활한 개발을 할 수 있도록 해야합니다.

 

간단한 해결방안

문제의 원인을 파악하는데는 멀리 돌아왔지만 해결책은 매우 간단합니다. WebSocket의 연결지속 시간, 즉 timeout 설정을 길게 가져가 주면 해결 할 수 있을 것 입니다. 아래와 같이 send와 read에 대해서 timeout 설정을 하면되는데요. 여기에서는 10분까지는 연결을 유지할 수 있도록 설정해주겠습니다.

server {
    listen 80;
    client_max_body_size 50M;

    location /api {
        proxy_pass http://backend;
    }

    location / {
        proxy_pass http://frontend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_send_timeout 600;
        proxy_read_timeout 600;
    }
}

 

 

이렇게 설정 후에는 devServer와의 연결이 더 이상 끊어지지 않고 지속적으로 개발 할 수 있게 되었습니다.

(마법같게도 10분이 지나도 연결이 유지되는 것을 확인하였습니다..?)