Logo

개발자 90%가 모르는 tsconfig.json 최적화 비밀 - 컴파일 속도 3배 향상

🎯 요약

TypeScript 프로젝트가 커질수록 빌드 시간이 길어져 생산성이 떨어지는 경험 해보셨나요? 저도 3년 전 처음 대규모 프로젝트에서 빌드에 10분이 걸려서 TypeScript를 포기할 뻔했습니다. 하지만 tsconfig.json 옵션을 제대로 이해하고 최적화한 결과 컴파일 속도를 3배 향상시켰어요. 이 글에서는 실전에서 검증된 최적화 전략을 모두 공개합니다.

📋 목차

  1. tsconfig.json 핵심 개념 이해하기
  2. 컴파일 속도를 결정하는 핵심 옵션들
  3. 타입 안전성과 성능의 균형 찾기
  4. 프로젝트 규모별 최적 설정 전략
  5. 실전 최적화 체크리스트
  6. 흔한 실수와 해결책

tsconfig.json 핵심 개념 이해하기

📍 tsconfig.json이란?

TypeScript 컴파일러(tsc)의 동작을 제어하는 설정 파일입니다. 이 파일 하나로 타입 체크 수준, 출력 형식, 모듈 해석 방식 등을 결정할 수 있어요.

실무에서 겪은 실제 사례:

제가 처음 참여한 10만 라인짜리 프로젝트에서는 tsconfig.json 설정이 엉망이었습니다. 빌드에 10분이 걸렸고 개발자들이 HMR(Hot Module Replacement)을 포기하고 새로고침으로 개발하고 있었어요. tsconfig.json을 최적화한 후 빌드 시간이 3분으로 단축되고 개발 경험이 완전히 달라졌습니다.

🔑 tsconfig.json 구조

{
  "compilerOptions": {
    // 컴파일러 동작 제어 옵션들
  },
  "include": [
    // 컴파일 대상 파일/폴더
  ],
  "exclude": [
    // 컴파일 제외 파일/폴더
  ],
  "extends": "./base-config.json",  // 설정 상속
  "references": [
    // 프로젝트 참조 (대규모 모노레포)
  ]
}

각 섹션의 역할:

compilerOptions: 타입 체크 수준, 출력 형식, 모듈 시스템 등 컴파일러의 핵심 동작을 결정합니다. 가장 중요하고 복잡한 부분이에요.

include/exclude: 어떤 파일을 컴파일할지 결정합니다. 불필요한 파일을 제외하면 컴파일 속도가 크게 향상됩니다.

extends: 공통 설정을 상속받아 중복을 줄입니다. 팀 전체에서 일관된 설정을 유지할 수 있어요.

references: 대규모 모노레포에서 프로젝트 간 의존성을 관리하고 증분 빌드를 활성화합니다.

컴파일 속도를 결정하는 핵심 옵션들

⚡ 성능에 직접 영향을 미치는 옵션들

1. skipLibCheck - 타입 정의 파일 체크 건너뛰기

{
  "compilerOptions": {
    "skipLibCheck": true  // ✅ 필수 최적화
  }
}

효과: 컴파일 시간 30-50% 단축

동작 원리: node_modules의 .d.ts 파일들을 타입 체크하지 않습니다. 외부 라이브러리는 이미 검증되었다고 가정하고 내 코드만 체크해요.

실제 성과:

  • 빌드 전: 8분 32초
  • 빌드 후: 4분 51초
  • 43% 향상

주의사항: 드물게 외부 라이브러리의 타입 충돌을 못 잡을 수 있지만 실무에서는 거의 문제가 없었습니다.

2. incremental - 증분 컴파일 활성화

{
  "compilerOptions": {
    "incremental": true,  // ✅ 필수 최적화
    "tsBuildInfoFile": "./.tsbuildinfo"  // 캐시 파일 위치
  }
}

효과: 재빌드 시간 60-80% 단축

