Next.js 16 Turbopack 프로덕션 빌드 — 속도 개선과 번들 크기 증가 리스크 사이에서 도입 시점 판단하기
Turbopack이 기본 번들러가 된 지금, 팀 내에서 슬슬 얘기가 나오기 시작했을 겁니다. "그냥 써도 돼?" 저도 처음엔 릴리즈 노트만 훑고 바로 적용했다가, 번들 크기가 묘하게 늘어난 걸 뒤늦게 발견하고 식은땀을 흘린 기억이 있습니다. App Router 공유 클라이언트 청크가 조용히 커져 있었는데, CI 빌드 속도가 개선된 것에 만족하고 넘어갔다가 나중에 Core Web Vitals — 사용자가 페이지의 첫 콘텐츠를 보기까지의 시간(FCP)과 가장 큰 요소가 렌더링되는 시간(LCP) — 점수가 뒤통수를 쳤습니다.
이 글은 커스텀 Webpack 설정 개수와 번들 분석 수치를 기준으로 Turbopack 도입 시점을 판단하는 기준을 Next.js 앱을 운영 중인 프론트엔드 개발자에게 공유하기 위해 씁니다. 추상적인 "빠르다"가 아니라, 어떤 상황에서 빠르고 어떤 상황에서 번들 크기가 커지는지를 수치와 함께 살펴봅니다.
핵심 개념
Turbo Engine: "필요한 것만 다시 계산한다"는 원칙
Webpack과 Turbopack을 단순히 "느린 것 vs 빠른 것"으로 나누면 중요한 차이를 놓칩니다. 설계 철학 자체가 다릅니다.
Webpack은 v5부터 파일 시스템 캐시(cache 옵션)를 지원해서, 이전 빌드 결과를 디스크에 저장해두고 재사용합니다. 하지만 이 캐시는 파일 단위입니다. 파일 하나가 바뀌면 해당 파일과 연결된 모듈 그래프를 다시 처리해야 합니다.
Turbopack의 Turbo Engine은 한 단계 더 세밀합니다. 빌드 과정에서 수행하는 모든 함수 — AST 파싱, 코드 변환, 청킹 등 — 의 결과를 각 함수별 캐시 단위(값 셀, value cell)에 저장합니다. 파일이 바뀌면 그 파일에 의존하는 함수들만 "더티(dirty)"로 표시되고, 나머지는 이미 계산된 값을 그대로 씁니다. Webpack의 파일 단위 캐시가 "파일 전체를 다시 굽느냐"라면, Turbopack은 "재료 손질 단계부터 어느 단계까지 다시 할지"를 판단하는 방식입니다.
계산 그래프는 이런 구조입니다:
번들 출력 (루트)
→ 청킹 결과
→ 부분 변환 모듈
→ AST 변환 결과
→ 소스 파일 (리프)
Button.tsx를 수정하면 AST 파싱 → 변환 → 해당 청크만 재계산되고, 수천 개의 다른 모듈은 캐시된 값 셀을 그대로 씁니다.
증분(incremental) 컴파일이란? 전체를 다시 빌드하지 않고, 변경된 부분과 그에 의존하는 부분만 선택적으로 재계산하는 방식입니다. Webpack도 파일 단위 캐싱을 지원하지만, Turbopack은 이를 함수 단위까지 세분화한 것이 차별점입니다.
프로덕션 빌드 지원은 언제부터?
next dev에서는 꽤 오래전부터 쓸 수 있었지만, 프로덕션 빌드(next build)가 안정화된 건 비교적 최근입니다.
| 버전 | 시점 | 주요 변화 |
|---|---|---|
| Next.js 15.5 | 2025년 9월 | next build Turbopack 베타 지원 시작 |
| Next.js 16 | 2025년 10월 | 개발·프로덕션 모두 Turbopack이 기본 번들러 |
| Next.js 16.1 | 2025년 12월 | 파일 시스템 캐시 안정화, 번들 분석기 공식 통합 |
| Next.js 16.2 | 2026년 3월 | next dev 시작 속도 ~400% 개선, Build Adapters API 안정화 |
Next.js 16부터는 next.config.ts의 webpack 키 자체가 deprecated입니다. Webpack으로 되돌리려면 CLI 플래그를 명시해야 합니다.
이 증분 컴파일 원리가 실제 CI 환경에서 어떤 숫자로 나타나는지, 코드와 함께 살펴보겠습니다.
실전 적용
파일 시스템 캐시 설정으로 CI 빌드 시간 줄이기
파일 시스템 캐시(turbopackFileSystemCacheForBuild)는 현재 opt-in 상태입니다. .next 폴더에 컴파일 중간 결과를 저장해두고, 다음 빌드에서 변경되지 않은 부분을 재사용하는 방식입니다.
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
turbopackFileSystemCacheForBuild: true,
},
}
export default nextConfigVercel 공식 측정치를 보면 효과가 상당합니다:
| 프로젝트 | 콜드 빌드 | 캐시 적용 후 | 개선율 |
|---|---|---|---|
| react.dev | 3.7초 | 380ms | ~10× |
| nextjs.org | 3.5초 | 700ms | ~5× |
| 대형 내부 앱 | 15초 | 1.1초 | ~14× |
이 효과는 프로젝트 규모가 클수록, 반복 빌드가 많은 CI 환경일수록 극대화됩니다. 수천 개의 컴포넌트를 가진 모노레포라면 이 옵션 하나만으로 CI 빌드를 수 분에서 수십 초대로 단축할 수 있습니다.
단, 한 가지 주의할 점이 있습니다. CI에서 .next 폴더 캐시를 복원하지 않으면 이 설정은 효과가 없습니다. GitHub Actions라면 아래처럼 캐시 스텝을 함께 추가해야 합니다:
# .github/workflows/build.yml
- name: Cache .next folder
uses: actions/cache@v4
with:
path: .next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/*.ts', '**/*.tsx') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('**/pnpm-lock.yaml') }}-Webpack 롤백 준비
커스텀 Webpack 설정이 많은 프로젝트라면, 일시적으로 롤백해두면서 마이그레이션 계획을 세우는 것을 권장합니다.
# CLI에서 Webpack으로 롤백
next build --webpack{
"scripts": {
"build": "next build",
"build:webpack": "next build --webpack"
}
}두 스크립트를 병행해두면 번들 크기와 빌드 시간을 나란히 측정하기 좋습니다. 마이그레이션 준비 단계에서 유용한 패턴입니다.
번들 크기 검증 — 도입 전후 수치 비교
빌드 속도만 확인하고 넘어가면 위험합니다. @next/bundle-analyzer로 청크 크기를 반드시 확인해보는 것이 좋습니다.
Next.js 16.1부터 번들 분석기가 공식 통합되어 별도 패키지 없이도 활성화할 수 있습니다. 아래 코드는 기존 방식인 @next/bundle-analyzer 래퍼 패턴입니다. 16.1 이상이라면 이 래퍼 없이 next.config.ts의 bundleAnalyzer: { enabled: true } 키만으로도 동일하게 활성화할 수 있습니다.
// next.config.ts (래퍼 방식 — @next/bundle-analyzer 패키지 설치 필요)
import bundleAnalyzer from '@next/bundle-analyzer'
import type { NextConfig } from 'next'
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
const nextConfig: NextConfig = {
experimental: {
turbopackFileSystemCacheForBuild: true,
},
}
export default withBundleAnalyzer(nextConfig)ANALYZE=true next build왜 번들 크기를 따로 확인해야 하나요? CatchMetrics의 실측 데이터에 따르면, Next.js 15.5 기준으로 App Router의 공유 클라이언트 청크가 약 211 kB 증가했고, 151개 라우트에서 First-load JS 중간값이 +279 kB 늘어난 사례가 있습니다. 빌드 속도 개선이 초기 로드 성능 저하로 이어질 수 있어서 반드시 검증이 필요합니다.
장단점 분석
장점
특히 주목할 것은 파일 시스템 캐시입니다. 개발 서버에서만 누리던 빠른 증분 컴파일 효과를 프로덕션 빌드에서도 경험할 수 있게 해주는 핵심 기능이고, 프로젝트 규모가 클수록 효과가 기하급수적으로 커집니다.
| 항목 | 내용 |
|---|---|
| 증분 컴파일 | 변경된 모듈과 의존 함수만 재계산 — 코드베이스가 클수록 효과 극대화 |
| 병렬 처리 | Rust의 멀티코어 활용으로 CPU 집약 작업을 코어 단위로 분산 처리 |
| 파일 시스템 캐시 | 프로세스 재시작 간에도 캐시 유지 — CI 반복 빌드 환경에서 특히 유효 |
| SWC 통합 | TypeScript, JSX 트랜스파일을 Rust 수준에서 처리 — 별도 도구 불필요 |
| 콜드 빌드 속도 | Webpack 대비 약 19% 빠름 (Next.js 15.5.2, 약 15,000 LoC 규모 앱 기준, CatchMetrics 측정), 50,000 LoC 이상에서 최대 85% 단축 사례 보고 |
단점 및 주의사항
번들 크기 증가 가능성이 현재 가장 큰 주의 항목입니다. tree shaking 제한과 맞물리면 의도치 않게 불필요한 코드가 번들에 포함될 수 있습니다. CommonJS와 배럴 파일의 경우 원인이 달라 구분해서 이해해두는 것이 좋습니다.
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| Webpack 플러그인 미지원 | CSS 모듈 플러그인, SVG 처리 플러그인 등 Webpack 생태계 호환 불가 | 의존 플러그인 목록화 후 Turbopack 동등 옵션 매핑 확인 |
| Tree shaking 제한 (CommonJS) | CommonJS는 정적 분석이 불가능해 ESM 기반 tree shaking이 원천 불가 | 라이브러리의 ESM 버전 존재 여부 확인 후 전환 |
| Tree shaking 제한 (배럴 파일) | index.ts에서 다수 모듈을 re-export하는 배럴 파일은 tree shaking이 비효율적으로 동작 |
배럴 파일 최소화, 직접 경로 import로 전환 |
| 번들 크기 증가 가능성 | App Router 공유 청크 크기가 늘어날 수 있음 | @next/bundle-analyzer로 마이그레이션 전후 비교 필수 |
| 로더 부분 호환 | resourceQuery 기반 로더, 이미지·스타일시트 변환 로더 미지원 — JS 반환 로더만 지원 |
특수 로더 의존성 사전 점검 |
| Next.js 결합 구조 | 현재 Next.js 없이 독립 사용 불가 | 독립 번들러가 필요하다면 Vite를 고려해볼 수 있습니다 |
배럴(barrel) 파일이란?
index.ts에서 여러 모듈을 한꺼번에 re-export하는 패턴입니다(export { Button } from './Button'). 편의성은 높지만 번들러가 사용하지 않는 코드를 제거하기 어려워집니다. CommonJS는 동적 구조로 인해 정적 분석 자체가 불가능한 반면, 배럴 파일은 ESM이더라도 의존 관계가 복잡해져 tree shaking 효율이 떨어집니다.
실무에서 가장 흔한 실수
-
빌드 속도만 측정하고 번들 크기 검증을 생략하는 경우입니다. CI 빌드가 빨라진 것에 만족하다가 실제 사용자의 초기 로드 속도가 나빠지는 상황이 생길 수 있습니다. 첫 콘텐츠 표시 시간(FCP)과 최대 요소 렌더링 시간(LCP) 수치를 마이그레이션 전후로 비교해보는 것이 좋습니다.
-
커스텀 Webpack 설정을 미리 목록화하지 않은 채 업그레이드하는 경우입니다. 어떤 팀은
next.config.ts의webpack콜백 안에 SVG 로더와 커스텀 글꼴 처리가 묻혀 있는 줄 모르고 올렸다가, 특정 페이지에서 아이콘이 전혀 표시되지 않는 상황을 스테이징에서야 발견했습니다. 특수 로더는 Turbopack에서 조용히 무시되는 경우가 있어, 에러 없이 잘못된 결과가 나올 수 있습니다. -
CI 캐시 설정 없이 파일 시스템 캐시만 활성화하는 경우입니다. 어떤 팀은
turbopackFileSystemCacheForBuild를 켰는데도 빌드 시간이 전혀 줄지 않아 의아해했는데, 원인은 GitHub Actions에서.next폴더가 매 실행마다 초기화되고 있었던 것이었습니다. 앞서 소개한actions/cache스텝과 함께 적용해야 효과를 볼 수 있습니다.
마치며
Turbopack 프로덕션 빌드는 "언제 도입할까"가 아니라 "우리 스택에서 무엇을 확인해야 하는가"의 문제입니다. 커스텀 Webpack 설정이 거의 없고, 대형 코드베이스에서 CI 빌드 시간이 병목인 팀이라면 지금 바로 시도해볼 수 있습니다. 반면 특수 로더나 Webpack 플러그인 의존도가 높다면, 마이그레이션 준비 단계를 먼저 거치는 것이 현명합니다.
지금 바로 시작해볼 수 있는 3단계입니다:
next.config.ts의webpack콜백과 특수 로더 목록을 뽑아두고, Turbopack API 레퍼런스에서 동등한 옵션이 있는지 확인해 보세요.- 스테이징 환경에서
ANALYZE=true next build를 실행해 번들 크기를 Webpack 결과와 나란히 비교해 보세요. - 문제가 없다면
turbopackFileSystemCacheForBuild: true와 GitHub Actions 캐시 설정을 함께 적용해 보세요.
이 글에서 번들 크기가 늘어나는 현상은 확인했지만, tree shaking이 실제로 어느 시점에 실패하고 어떻게 복구할 수 있는지는 아직 다루지 못했습니다.
참고 자료
- Inside Turbopack: Building Faster by Building Less | Next.js 공식 블로그
- Next.js 16 릴리즈 노트 | Next.js
- Next.js 16.1 릴리즈 노트 | Next.js
- Next.js 16.2 Turbopack 분석 | Next.js
- Turbopack API 레퍼런스 | Next.js 공식 문서
- turbopackFileSystemCache 설정 옵션 | Next.js 공식 문서
- Webpack vs Turbopack 성능 비교 | CatchMetrics
- Turbopack is Finally Stable: 5 Real-World Benchmarks vs. Webpack | Medium
- Turbopack in 2026: The Complete Guide | DEV Community
- Turbopack Next.js 16 Migration: Real Tradeoffs, Not Marketing | ByteIota