Next.js 15 output 파일이란

Next.js 15 output 파일이란
TILPosted On Apr 22, 202510 min read

Output

Next.js는 빌드 과정에서 각 페이지와 그 의존성들을 자동으로 추적해서, 프로덕션 버전을 배포하는 데 필요한 파일들만 골라내요.

이 기능 덕분에 배포 크기를 확 줄일 수 있어요. 예전에는 Docker로 배포할 때 next start를 실행하려면 패키지 의존성에 포함된 모든 파일을 다 설치해야 했죠. 그런데 Next.js 12부터는 .next/ 디렉터리 안의 Output File Tracing을 활용해서 꼭 필요한 파일만 포함할 수 있게 되었답니다.

게다가 이 방법은 이제 더 이상 사용하지 않는 serverless 타깃을 쓸 필요도 없게 만드는데요, 이 serverless 모드는 여러 가지 문제를 일으키기도 하고 불필요한 파일 복제를 만들어내기도 했었거든요.


추가로, Output File Tracing이 잘 작동하려면, 여러분이 직접 추가한 네이티브 모듈(native modules)이나 외부 파일들이 제대로 인식되는지 꼭 확인해보세요. 때때로 수동으로 설정을 조금 조정해줘야 할 수도 있거든요. 전체 빌드와 배포 최적화를 목표로 한다면 이 부분도 신경 쓰는 게 좋아요!

어떻게 작동할까?

Next.js가 빌드될 때, 내부적으로 @vercel/nft라는 도구를 사용해서 코드를 정적으로 분석해요. 여기서 import, require, 그리고 fs(파일 시스템)를 어떻게 쓰는지 살펴보면서, 특정 페이지가 로딩할 수 있는 모든 파일을 미리 파악하죠.

또한, Next.js의 프로덕션 서버도 필요한 파일들을 추적해서 .next/next-server.js.nft.json이라는 결과물을 만들어줘요. 이 파일은 실제 운영 환경에서 어떤 파일들이 필요한지 알려주는 중요한 파일이에요.

이렇게 생성된 .nft.json 파일들을 활용하려면, 이 JSON 안에 적힌 파일 목록을 읽고, 실제 파일 경로를 기준으로 필요한 파일들을 복사해서 배포할 위치에 넣으면 된답니다.


추가로 알아두면 좋은 점!

  • @vercel/nft는 Node.js 프로젝트에서 필요한 파일들을 추적하는 데 정말 유용한 도구에요. Next.js 외에도 서버리스 환경에서 최적화된 배포를 할 때도 활용하곤 하죠.
  • 이 방식을 이용하면, 불필요한 파일들을 빼고 정말 필요한 파일만 배포할 수 있어서 용량도 줄이고, 보안 측면에서도 이점이 있어요.
  • 만약 환경변수에 따라 동적으로 파일을 로딩하는 코드를 쓰면, 그 부분은 정적 분석에서 누락될 수 있으니 주의해야 해요. 그러니 동적 import를 많이 쓴다면 직접 확인해주는 게 좋아요.

간단히 말해서, Next.js는 앱에 필요한 모든 파일들을 꼼꼼히 체크해서 배포 시 효율적으로 관리할 수 있게 도와주는 거죠!

자동으로 추적된 파일 복사하기

Next.js에서는 배포할 때 필요한 파일들만 쏙쏙 골라서 복사해주는 'standalone' 폴더를 자동으로 만들어줍니다. 여기에는 프로젝트 파일뿐만 아니라, 꼭 필요한 node_modules 내의 파일들도 포함돼요. 덕분에 용량도 줄고, 배포 속도도 빨라지니 아주 편리하답니다!

이 기능을 사용하려면 next.config.js 파일에 아래처럼 설정만 추가해주면 끝!

module.exports = {
  output: 'standalone',
}

이렇게 설정하면 Next.js가 빌드할 때 필요한 파일들만 골라서 따로 모아준답니다. 특히 서버리스 함수나 도커 환경처럼 가볍고 독립적인 배포가 필요할 때 정말 유용해요.

추가 팁!

  • 만약 직접 추가로 복사해야 할 파일이나 폴더가 있다면, next.config.js에서 webpack 설정을 활용해 커스터마이징할 수도 있어요.
  • standalone 모드는 기본적으로 빌드된 output을 따로 분리하다 보니, 기존의 next start 명령어로도 다루기 쉬워진답니다.

필요한 파일만 추려서 배포하고 싶다면 꼭 한 번 적용해보세요!

이 내용을 쉽게 풀어서 설명해볼게요!


Next.js를 빌드하면 .next/standalone이라는 폴더가 생기는데, 이 폴더는 따로 node_modules를 설치하지 않고도 바로 배포할 수 있어요. 즉, 배포 환경을 좀 더 간소화할 수 있는 장점이 있죠.