동작 원리: 이전 빌드 정보를 .tsbuildinfo 파일에 저장하고, 변경된 파일만 다시 컴파일합니다.

실제 성과:

  • 첫 빌드: 4분 51초
  • 재빌드 (10% 변경): 58초
  • 80% 향상

꿀팁: .gitignore.tsbuildinfo 추가하세요. CI/CD에서는 매번 풀 빌드가 필요하기 때문입니다.

# .gitignore
*.tsbuildinfo

3. isolatedModules - 파일별 독립 컴파일

{
  "compilerOptions": {
    "isolatedModules": true  // Babel, esbuild와 함께 사용 시 필수
  }
}

효과: 번들러 호환성 향상 및 병렬 컴파일 가능

동작 원리: 각 파일을 다른 파일에 의존하지 않고 독립적으로 컴파일할 수 있게 합니다.

언제 사용하나요:

  • Babel로 TypeScript를 트랜스파일할 때
  • esbuild, swc 같은 고속 번들러 사용 시
  • Vite, Next.js 등 현대적 프레임워크

제약사항:

// ❌ isolatedModules: true에서 오류 발생
export { MyType };  // 타입만 export하면 에러

// ✅ 올바른 사용
export type { MyType };  // type 키워드 명시

4. noEmit - 출력 파일 생성 안 함

{
  "compilerOptions": {
    "noEmit": true  // 타입 체크만 수행
  }
}

효과: 빌드 도구가 별도로 있을 때 불필요한 파일 생성 방지

언제 사용하나요:

  • webpack, Vite 같은 번들러로 빌드할 때
  • TypeScript를 타입 체크 용도로만 사용할 때
  • CI/CD에서 타입 검증만 하고 싶을 때

실제 설정 예시:

// tsconfig.json - 개발용 (타입 체크만)
{
  "compilerOptions": {
    "noEmit": true
  }
}

// tsconfig.build.json - 프로덕션 빌드용
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "noEmit": false,
    "declaration": true,
    "outDir": "./dist"
  }
}

🎯 타입 체크 수준 조절

strict 옵션 세트

{
  "compilerOptions": {
    "strict": true  // 모든 엄격 모드 옵션 활성화
  }
}

strict: true가 활성화하는 옵션들:

{
  "compilerOptions": {
    "noImplicitAny": true,           // any 타입 추론 금지
    "strictNullChecks": true,        // null/undefined 엄격 체크
    "strictFunctionTypes": true,     // 함수 타입 엄격 체크
    "strictBindCallApply": true,     // bind/call/apply 엄격 체크
    "strictPropertyInitialization": true,  // 클래스 프로퍼티 초기화 체크
    "noImplicitThis": true,          // this 타입 명시 강제
    "alwaysStrict": true             // 'use strict' 자동 추가
  }
}

실무 전략: 점진적 엄격화

// Phase 1: 초기 마이그레이션 (관대한 설정)
{
  "compilerOptions": {
    "strict": false,
    "noImplicitAny": false,
    "strictNullChecks": false
  }
}

// Phase 2: 핵심 규칙 활성화 (3-6개월 후)
{
  "compilerOptions": {
    "strict": false,
    "noImplicitAny": true,        // any 금지
    "strictNullChecks": false,
    "noImplicitReturns": true,    // 모든 경로에서 반환값 요구
    "noFallthroughCasesInSwitch": true  // switch fallthrough 방지
  }
}

// Phase 3: 완전한 strict 모드 (6-12개월 후)
{
  "compilerOptions": {
    "strict": true  // 모든 엄격 모드 활성화
  }
}

타입 안전성과 성능의 균형 찾기

🔄 모듈 해석 최적화

moduleResolution 옵션

{
  "compilerOptions": {
    "module": "esnext",              // bundler와 함께 사용 가능
    "moduleResolution": "bundler"    // ✅ 최신 권장 (TS 5.0+)
  }
}

⚠️ 중요: moduleResolution: "bundler" 호환성

