--- id: ts-tsconfig-strategy title: tsconfig 전략 — strict / paths / project references category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [typescript, tsconfig, build, vibe-coding] tech_stack: { language: "TypeScript", applicable_to: ["Backend", "Frontend"] } applied_in: [] aliases: [tsconfig.json, strict, isolatedModules, project references, paths] --- # tsconfig 전략 > 디폴트로 켜야 할 것: **strict / noUncheckedIndexedAccess / exactOptionalPropertyTypes**. 모노레포면 project references. 빌드 도구가 type-check 안 해줌 → `tsc --noEmit` CI 필수. ## 📖 핵심 개념 - `strict`: 7개 옵션 묶음. - Compiler vs type-checker: bundler (esbuild/swc) 가 컴파일, tsc 는 검사 전담. - Project references: 큰 monorepo 의 점진 빌드. - Module 모드: `node16`/`nodenext` 가 ESM/CJS 정확. ## 💻 코드 패턴 ### 시작 base ```jsonc // tsconfig.base.json { "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "lib": ["ES2023", "DOM"], "strict": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "exactOptionalPropertyTypes": true, "noFallthroughCasesInSwitch": true, "noPropertyAccessFromIndexSignature": true, "isolatedModules": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "verbatimModuleSyntax": true, "resolveJsonModule": true } } ``` ### App 별 override ```jsonc // apps/api/tsconfig.json { "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", "rootDir": "src", "types": ["node"] }, "include": ["src/**/*"], "exclude": ["dist", "**/*.test.ts"] } ``` ### Path alias ```jsonc { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"], "@shared/*": ["../shared/src/*"] } } } ``` 런타임 호환: tsx / vite / Next.js 자동. Node 직접 실행 시 `tsx` 또는 `tsconfig-paths/register`. ### Project references (monorepo) ```jsonc // root tsconfig.json { "files": [], "references": [ { "path": "packages/shared" }, { "path": "apps/api" }, { "path": "apps/web" } ] } // packages/shared/tsconfig.json { "compilerOptions": { "composite": true, "declaration": true, "outDir": "dist" }, "include": ["src/**/*"] } // apps/api/tsconfig.json (참조) { "references": [{ "path": "../../packages/shared" }] } ``` ```bash tsc --build # 모두 점진 tsc --build --watch # watch tsc --build --clean # 정리 ``` ### Type-only import (verbatimModuleSyntax) ```ts import { type User, getUser } from './user'; // 컴파일 결과: type User 는 erased, 안전 ``` ### CI type-check (bundle 따로) ```yaml - run: tsc --noEmit # type 만 검사 - run: vite build # bundle (esbuild/swc) ``` bundler 가 type 무시 — 별도 step. ### Browser vs Node 분리 ```jsonc // web tsconfig "lib": ["ES2023", "DOM", "DOM.Iterable"] "types": [] // node types 빼기 // node tsconfig "lib": ["ES2023"] "types": ["node"] ``` ### Decorator (Nest 등) ```jsonc "experimentalDecorators": true, "emitDecoratorMetadata": true ``` 또는 TS 5.0+ stage-3 decorators. ## 🤔 의사결정 기준 | 상황 | 옵션 | |---|---| | 새 프로젝트 | strict + noUncheckedIndexedAccess | | 점진 마이그레이션 | strict 부분 켜기 (`strictNullChecks` 부터) | | ESM Node 18+ | `module: nodenext` | | Vite/Next | `moduleResolution: bundler` | | Monorepo 큰 | project references | | Library 배포 | `composite: true`, `declaration: true` | ## ❌ 안티패턴 - **strict false 그대로**: 의미 절반 잃음. - **`@ts-ignore` 남발**: 차라리 `as` + 주석. - **`any` 도배**: `unknown` + 좁히기. - **path alias 만 — runtime resolver X**: import 깨짐. - **`tsc` 빌드 + bundler 빌드 둘 다**: 한쪽 only. tsc=check, bundler=build. - **`skipLibCheck: false` 항상**: node_modules 타입 깨지면 빌드 fail. 기본 true. - **`isolatedModules: false`**: esbuild/swc 가 일부 패턴 못 컴파일. ## 🤖 LLM 활용 힌트 - strict + noUncheckedIndexedAccess + exactOptionalPropertyTypes 항상. - bundler 컴파일 + tsc 검사 분리. - monorepo = project references. ## 🔗 관련 문서 - [[TS_Build_Bundler_Patterns]] - [[TS_Monorepo_Patterns]] - [[JS_Module_System_ESM_CJS]]