TypeScript를 사용하면서 반드시 마주치는 파일이 바로 tsconfig.json
입니다. 이 설정 파일은 TypeScript 컴파일러(tsc)가 어떤 방식으로 동작할지를 결정합니다. 최근에 다뤘던 프로젝트별로 요구 사항이 다르다 보니, 매번 어떤 옵션을 켜고 끌지 고민하게 되었습니다. 이 글에서는 꼭 알아야 하는 옵션, 반드시 켜두면 좋은 옵션, 그리고 특수한 상황에서 유용한 옵션들을 짧게 정리해 보았습니다.
TypeScript를 사용하는 개발자라면 반드시 알아야하는 대표적인 항목들입니다. 프로젝트 환경이나 요구사항에 따라 적절히 값을 조정하는 것이 좋습니다.
어떤 버전의 JavaScript로 트랜스파일할지를 결정합니다. 예: ES5, ESNext, ES2020 등.
{
"compilerOptions": {
"target": "ES5"
}
}
// ES5 문법( function, var 등 )으로 트랜스파일됨
모듈 시스템을 결정합니다. commonjs, ESNext 등을 설정할 수 있습니다.
{
"compilerOptions": {
"module": "ESNext"
}
}
// import/export가 유지된 상태로 JS가 생성됨
빌드 결과물이 섞이지 않도록 디렉터리를 분리하고, 필요 없는 파일을 빌드 대상에서 제거합니다.
예를 들어 node_modules나 테스트 파일(.test.ts)을 제외하면 빌드 속도를 높이고 불필요한 산출물을 줄일 수 있습니다.
{
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}
전역 객체등에 대한 타입 정의를 어떤 환경으로 가정할지 지정합니다.
{
"compilerOptions": {
"lib": ["DOM", "ESNext"]
}
}
React 등 JSX/TSX를 다룰 때, 어떤 방식으로 JSX를 트랜스파일할지 결정합니다.
"react": React 16 이하 버전 사용 시 주로 사용되며, JSX를 createElement 형태로 변환합니다.
"react-jsx": React 17+ 버전의 새 JSX 트랜스폼을 사용하며, import React from "react" 없이도 JSX를 사용 가능하게 해줍니다.
"preserve": JSX를 그대로 남겨두고, 추가적인 변환은 Babel 등 별도 도구에서 처리하도록 합니다.
"react-native": React Native 환경을 위한 트랜스폼.
값 예시: "react", "react-jsx", "preserve", "react-native".
{
"compilerOptions": {
"jsx": "preserve"
}
}
꼭 켜야 하는 옵션들은 TypeScript의 타입을 엄격히 다루고 개발 편의성을 늘리기 위해서 매우 중요한 설정입니다.
프로젝트 규모가 커질수록, 타입 안정성과 코드 일관성을 위해 아래 옵션들을 기본적으로 켜두는 것을 강력히 권장합니다.
TypeScript의 모든 엄격 모드를 활성화합니다.
strictNullChecks
가 활성화되어 null
, undefined
를 안전하게 처리해야 합니다.{
"compilerOptions": {
"strict": false
}
}
// example.ts
function greet(name) {
// name에 타입이 명시되지 않았지만 에러 발생 X
return `Hello, ${name.toUpperCase()}`;
}
// 런타임에서 name이 undefined면 에러!
{
"compilerOptions": {
"strict": true
}
}
// example.ts
function greet(name: string) {
return `Hello, ${name.toUpperCase()}`;
}
// 컴파일 시 정확한 타입 검사 가능
node_modules
내부의 .d.ts
파일들에 대한 타입 검사 스킵
{
"compilerOptions": {
"skipLibCheck": true
}
}
파일 import 시 대소문자가 일관되지 않으면 에러 처리
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true
}
}
// import { X } from './MyFile.ts' vs './myFile.ts'
// 이런 식으로 대소문자 불일치를 에러로 잡아줌
CommonJS 모듈을 import할 때 default import를 허용하도록 보정
{
"compilerOptions": {
"esModuleInterop": false
}
}
// import \* as React from 'react';
// 반드시 namespace import 형태를 써야 하는 경우 발생
{
"compilerOptions": {
"esModuleInterop": true
}
}
// import React from 'react';
// default import 형태를 사용 가능
각 파일을 독립적인 모듈 단위로 처리 (Vite, Next.js 등 단일 파일 단위 트랜스파일 시 필수)
{
"compilerOptions": {
"isolatedModules": false
}
}
// import, export가 없더라도 그냥 컴파일됨
{
"compilerOptions": {
"isolatedModules": true
}
}
// import/export 없는 파일 -> 에러: TS1208
// "Cannot be compiled under --isolatedModules"
출처: https://ko.vite.dev/guide/features#isolatedmodules
TypeScript 컴파일 시 실제 JS 파일을 생성하지 않습니다.
{
"compilerOptions": {
"noEmit": false,
"outDir": "dist"
}
}
// 빌드 시 dist 폴더에 JS 파일들 생성
{
"compilerOptions": {
"noEmit": true
}
}
// 빌드해도 JS 결과물이 생성되지 않음
프로젝트마다 필요한 설정이 다르므로, 아래와 같은 상황에서는 추가로 고려할 옵션들이 있습니다.
noEmit: true
isolatedModules: true
jsx: "preserve" || "react-jsx"
출처: https://nextjs.org/docs/pages/api-reference/config/typescript#incremental-type-checking
module: "ESNext"
isolatedModules: true
noEmit: true
useDefineForClassFields: true
라이브러리를 배포하기 위해서는 타입 정의 파일(.d.ts) 생성이 매우 중요합니다. 또한, 모듈 시스템(CJS/ESM)을 어떻게 제공할지도 고려해야 합니다.
실제 배포 시에는 Rollup/Vite/Webpack 등을 추가로 구성하여 CJS/ESM 번들을 동시에 제공하는 경우가 많습니다. 만약 타입 선언만 추출하고 싶다면 "emitDeclarationOnly": true로 설정하고, JS 빌드는 Babel/ESBuild 등을 통해 처리할 수 있습니다.
tsconfig.json
은 TypeScript 프로젝트의 필수 설정으로, 구성을 어떻게 하느냐에 따라 개발 생산성과 코드 안정성이 크게 달라집니다. 꼭 알아야 하는 옵션을 통해 트랜스파일 환경과 파일 구조를 잡고, 반드시 켜두길 권장하는 옵션으로 타입 안전성과 개발 편의성을 높인 뒤, 특수한 상황에 맞춰 추가 옵션들을 세부 조정하면 됩니다.
궁극적으로는 프로젝트 목적(앱, 라이브러리, 레거시 전환 등)에 따라 설정을 다르게 가져가야 하므로, 위의 예시들과 문서들에서 가이드하는 방법을 토대로 상황에 맞는 최적의 tsconfig.json을 구성해보시길 바랍니다.