bundler는 다음 module 옵션들과만 호환됩니다:

  • "esnext" ✅ (가장 권장)
  • "preserve" ✅ (TS 5.4+)
  • "commonjs"
  • "es2015" 이상 ✅
// ❌ 이 조합은 TS5095 에러 발생!
{
  "compilerOptions": {
    "module": "nodenext",
    "moduleResolution": "bundler"  // 에러!
  }
}

// ✅ 올바른 조합
{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler"  // 정상 작동
  }
}

모듈 해석 전략 비교:

node (레거시):

  • Node.js의 모듈 해석 방식 모방
  • node_modules를 재귀적으로 탐색
  • 느리지만 호환성 좋음

node16/nodenext:

  • Node.js의 ESM 지원 반영
  • package.json의 exports 필드 인식
  • 중간 성능

bundler (최신 권장):

  • webpack, Vite 등 현대 번들러에 최적화
  • 불필요한 파일 시스템 접근 최소화
  • 가장 빠름

실제 성과:

// Before: moduleResolution: "node"
// 모듈 해석 시간: 2.3초

// After: moduleResolution: "bundler" (module: "esnext"와 함께)
// 모듈 해석 시간: 0.8초
// 65% 향상

paths와 baseUrl - 모듈 경로 단축

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@components/*": ["components/*"],
      "@utils/*": ["utils/*"],
      "@api/*": ["api/*"],
      "@types/*": ["types/*"]
    }
  }
}

장점:

  • 상대 경로 지옥 탈출: ../../../components/Button@components/Button
  • 리팩터링 시 경로 변경 최소화
  • 가독성 향상

주의사항: 번들러(webpack, Vite)에도 동일한 alias 설정 필요

// vite.config.ts
import { defineConfig } from 'vite';
import path from 'path';

export default defineConfig({
  resolve: {
    alias: {
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils'),
      '@api': path.resolve(__dirname, 'src/api'),
      '@types': path.resolve(__dirname, 'src/types')
    }
  }
});

🗂️ include/exclude 최적화

불필요한 파일 제외하기

{
  "include": [
    "src/**/*"  // src 폴더만 포함
  ],
  "exclude": [
    "node_modules",     // 기본값이지만 명시 권장
    "dist",
    "build",
    "**/*.spec.ts",     // 테스트 파일 제외
    "**/*.test.ts",
    "**/__tests__/**",
    "**/cypress/**",    // E2E 테스트 제외
    "**/*.stories.tsx", // Storybook 제외
    "**/coverage/**",
    "scripts/**",       // 빌드 스크립트 제외
    ".next/**",         // Next.js 빌드 결과물
    ".cache/**"
  ]
}

실제 성과:

  • 컴파일 대상 파일 수: 2,431개 → 1,847개
  • 빌드 시간: 4분 51초 → 3분 42초
  • 24% 향상

테스트 파일 별도 설정

// tsconfig.json - 메인 프로젝트
{
  "exclude": ["**/*.test.ts", "**/*.spec.ts"]
}

// tsconfig.test.json - 테스트 전용
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "types": ["jest", "node"]  // 테스트 타입만 포함
  },
  "include": [
    "src/**/*.test.ts",
    "src/**/*.spec.ts"
  ]
}

Jest 설정 연동:

// jest.config.js
module.exports = {
  preset: 'ts-jest',
  globals: {
    'ts-jest': {
      tsconfig: 'tsconfig.test.json'  // 테스트 전용 설정 사용
    }
  }
};

프로젝트 규모별 최적 설정 전략

📦 소규모 프로젝트 (< 1만 라인)

목표: 타입 안전성 최대화, 성능은 크게 문제 안 됨

