icon
9장 : 타입스크립트 컴파일러

증분 컴파일과 빌드 최적화


대규모 타입스크립트 프로젝트에서는 소스 코드의 양이 방대해지고, 그에 따라 전체 프로젝트를 컴파일하는 데 시간이 오래 걸리는 문제가 발생할 수 있습니다. 개발자가 코드를 수정할 때마다 오랜 시간 빌드를 기다려야 한다면 생산성이 크게 저해될 것입니다. 증분 컴파일(Incremental Compilation) 과 다양한 빌드 최적화 기법들은 이러한 문제를 해결하여 개발 워크플로우를 가속화하는 데 목적을 둡니다.


증분 컴파일

증분 컴파일(Incremental Compilation)은 TypeScript 3.4부터 도입된 기능으로, 이전 컴파일에서 변경된 파일과 그에 영향을 받는 파일만 다시 컴파일하는 방식입니다. 이를 통해 전체 프로젝트를 처음부터 다시 컴파일하는 것보다 훨씬 빠르게 빌드를 완료할 수 있습니다.

증분 컴파일을 활성화하려면 tsconfig.json 파일에 다음 옵션을 추가해야 합니다.

// tsconfig.json
{
  "compilerOptions": {
    "incremental": true, // 증분 컴파일 활성화
    "tsBuildInfoFile": "./.tsbuildinfo", // 증분 빌드 정보를 저장할 파일 경로 (선택 사항)
    // ... 다른 컴파일러 옵션 ...
  },
  // ...
}
  • "incremental": true: 이 옵션을 true로 설정하면 TypeScript 컴파일러는 이전에 컴파일된 정보를 기반으로 변경된 파일만 다시 처리합니다.
  • "tsBuildInfoFile" (선택 사항): 컴파일러는 증분 빌드에 필요한 정보를 tsbuildinfo라는 파일에 저장합니다. 기본적으로 outDirtsconfig.tsbuildinfo라는 이름으로 생성되지만, 이 옵션을 통해 파일의 이름과 경로를 커스터마이징할 수 있습니다. 일반적으로 이 파일은 .gitignore에 추가하여 버전 관리에서 제외합니다.

증분 컴파일의 원리

  1. 첫 번째 컴파일 시, TypeScript는 모든 소스 파일을 컴파일하고, tsbuildinfo 파일에 각 파일의 해시(Hash), 의존성 그래프, 타입 정보 등 증분 빌드에 필요한 메타데이터를 저장합니다.
  2. 이후 컴파일 시, TypeScript는 소스 파일의 변경 사항을 감지합니다.
  3. tsbuildinfo 파일에 저장된 정보와 비교하여 실제로 변경되거나 변경된 파일에 의해 영향을 받는 파일들만 식별합니다.
  4. 식별된 파일들만 다시 컴파일하고, 변경된 정보를 tsbuildinfo 파일에 업데이트합니다.

증분 컴파일은 개발 모드에서 (tsc --watch 또는 IDE의 백그라운드 컴파일) 특히 빛을 발합니다. 작은 코드 변경에 대해 거의 즉각적인 피드백을 받을 수 있습니다.


tsc --watch (Watch Mode)

개발 과정에서 코드를 수정할 때마다 수동으로 tsc 명령어를 실행하는 것은 번거롭습니다. tsc --watch (또는 tsc -w) 명령어는 파일 시스템 변경을 감지하여 변경된 파일만 자동으로 증분 컴파일하도록 합니다.

tsc --watch

이 명령어는 터미널에서 계속 실행되며, 파일을 저장할 때마다 자동으로 다시 컴파일 과정을 시작합니다. 이는 개발 워크플로우에서 매우 중요한 기능입니다.


프로젝트 레퍼런스와 증분 빌드

9.3절에서 다룬 프로젝트 레퍼런스(Project References) 는 증분 컴파일을 여러 하위 프로젝트(모듈)에 걸쳐 확장하는 개념입니다. tsconfig.json 파일에 composite: true를 설정한 하위 프로젝트들을 references로 연결하고, 최상위 tsconfig.json에서 이들을 참조합니다.

이 경우, 단순한 tsc 대신 tsc --build (또는 tsc -b) 명령어를 사용하여 증분 빌드를 수행합니다.

tsc --build

