1.3 tsconfig.json 기초
TypeScript 컴파일러는 수십 가지 동작 방식을 설정으로 제어할 수 있다. 이 설정들을 담는 파일이 tsconfig.json이다. 이 파일이 프로젝트에 있으면 tsc 명령을 인수 없이 실행해도 파일의 설정대로 컴파일이 진행된다. tsconfig.json을 제대로 이해하는 것이 TypeScript 프로젝트 관리의 첫걸음이다.
tsconfig.json의 역할
tsconfig.json은 두 가지를 정의한다.
- 컴파일 대상 파일: 어떤 파일을 컴파일할 것인가 (
include,exclude,files) - 컴파일러 동작: 어떻게 컴파일할 것인가 (
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"
}
}
사용 가능한 값들:
| 값 | 설명 | 주요 지원 기능 |
|---|---|---|
ES5 | ECMAScript 5 | var, 일반 함수 |
ES6 / ES2015 | ECMAScript 6 | let, const, 화살표 함수, 클래스 |
ES2017 | ECMAScript 2017 | async/await |
ES2020 | ECMAScript 2020 | 옵셔널 체이닝(?.), Nullish 병합(??) |
ES2022 | ECMAScript 2022 | Top-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"
}
}
사용 가능한 주요 값들:
| 값 | 설명 | 사용 환경 |
|---|---|---|
CommonJS | require() / module.exports | 기존 Node.js 프로젝트 |
ESNext | import / export | 번들러 사용 환경 (Webpack, Vite) |
NodeNext | Node.js ESM 완전 지원 | 최신 Node.js 프로젝트 |
None | 모듈 시스템 없음 | 글로벌 스크립트 |
module과 target은 함께 고려해야 한다. 번들러(Vite, Webpack)를 사용한다면 module: "ESNext"가 적합하다. 번들러 없이 Node.js에서 직접 실행한다면 module: "CommonJS" 또는 module: "NodeNext"를 사용한다.
3. strict — 엄격 모드
TypeScript의 엄격한 타입 검사 옵션들을 한 번에 활성화하는 플래그다.
{
"compilerOptions": {
"strict": true
}
}
"strict": true는 다음 9가지 옵션을 동시에 켠다.
| 옵션 | 설명 |
|---|---|
strictNullChecks | null과 undefined를 별도 타입으로 처리 |
strictFunctionTypes | 함수 매개변수 타입 공변성/반공변성 검사 |
strictBindCallApply | bind, call, apply 메서드 타입 검사 |
strictPropertyInitialization | 클래스 속성 초기화 검사 |
noImplicitAny | 타입 추론 불가 시 암묵적 any 금지 |
noImplicitThis | this 타입 명시 강제 |
alwaysStrict | 모든 파일에 "use strict" 추가 |
useUnknownInCatchVariables | catch 변수를 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"
}
}
rootDir은 outDir과 함께 사용해 디렉토리 구조를 유지하며 컴파일한다.
# 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 값 | 포함 내용 |
|---|---|
ES2022 | ES2022 표준 API (Array, Promise, Map 등) |
DOM | 브라우저 DOM API (document, window, HTMLElement 등) |
DOM.Iterable | DOM Iterable 타입 |
WebWorker | Web Worker API |
Node.js 백엔드 프로젝트라면 DOM 관련 lib은 불필요하다. 브라우저 환경이 아니므로 document나 window에 접근하면 오류여야 한다.
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가 없으면 모든 타입에 null과 undefined를 자유롭게 할당할 수 있어 런타임 오류를 막지 못한다.
기존 JavaScript 프로젝트를 TypeScript로 마이그레이션할 때 strict 모드를 한 번에 켜기 어렵다면, 개별 옵션을 하나씩 활성화하는 방법도 있다.
{
"compilerOptions": {
"strictNullChecks": true,
"noImplicitAny": true
}
}
moduleResolution: "bundler" 설명
moduleResolution은 TypeScript가 import한 모듈을 어떻게 찾는지를 지정한다.
| 값 | 설명 |
|---|---|
node | Node.js의 기존 CommonJS 모듈 해석 방식 |
node16 / nodenext | Node.js 16+ ESM 지원 |
bundler | Vite, esbuild, Webpack 등 번들러 환경 |
bundler는 확장자를 생략한 import를 허용하고 package.json의 exports 필드를 인식한다. 번들러를 사용하는 프론트엔드 프로젝트에서는 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 (개발 환경) |
esModuleInterop | CJS/ESM 상호운용 | true |
skipLibCheck | d.ts 타입 검사 생략 | true |
moduleResolution | 모듈 해석 방식 | bundler (Vite) / node (CJS) |
다음 장에서는 실제로 첫 TypeScript 파일을 작성하고 컴파일해 본다.