Turbopack 완벽 가이드 — Webpack 대체, 증분 컴파일, Next.js 최적화

Turbopack 완벽 가이드 — Webpack 대체, 증분 컴파일, Next.js 최적화

이 글의 핵심

Turbopack이 Webpack 대비 개발 서버·HMR에서 이점을 내는 구조적 이유와, Next.js 14 이상에서의 활성화 방법, next.config의 turbopack 옵션(rules·resolveAlias·root), 모노레포 최적화, 커스텀 Webpack 설정을 걷어내는 마이그레이션 절차를 실무 기준으로 연결합니다.

이 글의 핵심

Turbopack은 Vercel이 Webpack 후속으로 개발한 Rust 기반 번들러이며, Next.js에서는 주로 next dev 개발 경험(시작 시간, HMR, 증분 컴파일)을 바꾸는 축으로 자리 잡았습니다. 본 글은 마케팅 수치를 나열하는 데 그치지 않고, Webpack과의 아키텍처 차이, 증분 컴파일이 성립하는 조건, next.config에서의 실제 설정 키, 모노레포에서의 루트·해석 범위, Webpack 커스텀을 제거·이전하는 절차를 한 흐름으로 묶습니다.

선행 지식: Next.js App Router, next.config.js/next.config.ts, npm/pnpm/yarn 워크스페이스, Webpack의 module.rules 개념을 알고 있다고 가정합니다.


1. Turbopack과 Webpack: 무엇이 달라 성능이 갈리나

1.1 구현 언어와 병렬성

Webpack은 전통적으로 JavaScript 런타임(Node.js) 위에서 동작하며, 플러그인 생태계가 방대합니다. 대규모 그래프에서는 작업 큐·캐시·스레드 풀(예: thread-loader) 등으로 속도를 보완하지만, 핫 패스에서의 오버헤드가 누적되기 쉽습니다.

TurbopackRust로 작성되어 메모리 안전성과 세밀한 병렬 처리에 유리한 편입니다. Next.js에 내장된 Turbopack은 개발 모드에서 모듈 그래프를 필요한 만큼만 확장하고, 변경된 부분만 다시 계산하는 방향으로 최적화되어 있습니다.

1.2 체감 성능 비교(개발 서버 기준)

정확한 수치는 CPU 코어 수, 디스크 속도, 의존성 규모, 소스 맵, 타입스크립트 프로젝트 참조에 따라 달라집니다. 다만 공통적으로 보고되는 패턴은 다음과 같습니다.

관점Webpack(개발)Turbopack(개발)
콜드 스타트전체 또는 큰 단위 그래프 준비에 시간 소요초기에 더 얕은 그래프로 시작하는 경향
HMR 지연변경 전파·무효화 범위가 넓으면 초 단위까지 가능변경 국소화에 유리한 설계
설정 이식webpack() 훅으로 세밀 제어Webpack 설정 직접 적용 불가, turbopack 옵션으로 재구성

프로덕션 next build는 버전에 따라 Turbopack 기반 빌드가 실험·알파 단계로 제공될 수 있습니다. 운영 배포 전에는 반드시 해당 Next.js 버전의 공식 릴리스 노트에서 권장 사항과 제한 사항을 확인하십시오. 안정성이 최우선이면 프로덕션은 Webpack 빌드, 개발만 Turbopack으로 두는 전략이 여전히 보수적으로 타당합니다.


2. Next.js 14 이상에서 Turbopack 활성화

2.1 개발 서버 실행

Next.js 14 이후 버전에서는 CLI 플래그로 개발 번들러를 선택할 수 있습니다.

# Turbopack으로 개발 서버 실행
next dev --turbo

package.json 스크립트에 고정하는 편이 팀 규칙을 맞추기에 좋습니다.

{
  "scripts": {
    "dev": "next dev --turbo",
    "build": "next build",
    "start": "next start"
  }
}

2.2 설정 키 이름 변화: experimental.turbo → turbopack

과거 Next.js에서는 experimental.turbo 아래에 옵션을 두었으나, 최신 문서 기준으로는 turbopack 최상위 키를 사용합니다. experimental.turbo제거 예정이므로, 기존 프로젝트는 공식 codemod로 이전하는 것이 안전합니다.

npx @next/codemod@latest next-experimental-turbo-to-turbopack .