tsc --build는 다음과 같은 방식으로 증분 빌드를 최적화합니다.

  • 의존성 순서 파악: references에 정의된 프로젝트 간의 의존성 그래프를 분석하여 올바른 빌드 순서를 자동으로 결정합니다.
  • 하위 프로젝트별 증분 빌드: 각 하위 프로젝트에 대해 tsbuildinfo 파일을 사용하여 증분 컴파일을 수행합니다.
  • 변경된 프로젝트만 빌드: 하위 프로젝트의 소스 코드가 변경되었거나, 해당 프로젝트가 의존하는 프로젝트가 변경되었을 때만 해당 하위 프로젝트를 다시 빌드합니다. 예를 들어, common 프로젝트가 변경되면 ui 프로젝트도 다시 빌드되지만, ui 프로젝트만 변경되면 common 프로젝트는 다시 빌드되지 않습니다.

이는 모노레포와 같은 대규모 멀티-패키지 프로젝트에서 빌드 시간을 획기적으로 줄이는 데 핵심적인 역할을 합니다.


기타 빌드 최적화 기법

증분 컴파일 외에도 타입스크립트 빌드 시간을 최적화하는 데 도움이 되는 몇 가지 방법들이 있습니다.

  1. 적절한 tsconfig.json 설정

    • skipLibCheck: true: node_modules 내부의 .d.ts 파일에 대한 타입 검사를 건너뜁니다. 라이브러리의 타입 오류는 라이브러리 개발자의 몫이므로, 이 옵션을 켜면 컴파일 시간을 줄일 수 있습니다. (권장)
    • isolatedModules: true: 각 파일이 독립적인 모듈임을 확인하여 단일 파일 트랜스파일러(예: Babel, esbuild)와의 호환성을 높입니다. 이는 트랜스파일러가 .ts 파일을 개별적으로 처리할 수 있게 하여 빌드 파이프라인의 속도를 높일 수 있습니다.
    • noEmit: true: 컴파일러가 JS 파일을 생성하지 않고 오직 타입 검사만 수행하도록 합니다. 최종 JS 파일 생성은 Webpack, Vite, esbuild와 같은 번들러에게 맡길 때 사용합니다. 타입 검사는 TypeScript가, 실제 트랜스파일은 더 빠른 다른 도구가 하는 방식으로 역할을 분담하여 빌드를 가속화합니다.
  2. 더 빠른 트랜스파일러/번들러 사용: TypeScript 컴파일러(tsc)는 타입 검사도 함께 수행하기 때문에 순수한 트랜스파일러(JS 변환기)보다 느릴 수 있습니다. 빌드 속도가 중요하다면, 타입 검사는 TypeScript에 맡기고 실제 .ts -> .js 변환은 더 빠른 도구를 사용하는 파이프라인을 고려할 수 있습니다.

    • esbuild: 매우 빠른 속도로 유명한 빌드 도구입니다. 타입스크립트 코드를 JavaScript로 트랜스파일하는 데 tsc보다 훨씬 빠릅니다.
    • SWC: Rust로 작성된 또 다른 빠른 트랜스파일러입니다. Next.js, Parcel 등 많은 프로젝트에서 사용됩니다.
    • Babel: @babel/preset-typescript를 사용하여 TypeScript 코드를 트랜스파일할 수 있습니다. Babel은 타입 검사를 수행하지 않으므로, 타입 검사를 위해서는 여전히 tsc를 별도로 실행해야 합니다.
    • Vite: 개발 서버에서 esbuild를 사용하여 TS를 빠르게 트랜스파일하고, 프로덕션 빌드에서는 Rollup을 사용합니다. 개발 시 tsc를 통한 타입 검사는 별도의 프로세스로 실행됩니다.

    이러한 도구들은 tsc가 타입 검사만 하도록 noEmit: true를 설정하고, 실제 JS 변환 및 번들링은 이 도구들이 담당하게 합니다.

  3. 타입 가져오기 전략

    • 필요한 타입만 임포트: 사용하지 않는 모듈이나 객체를 임포트하지 않도록 주의합니다.
    • types 필드 활용: tsconfig.jsontypes 필드를 사용하여 전역으로 포함할 @types 패키지를 명시적으로 제한할 수 있습니다. 이는 불필요한 타입 정의 로드를 방지합니다.
  4. 하드웨어 리소스

    • 더 빠른 CPU, 충분한 RAM, NVMe SSD 등 하드웨어 사양을 개선하는 것도 빌드 속도에 직접적인 영향을 미칩니다.

증분 컴파일과 다양한 빌드 최적화 기법들은 대규모 타입스크립트 프로젝트의 개발 경험을 크게 좌우하는 요소입니다. incremental: truetsc --build를 통해 증분 빌드를 활성화하고, 필요에 따라 skipLibCheck, isolatedModules, noEmit과 같은 컴파일러 옵션을 조정하며, 더 빠른 번들러/트랜스파일러를 활용하는 것은 생산성을 극대화하는 데 필수적입니다. 이러한 최적화 전략들을 적절히 조합하여 프로젝트의 빌드 시간을 효과적으로 관리하세요.