프로젝트 레퍼런스(Project References)는 TypeScript 프로젝트를 여러 하위 프로젝트로 분리 해서 독립적으로 컴파일하고, 증분 빌드를 활용하는 기능입니다.

왜 프로젝트 레퍼런스가 필요한가

모노레포에서 모든 코드를 하나의 tsconfig.json으로 컴파일하면 문제가 생깁니다.

  • 파일 하나를 바꿔도 전체를 다시 컴파일
  • 프로젝트가 커지면 ** 타입 체크 시간이 급증**
  • 패키지 간 ** 의존성이 불명확**

프로젝트 레퍼런스는 이 문제를 해결합니다.

기본 설정

PLAINTEXT
my-monorepo/
├── packages/
│   ├── shared/         # 공용 유틸리티
│   │   ├── src/
│   │   └── tsconfig.json
│   ├── api/            # API 서버
│   │   ├── src/
│   │   └── tsconfig.json
│   └── web/            # 프론트엔드
│       ├── src/
│       └── tsconfig.json
└── tsconfig.json       # 루트 설정

하위 프로젝트 설정

JSON
// packages/shared/tsconfig.json
{
  "compilerOptions": {
    "composite": true,        // 필수: 프로젝트 레퍼런스에 참여
    "declaration": true,      // 필수: .d.ts 생성
    "declarationMap": true,   // 권장: Go to Definition 지원
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}
JSON
// packages/api/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "references": [
    { "path": "../shared" }   // shared 프로젝트를 참조
  ],
  "include": ["src/**/*"]
}

루트 설정

JSON
// tsconfig.json (루트)
{
  "files": [],                // 루트는 직접 컴파일할 파일 없음
  "references": [
    { "path": "packages/shared" },
    { "path": "packages/api" },
    { "path": "packages/web" }
  ]
}

tsc --build 모드

프로젝트 레퍼런스를 사용하면 --build 플래그로 빌드합니다.

BASH
# 전체 빌드 — 의존성 순서대로 컴파일
npx tsc --build

# 약자
npx tsc -b

# 변경된 것만 증분 빌드
npx tsc -b

# 깨끗하게 다시 빌드
npx tsc -b --clean
npx tsc -b --force

# 워치 모드
npx tsc -b --watch

증분 빌드의 효과

PLAINTEXT
첫 빌드: shared → api → web (전체 컴파일)
shared 수정: shared → api → web (shared에 의존하는 것만 재컴파일)
api만 수정: api (api만 재컴파일)

composite: true로 설정하면 .tsbuildinfo 파일이 생성되어 이전 빌드 정보를 캐싱합니다.

prepend 옵션

JSON
// 참조 프로젝트의 출력을 현재 출력 앞에 붙이기
{
  "references": [
    { "path": "../shared", "prepend": true }
  ]
}

prependoutFile을 사용할 때 참조 프로젝트의 출력을 합치는 옵션입니다. 번들러를 사용하는 경우에는 잘 쓰지 않습니다.

실전 패턴: 테스트 분리

JSON
// tsconfig.json — 소스 코드
{
  "compilerOptions": {
    "composite": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

// tsconfig.test.json — 테스트 코드
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "composite": true,
    "rootDir": ".",
    "outDir": "./dist-test",
    "types": ["node", "jest"]
  },
  "references": [
    { "path": "./" }  // 소스 프로젝트를 참조
  ],
  "include": ["src/**/*", "tests/**/*"]
}

Solution Style tsconfig

IDE에서 모든 파일을 인식하도록 하는 패턴입니다.

JSON
// tsconfig.json (루트 — solution style)
{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.test.json" }
  ]
}

주의점

  • composite: truedeclaration: true를 필수로 요구한다
  • 참조 프로젝트에서 직접 .ts 파일을 import하면 안 되고, 빌드된 .d.ts를 통해 참조한다
  • .tsbuildinfo 파일은 .gitignore에 추가하는 것이 좋다

정리

  • 프로젝트 레퍼런스는 큰 프로젝트를 하위 프로젝트로 분리해서 증분 빌드를 가능하게 한다
  • composite: truedeclaration: true가 필수 옵션이다
  • tsc --build로 의존성 순서대로 빌드하고, 변경된 프로젝트만 재컴파일한다
  • 모노레포에서 빌드 시간을 크게 줄일 수 있다
  • 테스트 코드를 분리하는 패턴에도 활용할 수 있다
댓글 로딩 중...