본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 11장: 프로젝트 설정과 모노레포 타입 전략
2026년 2월 11일·프로그래밍·

11장: 프로젝트 설정과 모노레포 타입 전략

TypeScript 프로젝트의 tsconfig.json 최적화, 프로젝트 참조, Isolated Declarations, 모노레포에서의 타입 전략을 실전 중심으로 다룹니다.

14분565자7개 섹션
typescriptperformancedevtools
공유
typescript-deepdive11 / 12
123456789101112
이전10장: 타입 수준 프로그래밍 — 타입으로 로직 작성하기다음12장: 실전 프로젝트 — 타입 안전 유틸리티 라이브러리 설계

10장까지 TypeScript 타입 시스템의 심화 기능을 다뤘습니다. 이 장에서는 시선을 바꿔 프로젝트 설정과 빌드 인프라에 집중합니다. 아무리 정교한 타입을 설계해도, tsconfig.json이 올바르게 구성되지 않으면 그 효과를 발휘할 수 없습니다. 특히 모노레포(monorepo) 환경에서의 타입 관리는 프로젝트 규모가 커질수록 핵심적인 과제가 됩니다.

tsconfig.json 최적화

5.x에서 중요한 컴파일러 옵션

TypeScript 5.x에서 추가되거나 변경된 주요 옵션을 정리합니다.

tsconfig.json — 권장 설정 (5.8 기준)
json
{
  "compilerOptions": {
    // === 타입 검사 ===
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noFallthroughCasesInSwitch": true,
 
    // === 모듈 시스템 ===
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "verbatimModuleSyntax": true,
    "rewriteRelativeImportExtensions": true,
 
    // === 출력 ===
    "target": "es2024",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
 
    // === 5.x 신규 옵션 ===
    "erasableSyntaxOnly": true,
    "isolatedDeclarations": true
  }
}

strict 옵션 분해

strict: true는 여러 하위 옵션을 한 번에 활성화합니다. 각 옵션이 어떤 검사를 수행하는지 이해하면 문제 해결에 도움됩니다.

strict가 활성화하는 옵션들
json
{
  "compilerOptions": {
    "strict": true,
    // 위 옵션은 아래를 모두 활성화:
    // "strictNullChecks": true,        — null/undefined 엄격 검사
    // "strictFunctionTypes": true,     — 함수 파라미터 반변성 검사
    // "strictBindCallApply": true,     — bind/call/apply 타입 검사
    // "strictPropertyInitialization": true, — 클래스 프로퍼티 초기화 검사
    // "noImplicitAny": true,           — 암시적 any 금지
    // "noImplicitThis": true,          — 암시적 this 타입 금지
    // "alwaysStrict": true,            — "use strict" 강제
    // "useUnknownInCatchVariables": true  — catch의 error를 unknown으로
  }
}

noUncheckedIndexedAccess

인덱스 시그니처로 접근할 때 결과에 undefined를 포함합니다. 배열의 범위 밖 접근이나 존재하지 않는 키 접근을 방지합니다.

noUncheckedIndexedAccess 효과
typescript
const arr = [1, 2, 3];
 
// noUncheckedIndexedAccess: false
arr[0]; // number
 
// noUncheckedIndexedAccess: true
arr[0]; // number | undefined
// 안전한 접근
const first = arr[0];
if (first !== undefined) {
  console.log(first.toFixed(2)); // OK
}

verbatimModuleSyntax (5.0+)

import type과 import 구문의 의미를 엄격하게 구분합니다. 이전의 importsNotUsedAsValues와 preserveValueImports를 대체합니다.

verbatimModuleSyntax
typescript
// 타입만 import할 때는 반드시 'import type' 사용
import type { User } from "./types";
 
// 값을 import할 때는 일반 import 사용
import { createUser } from "./utils";
 
// 혼합: import에서 타입과 값을 구분
import { createUser, type UserConfig } from "./users";

erasableSyntaxOnly (5.8+)

Node.js의 --experimental-strip-types와 호환되는 코드만 허용합니다. 런타임 의미를 가진 TypeScript 전용 구문(enum, namespace, 파라미터 프로퍼티)을 금지합니다.

erasableSyntaxOnly 영향
typescript
// 사용 불가
enum Status { Active, Inactive }  // Error
namespace Utils { export function helper() {} }  // Error
class Service {
  constructor(private name: string) {}  // Error: 파라미터 프로퍼티
}
 
// 대안
const Status = { Active: 0, Inactive: 1 } as const;
type Status = (typeof Status)[keyof typeof Status];
 
function helper() {}
export { helper as Utils_helper };
 
class Service {
  private name: string;
  constructor(name: string) {
    this.name = name;
  }
}

rewriteRelativeImportExtensions (5.7+)

소스 코드에서 .ts 확장자를 사용하고, 출력에서 자동으로 .js로 변환합니다.

경로 다시쓰기
typescript
// 소스 코드
import { helper } from "./utils.ts";
import { config } from "../config.ts";
 
// 출력 (.js)
import { helper } from "./utils.js";
import { config } from "../config.js";
Tip