2.3 TypeScript 설정 예시

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  // Turbopack 전용 옵션 (Next 15.3+ 문서 기준 키 이름)
  turbopack: {
    // 아래 섹션에서 상세히 다룸
  },
}

export default nextConfig

3. 증분 컴파일: 왜 “조금 고쳤는데” 빠른가

3.1 개념

증분 컴파일(incremental compilation)은 매번 전체 프로젝트를 처음부터 다시 빌드하지 않고, 변경된 모듈과 그에 의존하는 최소 집합만 다시 계산하는 방식입니다. TypeScript의 --incremental과 용어는 비슷하지만, 여기서는 번들러 그래프 상의 무효화(invalidation)에 초점을 둡니다.

3.2 Turbopack 관점에서의 직관

  1. 모듈 그래프는 파일 간 import/export로 연결됩니다.
  2. 파일 하나가 바뀌면, 해당 모듈과 이를 가져가는 상위 체인만 다시 번들에 반영하면 됩니다.
  3. 개발 모드에서는 사용자가 아직 열지 않은 라우트까지 미리 전부 컴파일하지 않는 등 지연(lazy) 전략을 취하기도 합니다(프로젝트 구조와 Next 버전에 따라 세부 동작은 다를 수 있음).

3.3 오해하기 쉬운 점

증분이 항상 이론상 최소만 다시 컴파일한다는 뜻은 아닙니다. 전역 스타일, 사이드 이펙트가 큰 엔트리, 광범위한 바렐(barrel) index.ts, 거대한 공용 타입 등은 무효화 범위를 넓힙니다. 따라서 “Turbopack으로 바꿨는데도 느리다”면 의존성 구조 리팩터링을 함께 봐야 합니다.


4. 커스텀 로더와 “플러그인”: Webpack과의 대응 관계

4.1 Webpack 플러그인은 그대로 옮기기 어렵다

Webpack 생태계의 핵심 확장 수단인 플러그인 API는 Turbopack에 1:1 대응되지 않습니다. Next.js에서 Turbopack을 쓸 때는 다음을 전제로 해야 합니다.

  • webpack(config, { isServer, ... }) 내부 수정은 Turbopack 경로에 적용되지 않습니다.
  • 대신 turbopack.rules, resolveAlias, resolveExtensions, 필요 시 import attributes로 로더를 좁게 적용합니다.

4.2 지원되는 Webpack 로더(예시)

공식 문서에서 테스트가 확인된 로더로는 @svgr/webpack, yaml-loader, raw-loader, string-replace-loader, graphql-tag/loader 등이 거론됩니다. 또한 Babel·Sass 등은 프로젝트 설정이 감지되면 자동으로 연계되는 경우가 많습니다.

주의: 로더 API 중 this.importModule, emitFile미구현 기능에 의존하는 로더는 동작하지 않을 수 있습니다.

4.3 turbopack.rules로 SVGR 예시

/** @type {import('next').NextConfig} */
const nextConfig = {
  turbopack: {
    rules: {
      '*.svg': {
        loaders: ['@svgr/webpack'],
        as: '*.js',
      },
    },
  },
}

module.exports = nextConfig

as: '*.js'는 로더 출력을 자바스크립트 모듈로 취급하겠다는 뜻입니다. 조건부로 클라이언트에만 적용하려면 문서의 condition: 'browser' 패턴을 검토합니다.

4.4 개별 import에만 로더 적용(import attributes)

특정 파일에만 로더를 적용하려면 with 절을 사용할 수 있습니다(Next 문서의 Turbopack 전용 속성).

import rawText from '../data.txt' with {
  turbopackLoader: 'raw-loader',
  turbopackAs: '*.js',
}

이 방식은 Webpack과 호환되지 않을 수 있으므로, 듀얼 빌드 전략을 쓰는 팀은 분기 조건을 명확히 해야 합니다.

4.5 resolveAlias로 패키지 치환

module.exports = {
  turbopack: {
    resolveAlias: {
      underscore: 'lodash',
    },
  },
}

Webpack의 resolve.alias와 유사하지만, 적용 대상 번들러가 Turbopack일 때만 의미가 있습니다.


5. 모노레포 환경 최적화

모노레포에서 Turbopack이 자주 부딪히는 지점은 “프로젝트 루트 밖의 파일 해석”로컬 패키지의 트랜스파일입니다.

5.1 turbopack.root