그리고 server.js라는 작은 서버 파일도 함께 만들어지는데, 이 파일은 기존에 next start로 서버를 띄우는 대신 사용할 수 있어요. 여기서 중요한 점은, 이 작은 서버는 기본적으로 public 폴더나 .next/static 폴더를 복사하지 않아요. 보통 정적 파일은 CDN에서 처리하는 게 효율적이기 때문이에요.

하지만 만약 CDN을 사용하지 않고 직접 서버에서 이 정적 파일들도 서비스하고 싶다면, 빌드 후에 아래 명령어로 직접 폴더를 복사해주면 돼요:

cp -r public .next/standalone/ && cp -r .next/static .next/standalone/.next/

복사가 끝나면, server.js가 이 경로에 있는 정적 파일들을 자동으로 서빙하게 됩니다.


조금 더 덧붙이자면, 단독 배포(standalone deployment)는 서버리스 환경이나 컨테이너 배포 등에서 아주 유용해요. 보통 node_modules 폴더 크기가 꽤 되는데, 이걸 제외하고 필요한 코드만 딱 가져가니까 훨씬 가볍고 빠르게 배포할 수 있어요.

또한, CDN을 활용하면 정적 파일 로딩 속도가 더 빨라져서 사용자 경험도 좋아지니까, 가능하면 CDN을 쓰는 방향을 추천해요. 그래도 간단하게 로컬 서버에서 정적 파일도 서비스해보고 싶다면, 위 복사 명령어 기억해두면 되겠죠?


필요할 때마다 이렇게 standalone 빌드 사용하시고, 배포 환경에 맞게 정적 파일 처리 방법도 유연하게 선택해보세요!

로컬에서 최소한의 server.js 파일을 실행하고 싶다면, 아래 커맨드를 사용하면 돼요:

node .next/standalone/server.js

참고할 점:
만약 여러분의 프로젝트가 특정 포트나 호스트네임에서 서버를 띄워야 한다면, 실행 전에 환경변수로 PORT나 HOSTNAME을 설정할 수 있어요.
예를 들어,

PORT=8080 HOSTNAME=0.0.0.0 node server.js

이렇게 하면 http://0.0.0.0:8080 에서 서버가 실행됩니다.


주의할 점 (Caveats)

이어서, server.js를 사용할 때 알아두면 좋은 주의사항들도 있으니 같이 살펴봐요!

monorepo 환경에서 Next.js의 트레이싱 기능을 사용할 때, 기본적으로 프로젝트 디렉토리가 트레이싱의 기준이 돼요. 예를 들어 packages/web-app이라는 패키지를 빌드할 때, packages/web-app 폴더만 트레이싱 루트로 잡히고, 그 밖의 파일들은 포함되지 않아요. 만약 이 외부 파일들도 포함시키고 싶다면 next.config.js에서 outputFileTracingRoot를 설정해주면 됩니다.

module.exports = {
  // monorepo 루트가 현재 디렉토리에서 두 단계 위에 있는 경우
  outputFileTracingRoot: path.join(__dirname, '../../'),
}

이렇게 하면 monorepo의 최상위 폴더부터 파일을 추적하기 때문에, 여러 패키지에서 필요한 공통 파일도 문제없이 포함될 수 있어요.


근데 가끔 Next.js가 필요한 파일을 빼먹거나, 반대로 필요 없는 파일을 잘못 포함시키는 경우가 있어요. 이럴 때 outputFileTracingExcludesoutputFileTracingIncludes 옵션을 활용할 수 있습니다.

  • outputFileTracingExcludes: 특정 페이지나 API 라우트에 대해 추가적으로 제외하고 싶은 파일이나 폴더를 지정
  • outputFileTracingIncludes: 특정 페이지나 API 라우트에 대해 추가로 포함해야 하는 파일이나 폴더를 지정

두 옵션 모두 minimatch glob 형식의 패턴을 받아서 더 세밀하게 조절할 수 있답니다.

module.exports = {
  outputFileTracingExcludes: {
    '/api/hello': ['./un-necessary-folder/**/*'], // '/api/hello' 라우트에 un-necessary-folder 아래 모든 파일 제외
  },
  outputFileTracingIncludes: {
    '/api/another': ['./necessary-folder/**/*'], // '/api/another' 라우트에 necessary-folder 포함
    '/api/login/\\[\\[\\.\\.\\.slug\\]\\]': [
      './node_modules/aws-crt/dist/bin/**/*', // 동적 라우트 '[...slug]'에 node_modules aws-crt 라이브러리 포함
    ],
  },
}

팁을 하나 더 드리자면, monorepo에서 이렇게 설정을 잘 해두면 빌드할 때 누락된 파일 문제를 방지하고, 불필요한 파일까지 묶이는 걸 막을 수 있어서 배포 용량 최적화에도 큰 도움이 돼요. 꼭 필요하다면 로깅 기능이나 자체 스크립트를 활용해서 다음 빌드에 포함되는 파일들을 점검해보는 것도 좋아요!

