Turbopack: Next.js 16.2의 새로운 기능들
Turbopack이 Next.js의 기본 번들러가 된 지 두 번의 릴리스가 지난 지금, 우리는 성능 개선과 버그 수정, 그리고 다양한 기능 패리티 달성에 집중하고 있습니다.
Next.js 16.2에서 새롭게 선보이는 주요 기능들을 소개해드리겠습니다.
- Server Fast Refresh: 세밀한 서버 측 핫 리로딩
- Web Worker Origin: Workers 내 WASM 라이브러리 지원 확대
- Subresource Integrity 지원: JavaScript 파일에 대한 무결성 검증
- 동적 임포트 Tree Shaking: 동적 import()에서 사용하지 않는 내보내기 제거
- Inline Loader 설정: 임포트 어트리뷰트를 통한 개별 로더 설정
- Lightning CSS 설정: 실험적 LightningCSS 설정 옵션
- 로그 필터링: ignoreIssue를 통한 불필요한 로그 및 경고 제거
- postcss.config.ts 지원: TypeScript PostCSS 설정
- 성능 개선 및 버그 수정: 200여 개의 변경 및 버그 수정
다음 릴리스에서는 컴파일러 성능 향상과 메모리 사용량 감소에 중점을 두겠습니다.
Server Fast Refresh: 더 똑똑한 서버 핫 리로딩
개발 중 서버 측 코드가 어떻게 리로드되는지 완전히 재설계했습니다.
기존 방식에서는 변경된 모듈의 require.cache를 지웠고, 그 모듈과 연결된 모든 다른 모듈도 함께 초기화했습니다. 이 방식은 변경되지 않은 node_modules까지 포함해서 필요 이상으로 많은 코드를 리로드했거든요.
새로운 시스템은 브라우저에서 사용되던 Fast Refresh 접근 방식을 서버 코드에도 가져왔습니다. Turbopack이 모듈 그래프를 정확히 파악하기 때문에 실제로 변경된 모듈만 리로드되고 나머지는 그대로 유지됩니다. 덕분에 서버 측 핫 리로딩의 효율성이 크게 향상됐습니다.
실제 Next.js 애플리케이션에서 측정한 결과, 애플리케이션 새로고침은 67~100% 더 빨라졌고 Next.js 내 컴파일 시간은 400~900% 개선됐습니다. 이러한 성능 개선은 작은 "hello world" 스타터 프로젝트부터 vercel.com 같은 대규모 사이트까지 일관되게 나타났습니다.
| 항목 | 개선 전 | 개선 후 | 개선율 |
|---|---|---|---|
| Next.js | 40ms | 2.7ms | 93% 단축 |
| 애플리케이션 | 19ms | 9.7ms | 49% 단축 |
이제 이 기능을 모든 개발자에게 기본적으로 활성화했습니다. 다만 Proxy와 Route Handlers는 아직 기존 방식을 사용하고 있으며, 향후 릴리스에서 지원할 예정입니다. 새로운 기능에 대한 피드백이나 문제점이 있으면 GitHub에서 알려주세요.
Web Worker Origin: WASM 라이브러리 호환성 개선
예전에는 Web Workers를 blob:// URL로 부트스트랩했습니다. 워커 로딩을 간단히 할 수 있었지만, location.origin 값이 비어있다는 문제가 있었거든요. 서드파티 라이브러리의 importScripts()나 fetch()를 사용하려던 워커 코드는 수정 없이는 요청을 해결할 수 없었습니다.
이제 업데이트된 Worker 부트스트랩 코드로 origin이 제대로 된 도메인명을 가리키고, 상대 경로 fetch도 정상 동작합니다. 이전 버전에서 Worker 내에서 WASM 코드 실행에 문제가 있었던 분들은 이제 문제없이 사용할 수 있을 겁니다.
Subresource Integrity 지원: 스크립트 무결성 검증
Turbopack이 이제 Subresource Integrity(SRI)를 지원합니다. SRI는 빌드 시점에 JavaScript 파일의 암호화 해시를 생성해서 브라우저가 파일 변조 여부를 검증할 수 있게 합니다.
브라우저의 Content Security Policy(CSP) 기능을 사용하면 실행할 수 있는 JavaScript를 제한해서 보안 문제의 전체 범주를 없앨 수 있습니다. 다만 nonce 기반의 일반적인 CSP 구현 방식은 모든 페이지를 동적으로 렌더링해야 하기 때문에 성능에 영향을 줍니다. Subresource Integrity는 다른 접근 방식인데요, 각 스크립트의 해시를 미리 계산해서 승인된 해시를 가진 스크립트만 브라우저가 실행하도록 제한합니다.
// next.config.js
const nextConfig = {
experimental: {
sri: {
algorithm: 'sha256',
},
},
};
module.exports = nextConfig;
동적 임포트의 Tree Shaking
Turbopack이 이제 구조 분해 동적 임포트도 정적 임포트처럼 tree shaking합니다. 번들에서 사용하지 않는 내보내기가 제거됩니다.
const { cat } = await import('./lib');
이제 위 코드는 tree shaking 관점에서 정적 임포트와 동일합니다. ./lib에서 사용되지 않는 내보내기는 모두 제거되죠.
Inline Loader 설정: 임포트별 로더 커스터마이징
Turbopack이 이제 import 어트리뷰트를 통해 개별 임포트에 로더를 설정할 수 있습니다. turbopack.rules에서 전역으로 로더를 적용하는 대신 with 절을 사용해서 각 임포트에 설정하세요.
import rawText from './data.txt' with {
turbopackLoader: 'raw-loader',
turbopackAs: '*.js',
};
import value from './data.js' with {
turbopackLoader: 'string-replace-loader',
turbopackLoaderOptions: '{"search":"PLACEHOLDER","replace":"replaced value"}',
};
특정 임포트만 특수한 처리가 필요할 때 유용합니다. 같은 파일 타입의 다른 임포트에 영향을 주지 않으니까요. 사용 가능한 어트리뷰트는 turbopackLoader, turbopackLoaderOptions, turbopackAs, turbopackModuleType입니다.
가능하면 next.config.ts에서 로더를 설정하는 것을 권장합니다. inline 로더를 사용한 코드는 이식성이 떨어지거든요. 이 옵션은 플러그인이나 로더로 생성된 코드에서 더 유용합니다.
Lightning CSS 설정: 빠른 CSS 변환
Lightning CSS는 Turbopack이 사용하는 Rust 기반의 빠른 CSS 트랜스포머입니다. 이번 릴리스에서 Lightning CSS 설정 옵션을 실험적으로 노출했습니다.