본문으로 건너뛰기
Advertisement

1.3 tsconfig.json 기초

TypeScript 컴파일러는 수십 가지 동작 방식을 설정으로 제어할 수 있다. 이 설정들을 담는 파일이 tsconfig.json이다. 이 파일이 프로젝트에 있으면 tsc 명령을 인수 없이 실행해도 파일의 설정대로 컴파일이 진행된다. tsconfig.json을 제대로 이해하는 것이 TypeScript 프로젝트 관리의 첫걸음이다.

tsconfig.json의 역할

tsconfig.json은 두 가지를 정의한다.

  1. 컴파일 대상 파일: 어떤 파일을 컴파일할 것인가 (include, exclude, files)
  2. 컴파일러 동작: 어떻게 컴파일할 것인가 (compilerOptions)

tsconfig.json이 있는 디렉토리가 프로젝트의 루트로 인식된다. tsc를 실행하면 현재 디렉토리에서 시작해 부모 디렉토리를 올라가면서 tsconfig.json을 찾는다.

tsconfig.json 생성

tsc --init 명령으로 기본 tsconfig.json을 생성한다.

npx tsc --init

생성된 파일은 수백 줄이지만 대부분 주석 처리되어 있다. 실제 활성화된 옵션은 몇 개 되지 않는다. 주석을 제거한 기본 구조는 다음과 같다.

{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}

핵심 compilerOptions 5가지

1. target — 출력 JavaScript 버전

TypeScript를 어떤 버전의 JavaScript로 컴파일할 것인지 지정한다.

{
"compilerOptions": {
"target": "ES2022"
}
}

사용 가능한 값들:

설명주요 지원 기능
ES5ECMAScript 5var, 일반 함수
ES6 / ES2015ECMAScript 6let, const, 화살표 함수, 클래스
ES2017ECMAScript 2017async/await
ES2020ECMAScript 2020옵셔널 체이닝(?.), Nullish 병합(??)
ES2022ECMAScript 2022Top-level await, 클래스 필드
ESNext최신 제안 포함최신 기능 모두

target에 따라 TypeScript가 다운레벨 컴파일을 수행한다. 예를 들어 target: "ES5"로 설정하면 화살표 함수를 일반 함수로 변환한다.

// TypeScript 소스
const greet = (name: string) => `Hello, ${name}`;
// target: "ES5"로 컴파일 결과
var greet = function(name) { return "Hello, " + name; };

// target: "ES2022"로 컴파일 결과
const greet = (name) => `Hello, ${name}`;

현재 Node.js 20 LTS를 사용한다면 ES2022 또는 ES2023을 권장한다. 최신 브라우저를 타겟으로 하는 경우도 ES2020 이상이면 충분하다.

2. module — 모듈 시스템

컴파일된 JavaScript가 사용할 모듈 시스템을 지정한다.

{
"compilerOptions": {
"module": "CommonJS"
}
}

사용 가능한 주요 값들:

설명사용 환경
CommonJSrequire() / module.exports기존 Node.js 프로젝트
ESNextimport / export번들러 사용 환경 (Webpack, Vite)
NodeNextNode.js ESM 완전 지원최신 Node.js 프로젝트
None모듈 시스템 없음글로벌 스크립트

moduletarget은 함께 고려해야 한다. 번들러(Vite, Webpack)를 사용한다면 module: "ESNext"가 적합하다. 번들러 없이 Node.js에서 직접 실행한다면 module: "CommonJS" 또는 module: "NodeNext"를 사용한다.

3. strict — 엄격 모드

TypeScript의 엄격한 타입 검사 옵션들을 한 번에 활성화하는 플래그다.

{
"compilerOptions": {
"strict": true
}
}

"strict": true는 다음 9가지 옵션을 동시에 켠다.