Next.js가 파일 트레이싱을 완전히 자동으로 잘 해주긴 하지만, 가끔 민감한 상황에서는 이 설정들로 미세 조정할 수 있다는 점 꼭 기억해두세요!

참고: outputFileTracingIncludes/outputFileTracingExcludes의 키는 glob 패턴이라서, 특수 문자는 꼭 이스케이프 처리를 해줘야 해요.

  • 현재 Next.js는 생성된 .nft.json 파일들을 따로 처리하지 않아요. 이 파일들은 배포 플랫폼(예: Vercel)에서 읽어서, 최소한의 용량으로 배포하기 위해 사용됩니다. 앞으로 출시될 버전에서는 이 .nft.json 파일들을 활용하는 새로운 명령어도 계획 중이라고 하네요.

실험적인 turbotrace 기능

의존성 추적(tracing)은 복잡한 계산과 분석을 필요로 해서 속도가 느릴 수밖에 없어요. 그래서 저희는 Rust로 turbotrace라는 도구를 만들었는데요, 기존 자바스크립트 구현체보다 훨씬 빠르고 똑똑한 대안이랍니다.


여기서 잠깐! glob 패턴을 쓸 때는 *, ?, [ 같은 특수 문자를 적절히 이스케이프해야 예상치 못한 결과를 막을 수 있어요. 예를 들어, \*처럼 백슬래시를 붙여주면 안전합니다.

그리고 turbotrace가 나중에 공식 next.js에 통합되면, 배포 속도도 많이 가뿐해질테니 기대해봐도 좋겠네요! Rust 기반이라 메모리 관리가 더 효율적인 점도 큰 장점입니다.

Next.js에서 turbotrace 기능을 활성화하려면, next.config.js 파일에 아래와 같은 설정을 추가해주면 됩니다:

module.exports = {
  experimental: {
    turbotrace: {
      // turbotrace 로그 레벨을 제어합니다. 기본값은 'error'입니다.
      logLevel:
        | 'bug'
        | 'fatal'
        | 'error'
        | 'warning'
        | 'hint'
        | 'note'
        | 'suggestions'
        | 'info',
      
      // 분석의 상세 내용을 로그에 포함할지 여부 (기본값: false)
      logDetail: false,
      
      // 모든 로그 메시지를 제한 없이 보여줍니다.
      // 기본적으로 turbotrace는 각 카테고리별로 1개의 로그 메시지만 표시합니다.
      logAll: false,
      
      // turbotrace의 컨텍스트 디렉터리를 지정합니다.
      // 컨텍스트 디렉터리 외부의 파일은 추적 대상에서 제외됩니다.
      // `outputFileTracingRoot` 설정과 유사한 역할을 합니다.
      // 만약 `outputFileTracingRoot`와 `contextDirectory`가 모두 설정되어 있으면, `contextDirectory`가 우선됩니다.
      contextDirectory: '',
      
      // 코드 내 `process.cwd()` 표현이 있다면 이것을 turbotrace에 알리기 위한 설정입니다.
      // 예를 들어 `require(process.cwd() + '/package.json')` 같은 경우 올바르게 추적됩니다.
      processCwd: '',
      
      // turbotrace의 최대 메모리 사용량(MB 단위)을 설정합니다. 기본값은 6000MB입니다.
      memoryLimit: 6000,
    },
  },
}

turbotrace 기능이 뭐죠?

turbotrace는 Next.js에서 사용되는 파일 의존성 추적 도구에요. 앱 번들링이나 서버리스 배포할 때 어떤 파일들이 실제로 필요한지 스스로 추적해서 정확한 파일만 포함할 수 있게 도와줍니다. 이 덕분에 배포 크기 최적화나 빌드 시간 단축에 효과적이죠.


몇 가지 팁!

  • logLevel'info''suggestions'로 높여두면 turbotrace가 어떻게 동작하는지 더 자세한 정보들을 확인할 수 있으니 디버깅할 때 유용해요.
  • contextDirectory를 프로젝트 루트로 지정하면 외부 라이브러리나 임시 파일 같은 엉뚱한 파일 추적을 줄일 수 있어요.
  • 메모리 제한(memoryLimit)은 상황에 맞게 조절해보세요. 큰 프로젝트라면 기본 6000MB보다 더 늘려야 문제가 생기지 않을 수 있어요.

Next.js를 사용할 때 이런 실험적인 기능들을 직접 써보면서 프로젝트에 맞게 튜닝해보는 것도 재미있는 경험이 될 거예요! 언제든 최신 문서나 커뮤니티 의견도 참고하면서 최적화해보세요 :)