Next.js의 차세대 번들러 Turbopack을 다룹니다. Rust 기반 아키텍처, 성능 벤치마크, FS 캐싱, Webpack 마이그레이션, 설정 방법을 살펴봅니다.
8장까지 Server Actions와 폼 처리를 다루었습니다. 이번 장에서는 Next.js의 빌드 도구 레이어로 내려가, Webpack을 대체하는 차세대 번들러 Turbopack의 아키텍처, 성능, 설정, 마이그레이션 전략을 살펴봅니다.
Turbopack은 Next.js 13에서 알파로 처음 공개된 이후, 꾸준히 안정성과 기능을 확장해 왔습니다. 각 버전별 진화를 정리하면 다음과 같습니다.
| 버전 | Turbopack 상태 |
|---|---|
| Next.js 13 | 알파 공개, 개발 서버 한정 |
| Next.js 14 | 베타, 5,600개 통합 테스트 통과 |
| Next.js 15.0 | Dev 안정화 (next dev --turbopack) |
| Next.js 15.5 | Builds 베타, 프로덕션 빌드 지원 시작 |
| Next.js 16.0 | 기본 번들러 전환, --webpack 플래그로 opt-out |
| Next.js 16.1 | FS 캐싱 안정화, 영속적 캐시로 재시작 시에도 빠른 빌드 |
| Next.js 16.2 | Tree Shaking 개선, SRI 지원, 87% 빠른 dev 시작 |
Next.js 15에서 개발 서버가 안정화되었고, Next.js 16에서 기본 번들러로 채택되면서 Webpack 시대의 전환이 본격화되었습니다.
Turbopack의 성능은 단순히 "Rust로 작성되어서 빠르다"라는 한마디로 설명할 수 없습니다. 세 가지 핵심 설계 원칙이 성능의 근간입니다.
JavaScript 번들러(Webpack, esbuild의 Go 부분 제외)는 V8 엔진 위에서 실행됩니다. Turbopack은 Rust로 작성되어 네이티브 바이너리로 컴파일됩니다. 가비지 컬렉션 없이 메모리를 직접 관리하고, 멀티스레드 병렬 처리가 가능합니다.
Turbopack의 핵심은 Turbo Engine이라 불리는 증분 계산 엔진입니다. 함수 호출 결과를 세밀하게 추적하여, 입력이 변경된 함수만 다시 실행합니다.
Webpack은 파일 하나가 변경되면 해당 모듈과 관련된 청크 전체를 다시 빌드합니다. Turbopack은 변경된 함수 단위로 재계산하므로, 프로젝트 규모가 커져도 HMR(Hot Module Replacement) 시간이 일정하게 유지됩니다.
Turbopack은 브라우저가 실제로 요청하는 모듈만 컴파일합니다. 100개의 라우트가 있는 프로젝트에서 /dashboard 페이지를 열면, 해당 페이지와 관련된 모듈만 처리합니다. Webpack이 전체 의존성 그래프를 미리 구성하는 것과 대조적입니다.
Turbopack의 성능 개선은 세 가지 측면에서 측정됩니다.
Next.js 16.2 기준, Turbopack은 Webpack 대비 76.7% 빠른 개발 서버 시작을 달성했습니다. 대규모 프로젝트일수록 차이가 극명합니다.
| 프로젝트 규모 | Webpack | Turbopack | 개선율 |
|---|---|---|---|
| 소규모 (50 모듈) | 1.2초 | 0.4초 | 67% |
| 중규모 (500 모듈) | 4.8초 | 1.1초 | 77% |
| 대규모 (5,000 모듈) | 18초 | 3.2초 | 82% |
코드를 수정했을 때 브라우저에 반영되는 속도, 즉 Fast Refresh는 개발 체감 성능에 가장 큰 영향을 미칩니다. Turbopack은 96.3% 빠른 Fast Refresh를 제공합니다.
이는 증분 계산 엔진 덕분입니다. 수천 개의 모듈 중 변경된 함수 하나만 재계산하기 때문에, 프로젝트 크기와 무관하게 거의 즉시 반영됩니다.
프로덕션 빌드에서는 Webpack 대비 2-5배 빠른 빌드 속도를 보여줍니다. FS 캐싱이 안정화된 Next.js 16.1 이후에는 최대 14배 빠른 재빌드가 가능합니다.
// 첫 번째 빌드: 전체 컴파일 (2-5x 빠름)
// pnpm build → 20초 (Webpack 기준 80초)
// 두 번째 빌드: 캐시 활용 (최대 14x 빠름)
// 코드 일부 수정 후 pnpm build → 6초 (Webpack 기준 80초)FS 캐싱은 빌드 결과를 파일 시스템에 영속적으로 저장하여, 다음 빌드 시 변경되지 않은 부분을 재사용합니다. CI/CD 환경에서 캐시를 공유하면 효과가 극대화됩니다.
FS 캐싱은 Next.js 16.1에서 안정화되어 기본 활성화됩니다. 이전 버전에서는 next.config.ts에서 실험적 옵션을 활성화해야 합니다.
Next.js 16부터 Turbopack이 기본 번들러이므로 별도 설정 없이 사용할 수 있습니다. Turbopack 관련 설정은 next.config.ts의 최상위 turbopack 키에서 관리합니다. 이전 버전의 experimental.turbo에서 승격된 것입니다.
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
// Next.js 16: 최상위 turbopack 키
turbopack: {
// Webpack loader를 Turbopack에서 사용
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
// 모듈 resolve alias
resolveAlias: {
// 특정 모듈을 다른 모듈로 대체
'old-module': 'new-module',
// Turbopack에서만 사용할 모듈 교체
canvas: './empty-module.ts',
},
// resolve 확장자 우선순위
resolveExtensions: ['.tsx', '.ts', '.jsx', '.js', '.json'],
},
};
export default nextConfig;Next.js 15에서는 experimental.turbo 키를 사용합니다. Next.js 16으로 업그레이드할 때 설정 경로를 변경해야 합니다. 두 키가 동시에 존재하면 turbopack이 우선합니다.
Turbopack은 일부 Webpack Loader를 직접 지원합니다. rules 설정으로 특정 파일 확장자에 Webpack Loader를 매핑할 수 있습니다. 다만, 모든 Loader가 호환되는 것은 아닙니다. Turbopack 팀이 공식적으로 테스트한 Loader 목록은 다음과 같습니다.
@svgr/webpack - SVG를 React 컴포넌트로 변환babel-loader - Babel 트랜스파일링 (SWC 미지원 플러그인용)sass-loader - SCSS/SASS 컴파일less-loader - Less 컴파일개발과 프로덕션 환경에서 다른 Turbopack 설정을 적용할 수 있습니다.
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
turbopack: {
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
// 소스맵 설정
// 개발: 기본 활성화
// 프로덕션: productionBrowserSourceMaps로 제어
},
// 프로덕션 소스맵 (Turbopack과 Webpack 모두 적용)
productionBrowserSourceMaps: false,
};
export default nextConfig;| 항목 | Webpack | Turbopack |
|---|---|---|
| 설정 위치 | webpack 키 | turbopack 키 |
| 커스텀 Loader | webpack.module.rules | turbopack.rules |
| Alias | webpack.resolve.alias | turbopack.resolveAlias |
| 플러그인 | webpack.plugins | 미지원 (대안 필요) |
| 설정 함수 | webpack: (config) => ... | 미지원 |
Turbopack으로 전환해도 다음 사항들은 그대로 유지됩니다.
images, env, redirects, rewrites, headers 등postcss.config.js가 그대로 동작.module.css 파일이 동일하게 동작NEXT_PUBLIC_* 접두사 규칙 동일tsconfig.json 동일Turbopack은 Webpack 플러그인 시스템을 지원하지 않습니다. 자주 사용되는 플러그인의 대안을 정리합니다.
// 1. webpack.DefinePlugin → Next.js 내장 환경 변수
// NEXT_PUBLIC_* 접두사 사용
// 2. CopyWebpackPlugin → public 디렉토리 활용
// 3. BundleAnalyzerPlugin → @next/bundle-analyzer
// next.config.ts에서 withBundleAnalyzer 래퍼 사용
// 4. MomentLocalesPlugin → dayjs나 date-fns로 마이그레이션
// 5. webpack.IgnorePlugin → turbopack.resolveAlias로 빈 모듈 매핑next.config.ts의 webpack 설정을 turbopack 키로 이전pnpm dev로 개발 서버 테스트pnpm build로 프로덕션 빌드 테스트Next.js 16.2에서 Turbopack의 Tree Shaking이 동적 임포트에 대해서도 적용됩니다. 이전에는 dynamic()이나 import()로 불러오는 모듈의 사용하지 않는 코드가 번들에 포함될 수 있었습니다.
// 16.2 이전: chart-library 전체가 번들에 포함될 수 있음
const Chart = dynamic(() =>
import('chart-library').then(mod => mod.LineChart)
);
// 16.2 이후: LineChart만 번들에 포함
// chart-library의 BarChart, PieChart 등은 제거됨이 개선은 특히 대규모 라이브러리를 동적으로 불러올 때 번들 크기를 크게 줄입니다.
Next.js 16.2에서 Turbopack이 SRI(Subresource Integrity)를 지원합니다. SRI는 브라우저가 스크립트를 실행하기 전에 해시값을 검증하여, 변조된 스크립트의 실행을 방지합니다.
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
experimental: {
sri: {
algorithm: 'sha256',
},
},
};
export default nextConfig;설정을 활성화하면, 생성된 스크립트 태그에 integrity 속성이 자동으로 추가됩니다.
<script
src="/_next/static/chunks/app/page-abc123.js"
integrity="sha256-abcdef123456..."
crossorigin="anonymous"
></script>CDN을 사용하는 환경이나 보안 요구사항이 높은 애플리케이션에서 유용합니다.
Turbopack에서 해결되지 않는 문제가 있다면, --webpack 플래그로 Webpack을 사용할 수 있습니다.
# 개발 서버
next dev --webpack
# 프로덕션 빌드
next build --webpack마이그레이션 기간에는 CI/CD에서 두 번들러로 모두 빌드하여 결과를 비교하는 것이 안전합니다. 차이가 없음을 확인한 후 Webpack 빌드를 제거합니다.
Turbopack이 모든 면에서 Webpack보다 우수한 것은 아닙니다. Cal.com 팀의 벤치마크에서 보고된 트레이드오프를 살펴봅니다.
Cal.com의 프로덕션 빌드에서 Turbopack이 생성한 공유 청크(shared chunks)의 총 크기가 Webpack보다 약간 큰 것으로 보고되었습니다. 이는 Turbopack의 청크 분할 전략이 아직 Webpack만큼 세밀하게 최적화되지 않았기 때문입니다.
| 지표 | Webpack | Turbopack | 비고 |
|---|---|---|---|
| 빌드 시간 | 기준 | 2-5x 빠름 | Turbopack 우세 |
| Fast Refresh | 기준 | 96.3% 빠름 | Turbopack 우세 |
| 공유 청크 크기 | 기준 | 약간 큼 | Webpack 우세 |
| 개별 페이지 크기 | 동일 | 동일 | 동등 |
공유 청크 크기 차이는 대부분의 애플리케이션에서 체감할 수 없는 수준입니다. CDN 캐싱이 적용된 환경에서는 실질적인 성능 차이가 거의 없습니다. Turbopack 팀은 청크 분할 최적화를 지속적으로 개선하고 있습니다.
Turbopack은 Webpack의 전체 플러그인 생태계를 지원하지 않습니다. 다음과 같은 경우에는 Webpack이 여전히 필요할 수 있습니다.
일반적인 Next.js 프로젝트의 Turbopack 설정을 종합한 예시입니다.
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
turbopack: {
// SVG를 React 컴포넌트로 사용
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
// 모듈 별칭
resolveAlias: {
// Node.js 전용 모듈을 브라우저 환경에서 빈 모듈로 대체
fs: { browser: './empty-module.ts' },
// 경로 별칭 (tsconfig paths와 별도)
'@icons': './src/assets/icons',
},
},
// SRI 활성화
experimental: {
sri: {
algorithm: 'sha256',
},
},
// 프로덕션 소스맵 비활성화
productionBrowserSourceMaps: false,
};
export default nextConfig;// Node.js 전용 모듈의 브라우저 대체용
export default {};next.config.ts의 최상위 turbopack 키에서 관리하며, Webpack Loader 일부를 rules로 호환 사용할 수 있습니다.--webpack 플래그로 언제든 Webpack으로 돌아갈 수 있습니다.다음 장에서는 미들웨어와 Proxy 고급 패턴을 다룹니다. Next.js 16의 proxy.ts가 기존 middleware.ts를 어떻게 발전시켰는지, 인증, i18n, 기능 플래그 등 실전 패턴을 살펴봅니다.
이 글이 도움이 되셨나요?
관련 주제 더 보기
Next.js의 미들웨어 진화를 다룹니다. middleware.ts에서 proxy.ts로의 전환, 인증, i18n, A/B 테스팅, 속도 제한 등 고급 패턴을 살펴봅니다.
Next.js의 Server Actions와 폼 처리를 다룹니다. next/form, useActionState, 보안, revalidation 통합, after() API, 고급 뮤테이션 패턴을 살펴봅니다.
Next.js의 이미지, 폰트, 메타데이터 최적화를 다룹니다. next/image, next/font, generateMetadata, OG 이미지 생성, Core Web Vitals 전략을 살펴봅니다.