Turbopack은 기본적으로 애플리케이션 루트 밖 파일을 해석하지 않도록 설계되어, 캐시 무효화와 파일 감시 비용을 줄입니다. pnpm 워크스페이스처럼 패키지가 상위 디렉터리에 퍼져 있으면 root를 공통 상위로 올리는 선택이 필요할 수 있습니다.

const path = require('path')

module.exports = {
  turbopack: {
    root: path.join(__dirname, '..'),
  },
}

npm link로 연결한 패키지를 개발 중이라면, 문서에서 권장하듯 링크된 의존성의 부모까지 포함되도록 root를 조정해야 합니다. 이는 감시 범위가 넓어진다는 트레이드오프가 있습니다.

5.2 transpilePackages

워크스페이스 내부의 TypeScript/모던 문법 패키지를 앱이 직접 소비할 때는 transpilePackages에 패키지 이름을 명시합니다. 이는 Webpack·Turbopack 공통으로 Next가 권장하는 패턴이며, “빌드되지 않은 소스 패키지”를 안정적으로 먹이기 위한 장치입니다.

/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: ['@myorg/ui', '@myorg/utils'],
}

module.exports = nextConfig

5.3 Turborepo와의 역할 분담

Turborepo태스크 오케스트레이션·원격 캐시에 가깝고, TurbopackNext 앱의 번들링에 가깝습니다. 이름이 비슷하여 혼동하기 쉬우나, 루트 turbo.json의 파이프라인next dev --turbo는 서로 다른 층위입니다. 모노레포에서는 turbolint/test/build를 캐시하고, 앱 단에서는 Turbopack으로 DX를 개선하는 식으로 역할을 나누는 경우가 많습니다.


6. 실전 마이그레이션 전략

6.1 사전 점검 체크리스트

  1. webpack() 커스텀 유무: 있다면 Turbopack에서 동등 기능이 있는지 문서·이슈 트래커로 확인합니다.
  2. 로더 의존성: rules로 이전 가능한지, 미지원 API에 걸리지 않는지 확인합니다.
  3. 환경 변수 주입: DefinePlugin 등 Webpack 전용을 쓰고 있다면 Next의 env·publicRuntimeConfig 등 공식 경로로 옮길 수 있는지 검토합니다.
  4. Babel vs SWC: 불필요한 Babel 체인은 제거할수록 개발·빌드 모두 유리한 경우가 많습니다.

6.2 단계적 롤아웃

  1. 로컬 한 명: next dev --turbo만 켜고 일상 개발을 1~2주 반복합니다.
  2. CI 스모크: next build는 기존(Webpack) 유지하되, 타입·lint·단위 테스트 파이프라인이 안정적인지 확인합니다.
  3. 팀 확대: 스크립트를 dev: "next dev --turbo"로 합의합니다.
  4. 프로덕션 Turbopack 빌드는 별도 검증 채널(스테이징, 카나리)에서만 시도합니다.

6.3 롤백 계획

package.jsondev:webpack처럼 이전 스크립트를 병행해 두면, 치명적 호환성 이슈가 발견될 때 즉시 되돌릴 수 있습니다.

{
  "scripts": {
    "dev": "next dev --turbo",
    "dev:webpack": "next dev"
  }
}

7. 트러블슈팅 요약

  • “설정이 반영이 안 된다”: Turbopack은 webpack() 변경을 읽지 않습니다. turbopack 키를 사용했는지, 캐시를 지웠는지 확인합니다.
  • “모노레포 패키지를 못 찾는다”: turbopack.root, transpilePackages, 패키지 exports 필드를 점검합니다.
  • “특정 로더만 실패한다”: 로더가 지원되지 않는 Webpack API에 의존하는지 이슈를 검색합니다.
  • “HMR이 여전히 느리다”: 거대한 단일 모듈, 과도한 barrel export, 전역 CSS 의존을 줄이는 쪽으로 구조를 재검토합니다.

8. 정리

Turbopack은 Next.js 개발 워크플로를 가속하는 강력한 옵션이지만, Webpack 생태계와 설정 자산을 그대로 들고 올 수 있는 도구가 아닙니다. 성능 이점을 최대화하려면 next dev --turbo 도입, turbopack 설정으로 로더·alias 재구성, 모노레포에서의 루트·트랜스파일 경계 정리, 프로덕션 Turbopack 빌드는 신중한 검증이라는 네 가지 축을 함께 다루어야 합니다. 이 글의 절차를 체크리스트로 삼아 팀 표준에 맞게 조정하시기 바랍니다.


참고