Cover image of latest post

Web

Frontend

DevOps

Next.js 에서 nginx 배포 시 Suspense 적용 되지 않을 때

Web - May 12, 2025

#

문제

진행중이던 tcgscanner 프로젝트를 배포하기 위해 nginx + ec2 조합을 사용했다.

프론트엔드는 next.js 로 만들었기 때문에 (같은 스택을 사용한 이 블로그처럼) vercel 로 배포해도 됐었으나, github organization 에서 관리되는 코드를 호스팅하기 위해서는 추가 결제를 해야 하는 점, 혹시 모를 cold start 관련 문제로 인해 ec2 에 배포하기로 결정했다.

백엔드 서버가 이미 ec2 에서 구동되고 있었기 때문에 다른 포트 설정 없이 배포하기 위해 nginx 를 사용했다.

하지만, 로컬에서 실행했을 때는 문제가 없던 Suspense 가 배포 후 정상적으로 작동하지 않았다.
정확히는, fallback UI 가 보여지지 않고 백엔드 서버로부터 모든 데이터를 다 받은 후에야 컴포넌트가 렌더링 되었다.

서버 성능이나 네트워크 문제는 아닌 것 같았기에, 혹시나 하는 마음에 문제의 원인이 nginx 에 있지 않을까 생각했다. 로컬과 배포 환경의 가장 큰 차이는 그것이었으니까.

#

해결

##

TL;DR

nginx 는 기본적으로 proxy_buffering 이 on 으로 되어있다. 이는, 서버로부터의 응답을 내부 버퍼에 저장하고, 전체 응답이 버퍼링될 때까지 클라이언트에 데이터를 전송하지 않는다는 것이다.

하지만, next.js 는 ‘스트리밍 응답(streaming response)’ 을 지원한다. 따라서, nginx 의 기본 버퍼링 설정을 끄거나, 그것을 무효화하는 헤더를 포함시키게 하면 된다.

next.js 설정에서 버퍼링을 막기 위해서는 다음과 같이 쓰면 된다.

const nextConfig: NextConfig = {
  headers: async () => {
    return [
      {
        source: '/:path*',
        headers: [{ key: 'Access-Control-Allow-Origin', value: '*' }],
      },
      {
        source: '/:path*{/}?',
        headers: [
          {
            key: 'X-Accel-Buffering',
            value: 'no',
          },
        ],
      },
    ];
  },
};

export default nextConfig;
##

원인

###

nginx 의 버퍼링

버퍼링은 nginx 가 백엔드 서버로부터 받은 응답을 내부 버퍼에 저장하고, 전체 응답이 버퍼링될 때까지 클라이언트에 데이터 전송을 시작하지 않는 것을 말한다. 이렇게 하면, 백엔드 서버는 클라이언트의 수신 속도를 고려하지 않아도 되어, 불필요한 유휴 시간을 줄일 수 있다는 장점이 있다.

예를 들어 버퍼링이 활성화되지 않았다면, nginx 는 백엔드 서버의 응답을 받는 즉시 클라이언트에 동기적으로 전송한다. 이 과정에서 백엔드 서버는 nginx 가 다음 응답 세그먼트를 받아들일 수 있을 때까지 대기해야 한다. 하지만, 버퍼링이 있다면, 백엔드는 nginx 에게 응답을 그냥 던져주고, nginx 는 그것들을 버퍼에 저장했다가 클라이언트의 수신 속도에 맞추어 보내주면 된다.

따라서, 이는 본디 백엔드 서버의 불필요한 부하를 줄이기 위한 것이지 클라이언트를 위한 것이 아니다. 오히려, 응답을 다 받아야지(혹은 버퍼가 가득 차면) 전송을 시작하기 때문에 빠른 클라이언트에서는 상대적으로 느린 응답을 받을 수 있다.

###

Next.js 의 스트리밍

스트리밍은 Next.js 에서 라우트를 더 작은 청크로 나누고, 이를 점진적으로 전송하는 기술이다. 이로써 사용자는 모든 데이터가 로드 될때까지 기다리지 않고서도 페이지의 일부를 보고 상호작용 할 수 있다.

이와 Suspense 를 같이 사용하면 좋은데, 선택적, 그리고 명시적으로 많은 데이터를 가져오는 컴포넌트를 그렇지 않은 컴포넌트와 분리하여 렌더링할 수 있기 때문이다.

하지만, 앞서 본 버퍼링은 이러한 스트리밍을 막는다. 따라서, nginx 와 같이 서버 응답을 버퍼링해놓는 프록시를 사용한다면 버퍼링 기능을 막아야지 제대로 사용할 수 있다.

#

의문점 및 추후 알아보고 싶은 점들

스트리밍을 지원하기 위해서는 버퍼링을 막아야 하는데, 이렇게 되면 백엔드 서버에 부하가 높아지는 것 아닐까? next.js 의 스트리밍이 정확히 어떤 방식으로 이루어지는지 더 깊이 알아보고 이것이 서버 성능 및 부하와 어떤 연관이 있는지 공부해야할듯.