tsconfig 짧게 정리

BO·3일 전
1

글을 시작하며

TypeScript를 사용하면서 반드시 마주치는 파일이 바로 tsconfig.json입니다. 이 설정 파일은 TypeScript 컴파일러(tsc)가 어떤 방식으로 동작할지를 결정합니다. 최근에 다뤘던 프로젝트별로 요구 사항이 다르다 보니, 매번 어떤 옵션을 켜고 끌지 고민하게 되었습니다. 이 글에서는 꼭 알아야 하는 옵션, 반드시 켜두면 좋은 옵션, 그리고 특수한 상황에서 유용한 옵션들을 짧게 정리해 보았습니다.

꼭 알아야 하는 옵션

TypeScript를 사용하는 개발자라면 반드시 알아야하는 대표적인 항목들입니다. 프로젝트 환경이나 요구사항에 따라 적절히 값을 조정하는 것이 좋습니다.

target

어떤 버전의 JavaScript로 트랜스파일할지를 결정합니다. 예: ES5, ESNext, ES2020 등.

용도

  • 브라우저 호환성 또는 Node.js 버전에 따라 달라집니다.
  • 최신 기능 사용이 가능하면 ESNext로 설정해도 좋고, 레거시 지원이 필요하다면 ES5나 ES6를 선택할 수 있습니다.
  • 예를 들어, 오래된 브라우저까지 지원해야 할 경우 ES5가 적합하지만, 최신 환경에서 빠르고 간단히 개발하고 싶다면 ESNext가 권장됩니다.

예제

{
  "compilerOptions": {
    "target": "ES5"
  }
}
// ES5 문법( function, var 등 )으로 트랜스파일됨

module

모듈 시스템을 결정합니다. commonjs, ESNext 등을 설정할 수 있습니다.

용도

  • Node.js(구버전) 환경에서는 보통 commonjs를 사용합니다. require, module.exports 형태로 동작합니다.
  • 모던 번들러나 브라우저 기반 ESM 사용 시 ESNext를 권장합니다. import, export 문법이 그대로 유지되어 트리 쉐이킹이나 최적화에 유리할 수 있습니다.

예제

{
  "compilerOptions": {
    "module": "ESNext"
  }
}
// import/export가 유지된 상태로 JS가 생성됨

outDir, rootDir, include, exclude

  • rootDir: 소스 코드의 루트 디렉터리
  • outDir: 컴파일된 JS, .d.ts 등을 어디에 생성할지
  • include, exclude: 어떤 파일(혹은 폴더)을 빌드에 포함/제외할지

용도

빌드 결과물이 섞이지 않도록 디렉터리를 분리하고, 필요 없는 파일을 빌드 대상에서 제거합니다.

예를 들어 node_modules나 테스트 파일(.test.ts)을 제외하면 빌드 속도를 높이고 불필요한 산출물을 줄일 수 있습니다.

예제

{
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "**/*.test.ts"]
}

lib

전역 객체등에 대한 타입 정의를 어떤 환경으로 가정할지 지정합니다.

용도

  • 브라우저인지, Node.js인지, DOM API를 사용하는지 등에 따라 달라집니다.
  • "lib": ["DOM"]는 브라우저 환경에서 DOM 관련 API를 사용함을 의미하고, "ESNext"로 설정하면 최신 ECMAScript 문법에 대한 타입도 사용할 수 있습니다.

예제

{
  "compilerOptions": {
    "lib": ["DOM", "ESNext"]
  }
}

jsx

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의 타입을 엄격히 다루고 개발 편의성을 늘리기 위해서 매우 중요한 설정입니다.
프로젝트 규모가 커질수록, 타입 안정성과 코드 일관성을 위해 아래 옵션들을 기본적으로 켜두는 것을 강력히 권장합니다.

strict

TypeScript의 모든 엄격 모드를 활성화합니다.