옵션설명
strictNullChecksnullundefined를 별도 타입으로 처리
strictFunctionTypes함수 매개변수 타입 공변성/반공변성 검사
strictBindCallApplybind, call, apply 메서드 타입 검사
strictPropertyInitialization클래스 속성 초기화 검사
noImplicitAny타입 추론 불가 시 암묵적 any 금지
noImplicitThisthis 타입 명시 강제
alwaysStrict모든 파일에 "use strict" 추가
useUnknownInCatchVariablescatch 변수를 unknown 타입으로 처리
exactOptionalPropertyTypes옵셔널 속성에 undefined 명시적 할당 금지

strict가 없으면 타입 검사가 느슨해져 TypeScript를 사용하는 의미가 크게 줄어든다.

// strict: false 시 허용되는 위험한 코드
function processName(name) { // name이 암묵적으로 any 타입
return name.toUpperCase(); // 런타임 오류 가능
}

// strict: true 시 오류 발생
// 오류: Parameter 'name' implicitly has an 'any' type.
// strictNullChecks의 효과
function getLength(str: string | null): number {
return str.length;
// strict: true → 오류: Object is possibly 'null'

return str?.length ?? 0; // 올바른 처리
}

새 프로젝트는 반드시 "strict": true로 시작한다.

4. outDir — 컴파일 결과물 디렉토리

컴파일된 .js 파일이 생성될 디렉토리를 지정한다.

{
"compilerOptions": {
"outDir": "./dist"
}
}

outDir을 설정하지 않으면 .ts 파일과 동일한 디렉토리에 .js 파일이 생성되어 소스 파일과 뒤섞인다.

# outDir 설정 전 (지저분함)
src/
├── index.ts
├── index.js ← 자동 생성된 파일
├── utils.ts
└── utils.js ← 자동 생성된 파일

# outDir: "./dist" 설정 후 (깔끔함)
src/
├── index.ts
└── utils.ts
dist/
├── index.js
└── utils.js

5. rootDir — 소스 파일 루트

TypeScript 소스 파일의 루트 디렉토리를 지정한다.

{
"compilerOptions": {
"rootDir": "./src"
}
}

rootDiroutDir과 함께 사용해 디렉토리 구조를 유지하며 컴파일한다.

# rootDir: "./src", outDir: "./dist" 설정 시
src/
├── index.ts
└── utils/
└── helper.ts

→ 컴파일 후

dist/
├── index.js
└── utils/
└── helper.js

rootDir을 설정하지 않으면 TypeScript가 모든 입력 파일의 공통 상위 디렉토리를 자동으로 루트로 사용한다. 의도하지 않은 구조가 만들어질 수 있으므로 명시적으로 지정하는 것이 좋다.

include / exclude / files

컴파일 대상 파일을 제어하는 옵션들이다.

include

컴파일할 파일이나 패턴을 배열로 지정한다. glob 패턴을 지원한다.

{
"include": [
"src/**/*",
"types/**/*"
]
}

include를 지정하지 않으면 tsconfig.json이 있는 디렉토리의 모든 TypeScript 파일을 컴파일한다.

exclude

컴파일에서 제외할 파일이나 패턴을 지정한다.

{
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
"**/*.spec.ts"
]
}

exclude를 명시하지 않으면 node_modules, bower_components, jspm_packages, outDir이 자동으로 제외된다.

files

컴파일할 파일을 정확한 경로로 개별 지정한다. 소수의 파일만 필요할 때 사용한다.

{
"files": [
"src/index.ts",
"src/types.ts"
]
}

lib — 내장 타입 선언

TypeScript가 기본으로 포함하는 타입 정의 라이브러리를 지정한다.

{
"compilerOptions": {
"lib": ["ES2022", "DOM", "DOM.Iterable"]
}
}
lib 값포함 내용
ES2022ES2022 표준 API (Array, Promise, Map 등)
DOM브라우저 DOM API (document, window, HTMLElement 등)
DOM.IterableDOM Iterable 타입
WebWorkerWeb Worker API

Node.js 백엔드 프로젝트라면 DOM 관련 lib은 불필요하다. 브라우저 환경이 아니므로 documentwindow에 접근하면 오류여야 한다.

lib을 명시하지 않으면 target에 따라 자동으로 기본값이 설정된다. target: "ES2022"이면 ES2022 lib이 자동으로 포함된다.