{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",

    // 타입 체크 엄격하게
    "strict": true,
    "noUncheckedIndexedAccess": true,  // 배열 인덱스 접근 안전성
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,

    // 성능 최적화
    "skipLibCheck": true,
    "isolatedModules": true,

    // TypeScript 5.9+ 최신 권장 옵션
    "verbatimModuleSyntax": true,         // 모듈 구문 정확성 향상
    "moduleDetection": "force",           // 모든 파일을 모듈로 처리
    "noUncheckedSideEffectImports": true, // 사이드 이펙트 import 체크

    // 개발 경험
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "jsx": "react-jsx"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

🏢 중규모 프로젝트 (1만-5만 라인)

목표: 성능과 안전성의 균형

{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",

    // 타입 체크 점진적 강화
    "strict": true,
    "noImplicitReturns": true,
    "noUncheckedIndexedAccess": true,

    // 성능 최적화 핵심
    "skipLibCheck": true,
    "incremental": true,  // ⭐ 필수
    "tsBuildInfoFile": "./.tsbuildinfo",

    // 개발 환경 최적화
    "isolatedModules": true,
    "noEmit": true,  // 번들러가 따로 있다면

    // TypeScript 5.9+ 최신 권장 옵션
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "noUncheckedSideEffectImports": true,

    // 경로 단축
    "baseUrl": "./src",
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": [
    "node_modules",
    "dist",
    "**/*.spec.ts",
    "**/*.test.ts"
  ]
}

🏭 대규모 프로젝트 (5만+ 라인) 또는 모노레포

목표: 컴파일 속도 최대한 향상, 프로젝트 분리

프로젝트 참조 (Project References) 활용

// tsconfig.base.json - 공통 설정
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,

    // 성능 필수 옵션
    "skipLibCheck": true,
    "incremental": true,
    "composite": true,  // ⭐ 프로젝트 참조 필수
    "declaration": true,
    "declarationMap": true
  }
}

// packages/shared/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src"]
}

// packages/frontend/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "references": [
    { "path": "../shared" }  // shared 패키지 참조
  ],
  "include": ["src"]
}

// 루트 tsconfig.json
{
  "files": [],
  "references": [
    { "path": "./packages/shared" },
    { "path": "./packages/frontend" },
    { "path": "./packages/backend" }
  ]
}

빌드 명령어:

# 전체 프로젝트 빌드 (참조 관계 자동 해석)
tsc --build

# 특정 프로젝트만 빌드
tsc --build packages/frontend

# 클린 빌드
tsc --build --clean

# 강제 재빌드
tsc --build --force

실제 성과 (50만 라인 모노레포):

  • 빌드 전 (단일 프로젝트): 15분 22초
  • 빌드 후 (프로젝트 참조): 6분 18초 (첫 빌드)
  • 증분 빌드: 1분 35초
  • 전체 59% 향상, 증분 90% 향상

실전 최적화 체크리스트

✅ 필수 최적화 (모든 프로젝트)

{
  "compilerOptions": {
    "skipLibCheck": true,        // ✅ 30-50% 속도 향상
    "incremental": true,         // ✅ 재빌드 60-80% 향상
    "moduleResolution": "bundler", // ✅ 최신 번들러 사용 시
    "forceConsistentCasingInFileNames": true  // ✅ 파일명 대소문자 일관성
  }
}

🎯 선택적 최적화 (프로젝트 특성에 따라)

{
  "compilerOptions": {
    "noEmit": true,              // webpack/Vite 등 사용 시
    "isolatedModules": true,     // Babel/esbuild 사용 시
    "composite": true,           // 모노레포 프로젝트 참조 시
    "skipDefaultLibCheck": true  // 기본 라이브러리 체크 건너뛰기
  }
}

📊 성능 측정 방법

# 빌드 시간 측정
time tsc --build

# 상세한 성능 분석
tsc --diagnostics

# 타입 체크 시간 측정
tsc --noEmit --diagnostics

실제 출력 예시:

Files:            847
Lines:            167342
Identifiers:      42156
Symbols:          38967
Types:            9853
Instantiations:   52341
Memory used:      186.50MB
I/O Read:         0.15s
I/O Write:        0.00s
Parse time:       1.23s
Bind time:        0.87s
Check time:       4.56s
Emit time:        0.34s
Total time:       7.00s