rewriteRelativeImportExtensions는 .ts → .js, .tsx → .jsx, .mts → .mjs, .cts → .cjs 변환을 지원합니다. Node.js ESM에서 확장자가 필수인 환경에서 특히 유용합니다.

프로젝트 참조 (Project References)

프로젝트 참조는 대규모 TypeScript 프로젝트를 여러 하위 프로젝트로 분할하여 빌드를 최적화하는 메커니즘입니다.

기본 구조

project/
  tsconfig.json          # 루트 (참조만 정의)
  packages/
    shared/
      tsconfig.json      # composite: true
      src/
        index.ts
    api/
      tsconfig.json      # references: [{ path: "../shared" }]
      src/
        server.ts
    web/
      tsconfig.json      # references: [{ path: "../shared" }]
      src/
        app.ts
packages/shared/tsconfig.json
json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src"]
}
packages/api/tsconfig.json
json
{
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "references": [
    { "path": "../shared" }
  ],
  "include": ["src"]
}
루트 tsconfig.json
json
{
  "files": [],
  "references": [
    { "path": "./packages/shared" },
    { "path": "./packages/api" },
    { "path": "./packages/web" }
  ]
}

빌드 명령어

프로젝트 참조 빌드
bash
# 전체 빌드 (의존성 순서 자동 해결)
tsc --build
 
# 특정 프로젝트만 빌드
tsc --build packages/api
 
# 강제 재빌드
tsc --build --force
 
# 클린 빌드
tsc --build --clean

composite 옵션

composite: true는 프로젝트 참조의 핵심입니다. 이 옵션을 켜면 다음이 강제됩니다.

  • declaration: true 자동 활성화
  • rootDir 설정 필수
  • 모든 소스 파일이 include 패턴에 포함되어야 함
  • .tsbuildinfo 파일을 생성하여 증분 빌드 지원

Isolated Declarations (5.5+)

Isolated Declarations는 .d.ts 파일 생성을 타입 검사 없이 수행할 수 있게 합니다. 이를 통해 선언 파일 생성을 별도 도구(swc, oxc 등)에 위임하고, 빌드를 병렬화할 수 있습니다.

Isolated Declarations 활성화
json
{
  "compilerOptions": {
    "isolatedDeclarations": true,
    "declaration": true
  }
}

이 모드에서는 공개 API의 타입이 명시적이어야 합니다. TypeScript가 추론에 의존하지 않고도 .d.ts를 생성할 수 있어야 합니다.

Isolated Declarations 요구사항
typescript
// Error: 반환 타입이 명시되지 않음
export function add(a: number, b: number) {
  return a + b;
}
 
// OK: 반환 타입 명시
export function add(a: number, b: number): number {
  return a + b;
}
 
// 내부 함수는 추론 가능 (export되지 않으므로)
function internal(x: number) {
  return x * 2; // OK
}
Info

Isolated Declarations는 모노레포에서 특히 유용합니다. 각 패키지의 .d.ts 생성을 병렬로 수행할 수 있어 빌드 시간이 크게 단축됩니다. 다만 공개 API에 모두 명시적 타입 주석이 필요하므로, 기존 프로젝트에 적용할 때는 점진적으로 진행하는 것이 좋습니다.

모노레포 타입 전략

내부 패키지 전략

모노레포의 내부 패키지는 빌드 없이 TypeScript 소스를 직접 참조하는 전략과, 빌드된 결과물을 참조하는 전략이 있습니다.

전략 1: 소스 직접 참조 (빌드 불필요)
json
// packages/ui/package.json
{
  "name": "@myorg/ui",
  "main": "./src/index.ts",
  "types": "./src/index.ts",
  "exports": {
    ".": {
      "types": "./src/index.ts",
      "default": "./src/index.ts"
    }
  }
}
전략 2: 빌드 결과물 참조
json
// packages/ui/package.json
{
  "name": "@myorg/ui",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    }
  }
}

tsconfig 상속 구조

모노레포에서는 공통 설정을 루트 tsconfig.base.json에 정의하고, 각 패키지가 이를 상속합니다.

tsconfig.base.json (루트)
json
{
  "compilerOptions": {
    "strict": true,
    "target": "es2024",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "verbatimModuleSyntax": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "skipLibCheck": true,
    "noUncheckedIndexedAccess": true,
    "isolatedModules": true
  }
}
packages/api/tsconfig.json
json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "composite": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "references": [
    { "path": "../shared" }
  ],
  "include": ["src"]
}

경로 별칭 관리

모노레포에서 패키지 간 참조를 위한 경로 별칭을 설정합니다.

경로 별칭 (tsconfig.json)
json
{
  "compilerOptions": {
    "paths": {
      "@myorg/shared": ["../shared/src"],
      "@myorg/shared/*": ["../shared/src/*"],
      "@myorg/ui": ["../ui/src"],
      "@myorg/ui/*": ["../ui/src/*"]
    }
  }
}
Warning

paths 설정은 TypeScript 컴파일러의 모듈 해석에만 영향을 주며, 런타임 번들러(Webpack, Vite, esbuild)에는 별도의 별칭 설정이 필요합니다. 번들러와 TypeScript의 경로 설정이 일치하지 않으면 "모듈을 찾을 수 없음" 에러가 발생합니다.