sourceMap — 디버깅용 소스맵

소스맵을 생성한다. .js.map 파일이 함께 생성되어 브라우저 개발자 도구나 Node.js 디버거에서 컴파일된 JavaScript 대신 원본 TypeScript 파일로 디버깅할 수 있다.

{
"compilerOptions": {
"sourceMap": true
}
}

소스맵이 있으면 오류 스택 트레이스가 .js 파일의 줄 번호 대신 .ts 파일의 줄 번호를 표시한다.

실전 예제: 용도별 tsconfig 비교

Node.js 백엔드 프로젝트

{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"lib": ["ES2022"],
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
  • module: "CommonJS": 기존 Node.js 생태계와 호환
  • DOM lib 제외: 브라우저 API 타입 없음
  • resolveJsonModule: import data from './data.json' 허용

최신 Node.js 프로젝트 (ESM)

{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

package.json"type": "module"을 추가하고, .ts 파일에서 import 시 확장자를 .js로 명시한다.

// NodeNext 모듈 시스템에서 import
import { helper } from './utils/helper.js'; // .js 확장자 필수

Vite + React 프론트엔드 프로젝트

{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"noEmit": true,
"allowImportingTsExtensions": true
},
"include": ["src"],
"exclude": ["node_modules"]
}
  • noEmit: true: Vite가 번들링을 담당하므로 tsc는 타입 검사만 수행
  • moduleResolution: "bundler": Vite 같은 번들러에 최적화된 모듈 해석
  • jsx: "react-jsx": React 17+ JSX 변환

고수 팁

strict 모드를 항상 켜야 하는 이유

strict: true 없이 TypeScript를 사용하는 것은 안전벨트 없이 운전하는 것과 같다. 특히 strictNullChecks가 없으면 모든 타입에 nullundefined를 자유롭게 할당할 수 있어 런타임 오류를 막지 못한다.

기존 JavaScript 프로젝트를 TypeScript로 마이그레이션할 때 strict 모드를 한 번에 켜기 어렵다면, 개별 옵션을 하나씩 활성화하는 방법도 있다.

{
"compilerOptions": {
"strictNullChecks": true,
"noImplicitAny": true
}
}

moduleResolution: "bundler" 설명

moduleResolution은 TypeScript가 import한 모듈을 어떻게 찾는지를 지정한다.

설명
nodeNode.js의 기존 CommonJS 모듈 해석 방식
node16 / nodenextNode.js 16+ ESM 지원
bundlerVite, esbuild, Webpack 등 번들러 환경

bundler는 확장자를 생략한 import를 허용하고 package.jsonexports 필드를 인식한다. 번들러를 사용하는 프론트엔드 프로젝트에서는 bundler를 사용한다.

// moduleResolution: "bundler" — 확장자 없이 import 가능
import { helper } from './utils/helper';

// moduleResolution: "nodenext" — 확장자 필수
import { helper } from './utils/helper.js';

tsconfig 상속으로 설정 재사용

여러 환경(개발, 프로덕션, 테스트)에서 tsconfig를 나눠 쓸 때 extends로 기본 설정을 상속한다.

// tsconfig.base.json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}

// tsconfig.json (개발용)
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"sourceMap": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}

// tsconfig.test.json (테스트용)
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"types": ["jest", "node"]
},
"include": ["src/**/*", "tests/**/*"]
}

정리

옵션역할권장값
target출력 JS 버전ES2022 (Node.js 20 기준)
module모듈 시스템CommonJS (Node.js) / ESNext (번들러)
strict엄격 타입 검사true (항상)
outDir컴파일 결과물 위치"./dist"
rootDir소스 파일 루트"./src"
lib내장 타입 정의환경에 맞게 (DOM / ES2022)
sourceMap소스맵 생성true (개발 환경)
esModuleInteropCJS/ESM 상호운용true
skipLibCheckd.ts 타입 검사 생략true
moduleResolution모듈 해석 방식bundler (Vite) / node (CJS)

다음 장에서는 실제로 첫 TypeScript 파일을 작성하고 컴파일해 본다.

Advertisement