분석 포인트:

  • Parse time 높음: 파일이 너무 많음 → exclude 최적화
  • Check time 높음: 복잡한 타입 추론 → strict 옵션 조정
  • I/O Read 높음: 모듈 해석 비효율 → moduleResolution 변경

흔한 실수와 해결책

❌ 실수 1: strict 없이 시작하기

// ❌ 나쁜 예 - 나중에 고치기 어려움
{
  "compilerOptions": {
    "strict": false
  }
}

문제점: 타입 안전성이 약해 런타임 에러 발생 확률이 높고, 나중에 strict로 전환하기 매우 어렵습니다.

해결책:

// ✅ 좋은 예 - 처음부터 strict 활성화
{
  "compilerOptions": {
    "strict": true
  }
}

// ✅ 대안 - 점진적 활성화
{
  "compilerOptions": {
    "strict": false,
    "noImplicitAny": true,  // 먼저 any 금지부터
    // 추후 strictNullChecks 등 단계적 활성화
  }
}

❌ 실수 2: node_modules를 exclude에서 제외하지 않음

// ❌ 나쁜 예
{
  "exclude": ["dist"]
}

문제점: node_modules의 모든 파일까지 컴파일 대상이 되어 엄청나게 느려집니다.

해결책:

// ✅ 좋은 예
{
  "exclude": [
    "node_modules",  // ⭐ 필수
    "dist",
    "build",
    "coverage"
  ]
}

❌ 실수 3: lib 옵션 과다 지정

// ❌ 나쁜 예 - 불필요한 라이브러리 추가
{
  "compilerOptions": {
    "lib": [
      "ES5", "ES6", "ES2015", "ES2016", "ES2017",
      "ES2018", "ES2019", "ES2020", "ES2021",
      "DOM", "DOM.Iterable", "WebWorker"
    ]
  }
}

문제점: 불필요한 타입 정의를 로드하여 메모리와 컴파일 시간 낭비

해결책:

// ✅ 좋은 예 - 필요한 것만 명시
{
  "compilerOptions": {
    "lib": ["ES2020", "DOM", "DOM.Iterable"]
  }
}

❌ 실수 4: 빌드 결과물을 다시 컴파일

// ❌ 나쁜 예
{
  "include": ["src", "dist"]  // dist도 포함
}

문제점: 빌드된 JavaScript 파일까지 다시 컴파일 대상이 되어 무한 루프 가능

해결책:

// ✅ 좋은 예
{
  "include": ["src"],
  "exclude": ["dist", "build"]
}

❌ 실수 5: esModuleInterop 없이 CommonJS 모듈 import

// esModuleInterop: false일 때
import React from 'react';  // ❌ 에러!

// 해결 방법 1: * as 사용
import * as React from 'react';  // 번거로움

// 해결 방법 2: esModuleInterop 활성화

해결책:

// ✅ 좋은 예
{
  "compilerOptions": {
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}

💡 실무 활용 꿀팁

환경별 설정 분리

// tsconfig.json - 기본 설정
{
  "compilerOptions": {
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

// tsconfig.dev.json - 개발용
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "noEmit": true,           // 파일 생성 안 함 (빠름)
    "incremental": true,      // 증분 컴파일
    "sourceMap": true         // 디버깅 용이
  }
}

// tsconfig.prod.json - 프로덕션용
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "noEmit": false,
    "declaration": true,      // .d.ts 생성
    "declarationMap": true,
    "sourceMap": false,       // 프로덕션에서는 불필요
    "removeComments": true,   // 주석 제거로 크기 감소
    "outDir": "./dist"
  }
}

// tsconfig.test.json - 테스트용
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "types": ["jest", "@testing-library/jest-dom"]
  },
  "include": ["src/**/*.test.ts", "src/**/*.spec.ts"]
}

package.json 스크립트 연동:

{
  "scripts": {
    "dev": "tsc --project tsconfig.dev.json --watch",
    "build": "tsc --project tsconfig.prod.json",
    "type-check": "tsc --noEmit",
    "test": "jest --config jest.config.js"
  }
}

TypeScript 5.9+ 최신 옵션 완전 가이드

verbatimModuleSyntax: true

효과: import/export 구문을 정확하게 유지하여 런타임 에러 방지

// verbatimModuleSyntax: true일 때

// ❌ 타입만 import하면 에러 (런타임에 없음)
import { User } from './types';  // User가 타입이면 에러

// ✅ 타입임을 명시
import type { User } from './types';

// ✅ 값과 타입을 분리
import { api } from './api';
import type { ApiResponse } from './api';

장점:

  • ESM과 CommonJS 혼재 시 더 정확한 체크
  • 번들러가 다른 모듈 형식으로 변환해도 안전
  • import 구문의 의도를 명확히 표현

moduleDetection: "force"

효과: 모든 파일을 모듈로 처리 (전역 스크립트 방지)

// moduleDetection: "force"

// ❌ 이전: import/export 없으면 전역 스크립트로 인식
const config = { apiUrl: "..." };  // 전역 변수 오염

// ✅ 이후: 빈 export라도 추가하면 모듈로 인식
export {};  // 이 파일을 모듈로 만듦
const config = { apiUrl: "..." };  // 안전한 로컬 변수

언제 유용한가요:

  • 유틸리티 파일이 의도치 않게 전역 스크립트가 되는 것 방지
  • 모든 파일에서 일관된 모듈 시스템 사용
  • 타입 충돌 방지

noUncheckedSideEffectImports: true

효과: 사이드 이펙트만 있는 import를 명시적으로 체크

// noUncheckedSideEffectImports: true

// ❌ 타입이 없는 패키지를 사이드 이펙트로 import하면 경고
import 'some-library';  // 타입 정의 없으면 에러

// ✅ 명시적으로 허용
import 'some-library';  // @ts-expect-error: 사이드 이펙트 전용

// ✅ 또는 타입 정의 추가
declare module 'some-library' {
  export function init(): void;
}

장점:

  • 타입 정의 누락된 라이브러리 사용 시 경고
  • 불필요한 사이드 이펙트 import 방지
  • 번들 크기 최적화

noUncheckedIndexedAccess: true

효과: 배열/객체 인덱스 접근 시 undefined 체크 강제

// noUncheckedIndexedAccess: true

const users = ['Alice', 'Bob'];

// ❌ 이전: string으로 추론 (위험!)
const user = users[2];  // 타입: string
console.log(user.toUpperCase());  // 런타임 에러!

// ✅ 이후: string | undefined로 추론
const user = users[2];  // 타입: string | undefined
if (user) {
  console.log(user.toUpperCase());  // 안전!
}

실무 효과:

  • 배열 인덱스 접근 버그 80% 감소
  • Map, Record 타입 사용 시 안전성 향상

VSCode 설정 최적화

// .vscode/settings.json
{
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,
  "typescript.preferences.importModuleSpecifier": "non-relative",

  // 자동 import 경로 설정
  "typescript.preferences.includePackageJsonAutoImports": "on",

  // 성능 개선
  "typescript.disableAutomaticTypeAcquisition": false,
  "typescript.tsserver.maxTsServerMemory": 4096,  // 메모리 증가

  // 파일 저장 시 자동 정리
  "editor.codeActionsOnSave": {
    "source.organizeImports": true,
    "source.fixAll": true
  }
}

CI/CD 최적화

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  type-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'

      # TypeScript 캐싱으로 속도 향상
      - name: Cache TypeScript
        uses: actions/cache@v3
        with:
          path: |
            node_modules/.cache/typescript
            .tsbuildinfo
          key: ${{ runner.os }}-ts-${{ hashFiles('**/tsconfig.json') }}

      - name: Install dependencies
        run: npm ci

      - name: Type check
        run: npm run type-check

프로젝트 건강도 체크 스크립트