이점

  • 암시적인 any를 허용하지 않으므로, 타입 선언을 까먹거나 실수했을 때 컴파일러가 즉시 경고를 줍니다.
  • 예) 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()}`;
}
// 컴파일 시 정확한 타입 검사 가능

skipLibCheck

node_modules 내부의 .d.ts 파일들에 대한 타입 검사 스킵

  • 이점: 외부 라이브러리의 사소한 타입 에러로 인한 빌드 중단을 막고, 빌드 속도를 크게 개선할 수 있습니다.

예제

{
  "compilerOptions": {
    "skipLibCheck": true
  }
}

forceConsistentCasingInFileNames

파일 import 시 대소문자가 일관되지 않으면 에러 처리

  • 이점: 파일명 대소문자 불일치 이슈를 사전에 막아줍니다.

예제

{
  "compilerOptions": {
    "forceConsistentCasingInFileNames": true
  }
}
// import { X } from './MyFile.ts' vs './myFile.ts'
// 이런 식으로 대소문자 불일치를 에러로 잡아줌

esModuleInterop

CommonJS 모듈을 import할 때 default import를 허용하도록 보정

  • 이점: CJS 기반 라이브러리를 import React from 'react'처럼 편하게 가져올 수 있습니다.

예제

{
  "compilerOptions": {
    "esModuleInterop": false
  }
}
// import \* as React from 'react';
// 반드시 namespace import 형태를 써야 하는 경우 발생
{
  "compilerOptions": {
    "esModuleInterop": true
  }
}
// import React from 'react';
// default import 형태를 사용 가능

isolatedModules

각 파일을 독립적인 모듈 단위로 처리 (Vite, Next.js 등 단일 파일 단위 트랜스파일 시 필수)

  • 이점: 파일별로 독립적인 스코프를 두기 때문에, 선언적/모듈식 코드를 더 엄격하게 작성하게 됩니다.
  • 주의: 모든 파일에 최소 하나의 import나 export가 있어야 함

예제

{
  "compilerOptions": {
    "isolatedModules": false
  }
}
// import, export가 없더라도 그냥 컴파일됨
{
  "compilerOptions": {
    "isolatedModules": true
  }
}
// import/export 없는 파일 -> 에러: TS1208
// "Cannot be compiled under --isolatedModules"

주의할 점

출처: https://ko.vite.dev/guide/features#isolatedmodules

noEmit

TypeScript 컴파일 시 실제 JS 파일을 생성하지 않습니다.

  • 이점: Vite, Webpack, Next.js처럼 자체 번들 파이프라인이 있는 경우, TS 단계에서 굳이 JS 산출물을 만들 필요가 없습니다. 순수하게 타입 검사만 수행하고, 나머지 번들링은 다른 도구에 맡기는 형태를 취할 수 있습니다.

예제

{
  "compilerOptions": {
    "noEmit": false,
    "outDir": "dist"
  }
}
// 빌드 시 dist 폴더에 JS 파일들 생성
{
  "compilerOptions": {
    "noEmit": true
  }
}
// 빌드해도 JS 결과물이 생성되지 않음

특수한 상황에서의 옵션들

프로젝트마다 필요한 설정이 다르므로, 아래와 같은 상황에서는 추가로 고려할 옵션들이 있습니다.

Next.js

Vite

JavaSript와 TypeScript가 함께 사용되는 프로젝트

allowJs: true

  • .js 파일도 TS 컴파일 파이프라인에서 처리

checkJs: true/false

  • .js 파일에 대해 타입 검사 여부

라이브러리 개발 시 고려할 옵션들

라이브러리를 배포하기 위해서는 타입 정의 파일(.d.ts) 생성이 매우 중요합니다. 또한, 모듈 시스템(CJS/ESM)을 어떻게 제공할지도 고려해야 합니다.

1. declaration: true

  • TS 컴파일 시 .d.ts 타입 선언 파일을 생성
  • 라이브러리를 사용하는 소비자에게 타입 정보를 제공합니다.

2. emitDeclarationOnly: true

  • JS 코드 대신 타입 선언 파일만 배출할 때 유용
  • Babel 등 다른 빌드 도구가 JS를 변환하도록 하고, TS는 타입만 추출.

3. declarationMap: true

  • .d.ts.map 파일을 생성하여, 타입 정의에 대한 소스 맵을 제공합니다.

4. declarationDir: "types"

  • .d.ts 파일을 별도 디렉토리에 모아두고 싶다면 설정
  • 예: "declarationDir": "dist/types"

5. composite, incremental

  • 대규모 프로젝트나 모노레포 환경에서 빌드 속도를 개선하고, 프로젝트 간 의존성을 관리할 때 사용.
  • composite: true가 설정되면, declaration도 자동으로 true가 됩니다.

실제 배포 시에는 Rollup/Vite/Webpack 등을 추가로 구성하여 CJS/ESM 번들을 동시에 제공하는 경우가 많습니다. 만약 타입 선언만 추출하고 싶다면 "emitDeclarationOnly": true로 설정하고, JS 빌드는 Babel/ESBuild 등을 통해 처리할 수 있습니다.

글을 마치며

tsconfig.json은 TypeScript 프로젝트의 필수 설정으로, 구성을 어떻게 하느냐에 따라 개발 생산성과 코드 안정성이 크게 달라집니다. 꼭 알아야 하는 옵션을 통해 트랜스파일 환경과 파일 구조를 잡고, 반드시 켜두길 권장하는 옵션으로 타입 안전성과 개발 편의성을 높인 뒤, 특수한 상황에 맞춰 추가 옵션들을 세부 조정하면 됩니다.

궁극적으로는 프로젝트 목적(앱, 라이브러리, 레거시 전환 등)에 따라 설정을 다르게 가져가야 하므로, 위의 예시들과 문서들에서 가이드하는 방법을 토대로 상황에 맞는 최적의 tsconfig.json을 구성해보시길 바랍니다.

profile
Time waits for no one

0개의 댓글