빠른 구조
packages/
core/
tsconfig.json
app/
tsconfig.json
tsconfig.json{
"files": [],
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/app" }
]
}tsc --build
tsc --build --clean| 상황 | 먼저 볼 기준 |
|---|---|
| 모노레포 패키지가 서로 참조한다 | project references |
| 전체 타입 검사 시간이 길다 | tsc --build 증분 빌드 |
| 패키지 경계를 강제하고 싶다 | 각 패키지별 tsconfig.json |
| 참조 대상이 빌드되지 않는다 | composite, declaration 확인 |
무엇을 해결하나
project references는 TypeScript 프로젝트를 여러 작은 단위로 나누는 기능입니다. 루트 tsconfig.json에서 하위 프로젝트를 references로 선언하면 tsc --build가 의존 순서를 계산하고 필요한 프로젝트부터 빌드합니다.
{
"references": [
{ "path": "../core" }
]
}큰 저장소에서 하나의 tsconfig만 쓰면 작은 변경에도 전체 타입 검사가 반복되기 쉽습니다. 프로젝트를 경계별로 나누면 패키지 간 계약은 .d.ts 출력으로 연결되고, 이미 최신인 프로젝트는 다시 검사하지 않는 흐름을 만들 수 있습니다.
composite 설정
참조되는 프로젝트는 composite: true가 필요합니다. 이 옵션은 TypeScript가 해당 프로젝트의 입력과 출력을 추적할 수 있도록 몇 가지 제약을 강제합니다. 대표적으로 구현 파일은 include나 files에 포함되어야 하고, declaration 출력이 필요합니다.
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}declarationMap을 켜면 editor에서 참조 프로젝트의 .d.ts가 아니라 원본 소스로 이동하기 쉬워집니다. 모노레포에서 "Go to Definition" 품질을 유지하려면 같이 검토할 만합니다.
build mode
tsc --build 또는 tsc -b는 project references를 위한 빌드 모드입니다. 일반 tsc -p처럼 단일 config만 보는 흐름이 아니라, 참조 그래프를 따라 필요한 프로젝트를 순서대로 빌드합니다.
tsc -b
tsc -b packages/core packages/app
tsc -b --verbose빌드 산출물이나 증분 상태를 초기화해야 할 때는 --clean을 사용합니다.
tsc -b --clean
tsc -bCI에서는 루트에서 tsc -b --pretty false처럼 실행해 전체 타입 경계를 검증하고, 로컬에서는 변경 패키지 중심으로 실행하는 방식을 둘 수 있습니다.
여러 패키지가 서로 의존하는 모노레포, 앱과 공유 라이브러리를 함께 관리하는 저장소, 서버와 클라이언트가 공통 타입 패키지를 공유하는 구조에서 효과가 큽니다.
packages/core -> packages/api -> apps/web단일 앱 규모가 작고 패키지 경계가 뚜렷하지 않다면 project references가 오히려 설정 부담을 늘릴 수 있습니다. 먼저 include, exclude, paths, 빌드 캐시만으로 충분한지 확인한 뒤 도입하는 편이 좋습니다.
주의할 점
project references는 path alias의 대체재가 아닙니다. alias는 import 경로를 읽기 쉽게 만드는 설정이고, references는 TypeScript 프로젝트 간 빌드 순서와 타입 출력 경계를 만드는 설정입니다.
참조 프로젝트가 내부 소스를 직접 import하게 두면 경계가 흐려질 수 있습니다. 패키지 entry point, declaration 출력, package manager workspace 설정까지 함께 맞춰야 실제 런타임 경계와 타입 경계가 같은 방향으로 움직입니다.
// 피하는 편이 좋은 내부 경로 참조
import { parse } from "@repo/core/src/internal/parse";
// 공개 entry point를 통한 참조
import { parse } from "@repo/core";참고 링크
2 sources