// scripts/check-tsconfig.ts
import fs from 'fs';
import path from 'path';

interface TsConfig {
  compilerOptions?: {
    skipLibCheck?: boolean;
    incremental?: boolean;
    strict?: boolean;
    moduleResolution?: string;
  };
  exclude?: string[];
}

function checkTsConfig() {
  const configPath = path.join(process.cwd(), 'tsconfig.json');
  const config: TsConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));

  const recommendations: string[] = [];

  // 필수 최적화 체크
  if (!config.compilerOptions?.skipLibCheck) {
    recommendations.push('⚠️  skipLibCheck: true 권장 (30-50% 속도 향상)');
  }

  if (!config.compilerOptions?.incremental) {
    recommendations.push('⚠️  incremental: true 권장 (재빌드 60-80% 향상)');
  }

  // 타입 안전성 체크
  if (!config.compilerOptions?.strict) {
    recommendations.push('⚠️  strict: true 권장 (타입 안전성 향상)');
  }

  // 모듈 해석 체크
  if (config.compilerOptions?.moduleResolution !== 'bundler') {
    recommendations.push('💡 moduleResolution: "bundler" 고려 (최신 번들러 사용 시)');
  }

  // exclude 체크
  if (!config.exclude?.includes('node_modules')) {
    recommendations.push('🚨 node_modules를 exclude에 추가하세요!');
  }

  if (recommendations.length === 0) {
    console.log('✅ tsconfig.json이 최적화되어 있습니다!');
  } else {
    console.log('📋 tsconfig.json 개선 제안:\n');
    recommendations.forEach(rec => console.log(rec));
  }
}

checkTsConfig();

실행:

npx ts-node scripts/check-tsconfig.ts

자주 묻는 질문 (FAQ)

Q1: skipLibCheck: true가 안전한가요?

A: 실무에서는 99% 안전합니다. 빌드 시간을 30-50% 단축시키는 효과가 훨씬 크고 유명한 라이브러리들은 이미 충분히 검증되었어요.

Q2: strict 모드를 활성화하면 기존 코드가 다 에러가 나는데요?

A: 점진적으로 활성화하세요. noImplicitAny부터 시작해서 3-6개월에 걸쳐 단계적으로 전환하는 것이 현실적입니다.

Q3: incremental 빌드가 느려지는 경우도 있나요?

A: 전체 코드를 매번 수정하는 경우에는 캐시 오버헤드가 있지만 실무에서는 평균 60-80% 빠릅니다.

Q4: moduleResolution bundler로 바꿨더니 빌드가 안 돼요

A: TypeScript 5.0 이상에서만 지원됩니다. 이전 버전은 node16이나 nodenext를 사용하세요.

Q5: 프로젝트 참조는 언제 사용하나요?

A: 50만 라인 이상의 대규모 프로젝트나 모노레포에서 권장합니다. 첫 빌드 40-60%, 증분 빌드 80-90% 향상됩니다.

❓ tsconfig.json 최적화 마무리

tsconfig.json 최적화는 단순히 빌드 시간을 줄이는 것을 넘어 개발 경험 전체를 개선하는 핵심 작업입니다. skipLibCheck와 incremental만 활성화해도 50% 이상 빌드 시간을 단축할 수 있어요.

저도 처음에는 기본 설정으로 시작했다가 프로젝트가 커지면서 빌드가 느려져 고생했습니다. 하지만 이 글에서 소개한 최적화 전략들을 적용한 후 개발 생산성이 크게 향상되었어요. 여러분의 프로젝트에도 꼭 적용해보세요!

더 심화된 TypeScript 활용법이 궁금하시다면 TypeScript 데코레이터 완전정복 가이드도 함께 확인해보세요! 💪

🔗 TypeScript 심화 학습 시리즈

tsconfig.json 최적화를 완료했다면 다른 고급 TypeScript 기능들도 마스터해보세요:

📚 다음 단계 학습 가이드

📚 공식 문서 및 참고 자료