의존성 타입 관리

package.json에서 타입 의존성 관리
json
{
  "dependencies": {
    "@myorg/shared": "workspace:*"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.8.0"
  }
}

빌드 성능 최적화

증분 빌드

증분 빌드 설정
json
{
  "compilerOptions": {
    "incremental": true,
    "tsBuildInfoFile": "./.tsbuildinfo"
  }
}

.tsbuildinfo 파일은 이전 빌드의 정보를 캐싱하여, 변경된 파일만 다시 컴파일합니다.

skipLibCheck

라이브러리 타입 검사 건너뛰기
json
{
  "compilerOptions": {
    "skipLibCheck": true
  }
}

skipLibCheck은 .d.ts 파일의 타입 검사를 건너뜁니다. 외부 라이브러리의 타입 선언에 오류가 있어도 빌드가 통과하며, 빌드 시간이 크게 단축됩니다.

타입 검사와 빌드 분리

대규모 프로젝트에서는 타입 검사와 JavaScript 빌드를 분리하는 것이 일반적입니다.

package.json 스크립트
json
{
  "scripts": {
    "typecheck": "tsc --noEmit",
    "build": "esbuild src/index.ts --bundle --outdir=dist",
    "build:types": "tsc --emitDeclarationOnly",
    "ci": "pnpm typecheck && pnpm build && pnpm build:types"
  }
}
Tip

esbuild, swc, Vite 등의 빌드 도구는 TypeScript 타입을 제거만 하고 타입 검사는 수행하지 않습니다. 타입 검사는 CI/CD 파이프라인에서 tsc --noEmit으로 별도 수행하는 것이 일반적인 패턴입니다.

moduleResolution 전략 비교

전략용도특징
node16 / nodenextNode.js 프로젝트package.json의 exports 필드 존중, ESM/CJS 구분
bundler번들러 사용 프로젝트exports 존중, 확장자 선택적, 가장 유연
node (레거시)기존 CJS 프로젝트main 필드만 참조, exports 무시
번들러 프로젝트 (Vite, Webpack 등)
json
{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler"
  }
}
Node.js 프로젝트
json
{
  "compilerOptions": {
    "module": "nodenext",
    "moduleResolution": "nodenext"
  }
}

정리

TypeScript 프로젝트 설정은 타입 안전성, 빌드 성능, 개발자 경험에 직접적인 영향을 줍니다. 5.x에서 추가된 verbatimModuleSyntax, rewriteRelativeImportExtensions, erasableSyntaxOnly, isolatedDeclarations는 모듈 시스템의 명확성과 빌드 효율성을 크게 개선합니다. 모노레포에서는 프로젝트 참조, tsconfig 상속, Isolated Declarations를 조합하여 확장 가능한 타입 인프라를 구축할 수 있습니다.

다음 장에서는 이 시리즈의 마무리로, 지금까지 배운 모든 내용을 종합하여 타입 안전 유틸리티 라이브러리를 처음부터 설계하고 구현하는 실전 프로젝트를 진행합니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#typescript#performance#devtools

관련 글

프로그래밍

12장: 실전 프로젝트 — 타입 안전 유틸리티 라이브러리 설계

TypeScript 5.x의 고급 타입 기법을 총동원하여 타입 안전 유틸리티 라이브러리를 처음부터 설계하고 구현하는 실전 프로젝트입니다.

2026년 2월 13일·18분
프로그래밍

10장: 타입 수준 프로그래밍 — 타입으로 로직 작성하기

TypeScript 타입 시스템을 프로그래밍 언어로 활용하는 고급 기법 — 산술 연산, 문자열 파서, 상태 머신 등을 타입만으로 구현하는 패턴을 다룹니다.

2026년 2월 9일·14분
프로그래밍

9장: NoInfer와 새로운 유틸리티 타입

TypeScript 5.4의 NoInfer 유틸리티 타입과 5.x에서 추가된 새로운 타입 도구들을 활용한 라이브러리 설계 패턴을 다룹니다.

2026년 2월 7일·14분
이전 글10장: 타입 수준 프로그래밍 — 타입으로 로직 작성하기
다음 글12장: 실전 프로젝트 — 타입 안전 유틸리티 라이브러리 설계

댓글

목차

약 14분 남음
  • tsconfig.json 최적화
    • 5.x에서 중요한 컴파일러 옵션
    • strict 옵션 분해
    • noUncheckedIndexedAccess
    • verbatimModuleSyntax (5.0+)
    • erasableSyntaxOnly (5.8+)
    • rewriteRelativeImportExtensions (5.7+)
  • 프로젝트 참조 (Project References)
    • 기본 구조
    • 빌드 명령어
    • composite 옵션
  • Isolated Declarations (5.5+)
  • 모노레포 타입 전략
    • 내부 패키지 전략
    • tsconfig 상속 구조
    • 경로 별칭 관리
    • 의존성 타입 관리
  • 빌드 성능 최적화
    • 증분 빌드
    • skipLibCheck
    • 타입 검사와 빌드 분리
  • moduleResolution 전략 비교
  • 정리