TypeScript 성능 향상의 비밀: tsconfig.json 마스터하기
TypeScript 5.7 버전 기준으로, 완벽한 tsconfig.json
설정을 위한 핵심 옵션들을 알아볼까요?
이 블로그 글에서 다루지 않는 내용
이 글은 로컬 모듈이 모두 ESM인 프로젝트 설정 방법에 중점을 두고 있습니다.
CommonJS 모듈 import에 대한 팁은 제공하지만, 다음 내용은 다루지 않습니다.
allowJs
및checkJs
옵션을 사용한 일반 JavaScript 파일 가져오기 및 타입 검사- JSX 설정 (TypeScript 핸드북의 "JSX" 섹션 참조)
composite
옵션 등을 사용한 모노레포 프로젝트 설정 (TypeScript 핸드북의 "프로젝트 참조" 챕터와 제 블로그 글 "npm workspaces 및 TypeScript 프로젝트 참조를 통한 간단한 모노레포" 참조)
표기법
소스 코드에서 추론된 타입을 표시하기 위해 ts-expect
npm 패키지를 사용합니다. 예:
// someVariable의 추론된 타입이 boolean인지 확인
expectType<boolean>(someVariable);
JSON 파일에서 후행 쉼표를 자주 사용하는데요, tsconfig.json
에서 지원되고, 코드 재정렬 및 복사에 도움이 되기 때문입니다.
extends
를 사용하여 기본 파일 확장
extends
옵션을 사용하면 모듈 지정자를 통해 기존 tsconfig.json
파일을 참조할 수 있습니다 (JSON 파일을 가져오는 것처럼).
해당 파일은 현재 tsconfig
가 확장하는 기본 설정이 되며, 기본 설정의 모든 옵션을 포함하지만, 필요에 따라 재정의하거나 새로운 옵션을 추가할 수 있습니다.
tsconfig/bases
GitHub 저장소는 @tsconfig
npm 네임스페이스에서 사용 가능한 기본 설정들을 제공합니다.
npm을 통해 로컬에 설치한 후 다음과 같이 사용할 수 있습니다.
{
"extends": "@tsconfig/node-lts/tsconfig.json",
}
아쉽게도 제게 딱 맞는 파일은 없었지만, 여러분의 tsconfig
설정에 영감을 줄 수 있을 겁니다.
입력 파일 위치
{
"include": ["src/**/*", "test/**/*"],
}
TypeScript에게 입력 파일을 알려줘야 합니다. 다음 옵션을 사용할 수 있습니다.
files
: 모든 입력 파일의 전체 배열include
: 와일드카드를 사용한 패턴 배열을 통해 입력 파일을 지정합니다. 패턴은tsconfig.json
을 기준으로 해석됩니다.exclude
:include
에서 지정된 파일 중 제외할 파일을 패턴 배열을 통해 지정합니다.
출력 결과
출력 파일 위치
"compilerOptions": {
"rootDir": ".",
"outDir": "dist",
}
TypeScript가 출력 파일 위치를 결정하는 방법은 다음과 같습니다.
tsconfig.json
을 기준으로 한 입력 파일의 상대 경로를 가져옵니다.rootDir
에 지정된 접두사를 제거합니다.- 결과를
outDir
에 추가합니다.
rootDir
의 기본값은 입력 파일의 상대 경로에서 가장 긴 공통 접두사입니다.
예를 들어, 다음 tsconfig.json
을 생각해 보세요.
{
"include": ["src/**/*", "test/**/*"],
"compilerOptions": {
"rootDir": ".",
"outDir": "dist",
}
}
프로젝트 파일 구조는 다음과 같습니다.
/tmp/my-proj/
tsconfig.json
src/
main.ts
test/
test.ts
TypeScript 컴파일러는 다음과 같은 출력을 생성합니다.
/tmp/my-proj/
dist/
src/
main.js
test/
test.js
tsconfig.json
에서 rootDir
를 제거해도 기본값이 "."이기 때문에 출력은 동일합니다.
하지만 include
도 변경하면 출력이 달라집니다.
{
"include": ["src/**/*"],
"compilerOptions": {
"outDir": "dist",
}
}
이제 rootDir
의 기본값은 src
이고 출력은 다음과 같습니다.
/tmp/my-proj/
dist/
main.js
rootDir
의 기본값은 include
에 따라 변경되기 때문에, 저는 tsconfig.json
에 명시적으로 지정하는 것을 선호합니다.
하지만 기본값이 적합하다면 생략해도 괜찮습니다.
소스 맵 생성
"compilerOptions": {
"sourceMap": true,
}
sourceMap
은 트랜스파일된 JavaScript에서 원본 TypeScript로 연결되는 소스 맵 파일을 생성합니다.
디버깅에 도움이 되므로 일반적으로 사용하는 것이 좋습니다.
.d.ts
파일 생성 (라이브러리 등)
트랜스파일된 TypeScript 코드를 다른 TypeScript 코드에서 사용하려면 .d.ts
파일을 포함해야 합니다.
"compilerOptions": {
"declaration": true,
"declarationMap": true, // 가져오는 쪽에서 소스 코드로 이동할 수 있도록 함
}
선택적으로 npm 패키지에 TypeScript 소스 코드를 포함하고 declarationMap
을 활성화할 수 있습니다.
이렇게 하면 가져오는 쪽에서 타입을 클릭하거나 값의 정의로 이동할 때 편집기가 원본 소스 코드로 이동합니다.
declarationDir
옵션
기본적으로 각 .d.ts
파일은 해당 .js
파일 옆에 위치합니다.
이를 변경하려면 declarationDir
옵션을 사용할 수 있습니다.
생성된 파일 미세 조정
"compilerOptions": {
"newLine": "lf",
"removeComments": false,
}
위에 표시된 값은 기본값입니다.
newLine
: 생성된 파일의 줄 끝 문자를 설정합니다. 허용되는 값은 다음과 같습니다."lf"
:"\n"
(Unix)"crlf"
:"\r\n"
(Windows)
removeComments
: 활성화된 경우 TypeScript 파일의 모든 주석이 트랜스파일된 JavaScript 파일에서 제거됩니다. 저는 기본값을 유지하고 주석을 제거하지 않는 것을 선호합니다.- 트랜스파일된 JavaScript를 읽기 쉽게 해 줍니다. 특히 TypeScript 소스 코드가 포함되지 않은 경우 더욱 그렇습니다.
- 번들러는 주석을 제거합니다.
- Node.js에서는 추가 부담이 크게 중요하지 않습니다.
언어 및 플랫폼 기능
"compilerOptions": {
"target": "ES2024",
// DOM을 사용하려면 생략
"lib": [ "ES2024" ],
}
target
target
은 최신 JavaScript 구문을 이전 구문으로 트랜스파일할지 결정합니다.
예를 들어, target
이 "ES5"
이면 화살표 함수 () => {}
가 함수 표현식 function () {}
으로 트랜스파일됩니다.
다양한 플랫폼에 대한 권장 설정은 tsconfig/bases
GitHub 저장소에서 확인할 수 있습니다.
"ESNext"
값은 "설치된 TypeScript에서 지원하는 가장 높은 버전"을 의미합니다.
TypeScript 버전에 따라 변경되므로 업그레이드 시 문제가 발생할 수 있습니다.
JavaScript 기능을 트랜스파일하지 않는 옵션이 있어야 하는지 궁금합니다.
반면에 최신 JavaScript를 이전 브라우저에서 작성할 수 있다는 것은 매우 편리합니다.
적절한 target
선택 방법
대상 플랫폼에서 작동하는 ECMAScript 버전을 선택해야 합니다. 다음 두 표는 좋은 개요를 제공합니다.
- 브라우저: compat-table.github.io
- Node.js: node.green
공식 tsconfig
기본 설정에서 target
값을 확인할 수도 있습니다.
lib
lib
는 내장 API에 사용할 수 있는 타입을 결정합니다(예: Math
또는 내장 타입의 메서드).
TypeScript 문서에서는 배열에 추가할 수 있는 값을 설명합니다. 전체 목록은 TypeScript 저장소에서 확인할 수 있습니다.
타입을 찾고 있다면 해당 파일을 검색해 보세요!
"ES2024"
, "DOM"
과 같은 카테고리와 "DOM.Iterable"
, "ES2024.Promise"
와 같은 하위 카테고리가 있습니다.
값은 대소문자를 구분하지 않습니다.
Visual Studio Code의 자동 완성 제안에는 대문자가 많이 포함되어 있지만 파일 이름에는 대문자가 없습니다.
lib
값은 어느 쪽이든 쓸 수 있습니다.
TypeScript는 언제 특정 API를 지원할까요? "적어도 두 개의 브라우저 엔진(즉, 두 개의 크로뮴 브라우저뿐만 아니라)에서 접두사/플래그 없이 사용 가능해야 합니다"
target
을 통한 lib
설정
target
은 lib
의 기본값을 결정합니다.
lib
이 생략되고 target
이 "ES20YY"
이면 "ES20YY.Full"
이 사용됩니다.
그러나 이것은 직접 사용할 수 있는 값이 아닙니다.
lib
을 제거했을 때와 동일한 효과를 얻으려면 (예를 들어) es2024.full.d.ts
의 내용을 직접 열거해야 합니다.
/// <reference lib="es2024" />
/// <reference lib="dom" />
/// <reference lib="webworker.importscripts" />
/// <reference lib="scripthost" />
/// <reference lib="dom.iterable" />
/// <reference lib="dom.asynciterable" />
이 파일에서 흥미로운 현상을 관찰할 수 있습니다.
"ES20YY"
카테고리에는 일반적으로 모든 하위 카테고리가 포함됩니다."DOM"
카테고리는 그렇지 않습니다. 예를 들어,"DOM.Iterable"
하위 카테고리는 아직 포함되어 있지 않습니다.
무엇보다도 "DOM.Iterable"
은 NodeList
에 대한 반복을 가능하게 합니다. 예:
for (const x of document.querySelectorAll('div')) {}
내장 Node.js API 타입
Node.js API 타입은 npm 패키지를 통해 설치해야 합니다.
npm install @types/node
모듈 시스템
module
: TypeScript가 가져온 모듈을 찾는 방법
다음 옵션은 TypeScript가 가져온 모듈을 찾는 방법에 영향을 줍니다.
"compilerOptions": {
"module": "Node16",
"noUncheckedSideEffectImports": true,
}
module
옵션
이 옵션을 사용하여 모듈 처리 시스템을 지정합니다.
이 옵션을 올바르게 설정하면 관련 옵션인 moduleResolution
도 처리되는데요, 이 옵션에 적합한 기본값을 제공합니다.
TypeScript 문서에서는 다음 두 값 중 하나를 권장합니다.
- Node.js:
"Node16"
은 CommonJS와 최신 ESM 기능을 모두 지원합니다.moduleResolution
:"Node16"
을 의미합니다."NodeNext"
도 있지만, 이는 변경될 수 있는 대상입니다. 현재는"Node16"
과 동일하지만 나중에 변경될 수 있으며, 기존 코드베이스를 손상시킬 수 있습니다.
- 번들러:
"Preserve"
는 CommonJS와 최신 ESM 기능을 모두 지원합니다. 대부분의 번들러에서 수행하는 작업과 일치합니다.moduleResolution
:"bundler"
를 의미합니다.
번들러는 주로 Node.js의 방식을 따르기 때문에 저는 항상 "Node16"
을 사용하고 있으며, 아직 문제가 발생하지 않았습니다.
두 경우 모두 TypeScript는 가져오는 로컬 모듈의 전체 이름을 언급하도록 합니다.
Node.js가 CommonJS로만 컴파일되었을 때 자주 사용했던 것처럼 파일 이름 확장자를 생략할 수 없습니다.
새 접근 방식은 순수 JavaScript ESM의 작동 방식을 반영합니다.
module:Node16
은 target:es2022
를 의미하지만, 이 경우 module
과 target
은 module
과 moduleResolution
만큼 밀접하게 관련되어 있지 않기 때문에 수동으로 target
을 설정하는 것을 선호합니다.
또한 module:Bundler
는 아무것도 의미하지 않습니다.
noUncheckedSideEffectImports
옵션
기본적으로 TypeScript는 빈 import가 존재하지 않더라도 오류를 발생시키지 않습니다.
이러한 동작의 이유는 일부 번들러에서 TypeScript가 아닌 아티팩트를 모듈과 연결하기 위해 지원하는 패턴이기 때문입니다.
그리고 TypeScript는 TypeScript 파일만 인식합니다.
이러한 import는 다음과 같습니다.
import './component-styles.css';
흥미롭게도 TypeScript는 일반적으로 존재하지 않는 TypeScript 파일을 빈 import하는 것도 허용합니다.
존재하지 않는 파일에서 무언가를 가져올 때만 오류를 발생시킵니다.
import './does-not-exist.js'; // 오류 없음!
noUncheckedSideEffectImports
를 true
로 설정하면 이러한 동작이 변경됩니다.
TypeScript가 아닌 아티팩트를 가져오기 위한 대안은 나중에 설명하겠습니다.
(JS 파일 생성 없이) TypeScript 직접 실행
대부분의 브라우저가 아닌 JavaScript 플랫폼은 이제 TypeScript 코드를 트랜스파일하지 않고 직접 실행할 수 있습니다.
"compilerOptions": {
"allowImportingTsExtensions": true,
// JavaScript로 컴파일하는 경우에만 필요
"rewriteRelativeImportExtensions": true,
}
allowImportingTsExtensions
: 이 옵션을 사용하면 트랜스파일된 버전이 아닌 모듈의 TypeScript 버전을 가져올 수 있습니다.rewriteRelativeImportExtensions
: 이 옵션을 사용하면 직접 실행하기 위한 TypeScript 코드도 트랜스파일할 수 있습니다. 기본적으로 TypeScript는 import의 모듈 지정자를 변경하지 않습니다. 이 옵션에는 몇 가지 주의 사항이 있습니다.- 상대 경로만 다시 작성됩니다.
baseUrl
및paths
옵션을 고려하지 않고 "단순하게" 다시 작성됩니다(이 블로그 게시물의 범위를 벗어납니다)."exports"
및"imports"
속성을 통해 라우팅되는 경로는 상대 경로처럼 보이지 않으므로 다시 작성되지 않습니다.
타입 검사(만)를 위해 tsc
를 사용하려면 noEmit
옵션 섹션을 참조하세요.
Node의 TypeScript 기본 지원에 대한 자세한 내용은 제 블로그 게시물을 참조하세요.
JSON 가져오기
"compilerOptions": {
"resolveJsonModule": true,
}
resolveJsonModule
옵션을 사용하면 JSON 파일을 가져올 수 있습니다.
import config from './config.json' with {type: 'json'};
console.log(config.hello);
다른 TypeScript가 아닌 아티팩트 가져오기
TypeScript가 알지 못하는 확장자 ext
를 가진 파일 basename.ext
를 가져올 때마다 basename.d.ext.ts
파일을 찾습니다. 찾을 수 없는 경우 오류를 발생시킵니다.
TypeScript 문서에는 이러한 파일의 좋은 예가 있습니다.
알 수 없는 import에 대한 오류를 방지하는 두 가지 방법이 있습니다.
첫째, allowArbitraryExtensions
옵션을 사용하여 이 경우 모든 종류의 오류 보고를 방지할 수 있습니다.
둘째, 와일드카드 지정자를 사용하는 주변 모듈 선언을 생성할 수 있습니다.
이 .d.ts
파일은 TypeScript가 인식하는 파일 중 어딘가에 있어야 합니다.
다음 예는 파일 이름 확장자가 .css
인 모든 import에 대한 오류를 억제합니다.
// ./src/globals.d.ts
declare module "*.css" {}
타입 검사
"compilerOptions": {
"strict": true,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
}
제 생각에는 strict
가 필수입니다.
나머지 설정은 코드에 대한 추가적인 엄격성을 원하는지 여부에 따라 직접 결정해야 합니다.
모든 설정을 추가하고 어떤 설정이 너무 많은 문제를 일으키는지 확인하는 것부터 시작할 수 있습니다.
이 섹션에서는 strict
에 포함된 설정(예: noImplicitAny
)은 무시합니다.
noFallthroughCasesInSwitch
:true
이면 비어 있지 않은switch
케이스는break
,return
또는throw
로 끝나야 합니다.noImplicitOverride
:true
이면 상위 클래스 메서드를 재정의하는 메서드는override
한정자를 가져야 합니다.noImplicitReturns
:true
이면 반환 타입이void
인 경우에만 "암시적 반환"(함수 또는 메서드 종료)이 허용됩니다.
exactOptionalPropertyTypes
true
이면 다음 예에서 .colorTheme
을 생략할 수만 있고 undefined
로 설정할 수는 없습니다.
interface Settings {
// 속성이 없으면 "system"을 의미합니다.
colorTheme?: 'dark' | 'light';
}
const obj1: Settings = {}; // 허용
const obj2: Settings = {colorTheme: undefined}; // 허용되지 않음
noPropertyAccessFromIndexSignature
true
이면 다음과 같은 타입의 경우 알 수 없는 속성에 점 표기법을 사용할 수 없고 알려진 속성에만 사용할 수 있습니다.
interface ObjectWithId {
id: string;
[key: string]: string;
}
declare const obj: ObjectWithId;
const value1 = obj.id; // 허용
const value2 = obj['unknownProp']; // 허용
const value3 = obj.unknownProp; // 허용되지 않음
noUncheckedIndexedAccess
true
이면 알 수 없는 속성의 타입은 undefined
와 인덱스 시그니처 타입의 합집합입니다.
interface ObjectWithId {
id: string;
[key: string]: string;
}
declare const obj: ObjectWithId;
expectType<string>(obj.id);
expectType<undefined | string>(obj.unknownProp);
noUncheckedIndexedAccess
및 배열
noUncheckedIndexedAccess
옵션은 배열 처리 방식에도 영향을 줍니다.
const arr = ['a', 'b'];
const elem = arr[0];
expectType<undefined | string>(elem);
이 설정이 false
이면 elem
의 타입은 string
입니다.
배열의 일반적인 패턴 중 하나는 요소에 액세스하기 전에 길이를 확인하는 것입니다.
그러나 noUncheckedIndexedAccess
를 사용하면 이 패턴이 불편해집니다.
function logElemAt0(arr: Array<string>) {
if (0 < arr.length) {
const elem = arr[0];
expectType<undefined | string>(elem);
console.log(elem);
}
}
따라서 다른 패턴을 사용하는 것이 더 합리적입니다.
function logElemAt0(arr: Array<string>) {
if (0 in arr) {
const elem = arr[0];
expectType<string>(elem);
console.log(elem);
}
}
저는 이 옵션에 대해 갈등을 느낍니다. 한편으로는 새 패턴이 배열에 구멍이 있을 수 있음을 반영합니다.
반면에 구멍은 드물고 ES6 이후 JavaScript는 구멍을 값이 undefined
인 요소로 취급합니다.
> Array.from([,,,])
[ undefined, undefined, undefined ]
적절한 기본값을 가진 타입 검사 옵션
기본적으로 다음 옵션은 편집기에서 경고를 생성하지만 컴파일러 오류를 생성하거나 문제를 무시하도록 선택할 수도 있습니다.
allowUnreachableCode
allowUnusedLabels
noUnusedLocals
noUnusedParameters
상호 운용성: 외부 도구가 TypeScript를 JavaScript 및 선언으로 컴파일하도록 지원
"compilerOptions": {
"verbatimModuleSyntax": true,
"isolatedDeclarations": true,
}
TypeScript 컴파일러는 세 가지 작업을 수행합니다.
- 타입 검사
- JavaScript 파일 생성
- 선언 파일 생성
요즘에는 2번과 3번을 훨씬 빠르게 수행하는 외부 도구가 인기를 얻고 있습니다. 이러한 도구에는 두 가지 요구 사항이 있습니다.
- 출력 파일을 생성할 때 입력 파일에서 가져온 파일의 정보를 찾지 않아도 됩니다.
- 의미 분석도 필요하지 않습니다. 구문 분석만 필요합니다.
이러한 제약 조건을 정적으로 적용하는 두 가지 설정이 있습니다. 컴파일러 오류를 발생시키지만 JavaScript 및 선언 생성 방식은 변경하지 않습니다.
verbatimModuleSyntax
: TypeScript를 JavaScript로 컴파일하는 데 도움이 됩니다.isolatedDeclarations
: TypeScript를 선언으로 컴파일하는 데 도움이 됩니다.
verbatimModuleSyntax
: TypeScript를 JavaScript로 컴파일
TypeScript 파일의 대부분의 JavaScript가 아닌 부분은 쉽게 감지할 수 있습니다.
예외는 import입니다. (비교적 간단한) 의미 분석 없이는 import가 (TypeScript) 타입인지 (JavaScript) 값인지 알 수 없습니다.
verbatimModuleSyntax
가 활성화된 경우 타입 전용 import에 type
키워드를 추가해야 합니다. 예:
// 입력
import {type SomeInterface, SomeClass} from './my-module.js';
// 출력
import {SomeClass} from './my-module.js';
클래스는 값이자 타입이라는 점에 유의하세요.
이 경우 구문의 해당 부분은 일반 JavaScript에 남아 있을 수 있으므로 type
키워드가 필요하지 않습니다.
export
절에 타입을 언급하는 경우에도 type
을 추가해야 합니다.
interface MyInterface {}
export {type MyInterface};
// 대안:
export interface MyInterface {}
isolatedModules
verbatimModuleSyntax
를 활성화하면 isolatedModules
도 활성화되므로 전자 설정만 필요합니다.
후자는 문제가 되는 몇 가지 모호한 기능을 사용하지 못하도록 합니다.
덧붙여서, 이 옵션을 사용하면 esbuild가 TypeScript를 JavaScript로 컴파일할 수 있습니다.
isolatedDeclarations
: TypeScript를 선언으로 컴파일
isolatedDeclarations
는 주로 내보낸 함수 및 메서드에 반환 타입 주석을 추가하도록 합니다.
즉, 외부 도구가 반환 타입을 추론할 필요가 없습니다.
자세한 내용은 TypeScript 5.5 릴리스 노트의 격리된 선언에 대한 포괄적인 섹션을 참조하세요.
noEmit
: 타입 검사에만 tsc
사용
TypeScript 코드를 직접 실행하거나 TypeScript 파일을 컴파일하기 위해 외부 도구(JavaScript 파일, 선언 파일 등)를 사용하는 경우와 같이 타입 검사에만 tsc
를 사용하려는 경우가 있습니다.
"compilerOptions": {
"noEmit": true,
}
noEmit
:true
이면tsc
를 실행할 때 TypeScript 코드의 타입만 검사하고 파일을 생성하지 않습니다.- 출력 관련 옵션을 추가로 제거할지 여부는 외부 도구에서 사용하는 옵션에 따라 달라집니다.
ESM에서 CommonJS 가져오기
ESM 모듈에서 CommonJS 모듈을 가져올 때 한 가지 주요 문제가 발생합니다.
- ESM에서 기본 export는 모듈 네임스페이스 객체의
.default
속성입니다. - CommonJS에서 모듈 객체는 기본 export입니다. 예를 들어,
module.exports
를 함수로 설정하는 CommonJS 모듈이 많이 있습니다.
도움이 되는 두 가지 옵션을 살펴보겠습니다.
allowSyntheticDefaultImports
: CommonJS 모듈의 기본 import 타입 검사
이 옵션은 TypeScript에서 생성된 JavaScript 코드가 아닌 타입 검사에만 영향을 줍니다.
활성화된 경우 CommonJS 모듈의 기본 import는 module.exports.default
가 아닌 module.exports
를 참조합니다.
단, module.exports.default
가 없는 경우에만 해당됩니다.
이는 Node.js가 CommonJS 모듈의 기본 import를 처리하는 방식을 반영합니다.
"CommonJS 모듈을 가져올 때 module.exports
객체가 기본 export로 제공됩니다. 더 나은 생태계 호환성을 위해 편의상 정적 분석에 의해 제공되는 명명된 export를 사용할 수 있습니다."
이 옵션이 필요할까요? 네, 하지만 moduleResolution
이 "bundler"
이거나 module
이 "Node16"
인 경우(이는 esModuleInterop
를 활성화하고 allowSyntheticDefaultImports
를 활성화합니다) 자동으로 활성화됩니다.
esModuleInterop
: TypeScript를 CommonJS 코드로 더 잘 컴파일
이 옵션은 생성된 CommonJS 코드에 영향을 줍니다.
false
인 경우:import * as m from 'm'
은const m = require('m')
으로 컴파일됩니다.import m from 'm'
은 (대략적으로)const m = require('m')
으로 컴파일되고m
의 모든 액세스는m.default
로 컴파일됩니다.
true
인 경우:import * as m from 'm'
은module.exports
와 동일한 속성과module.exports
를 참조하는.default
속성을 가진 새 객체를m
에 할당합니다.import m from 'm'
은module.exports
를 참조하는 단일 속성.default
를 가진 새 객체를m
에 할당합니다.m
의 모든 액세스는m.default
로 컴파일됩니다.
CommonJS 모듈에 마커 속성 . __esModule
이 있는 경우 항상 esModuleInterop
가 꺼져 있는 것처럼 가져옵니다.
이 옵션이 필요할까요? 아니요, ESM 모듈만 작성하기 때문입니다.
적절한 기본값을 가진 추가 옵션
일반적으로 다음 옵션은 무시해도 됩니다.
moduleDetection
: 이 옵션은 TypeScript가 파일이 스크립트인지 모듈인지 결정하는 방법을 구성합니다. 기본값인"auto"
가 대부분의 경우 잘 작동하기 때문에 일반적으로 생략할 수 있습니다. 코드베이스에 import도 export도 없는 모듈이 있는 경우에만 명시적으로"force"
로 설정해야 합니다.module
이"Node16"
이고package.json
에"type":"module"
이 있는 경우 해당 파일도 모듈로 해석됩니다.skipLibCheck
: 선언 파일로 특별한 작업을 하지 않는 한 이 옵션을 무시하고 기본 설정을 사용해도 됩니다. GitHub의 토론에 따르면true
로 설정하면 단점이 많습니다(기본값은false
).
TypeScript에서 고려하는 package.json
설정
TypeScript는 몇 가지 package.json
속성을 고려합니다.
type
: 이것은 중요한 설정입니다. ESM 모듈로 컴파일하는 경우package.json
에 항상 다음이 포함되어야 합니다.
"type": "module"
exports
: 패키지의 어떤 파일이 공개적으로 표시되는지 지정하고 경로를 다시 매핑합니다(가져오는 쪽에서 보는 내용이 실제 내부 경로와 다르도록). 이러한 모든 설정은 가져오는 환경(브라우저, Node.js 등)에 따라 조건부로 적용할 수 있습니다. 자세한 내용은 제 블로그 게시물 "Node.js의 TypeScript 및 기본 ESM"을 참조하세요.package exports
의 깔끔한 기능 중 하나는 베어 import를 통해 자체 패키지를 참조할 수 있고package exports
규칙이 적용된다는 것입니다. 이는 단위 테스트에 유용합니다.imports
: 내부 모듈 및 외부 패키지에 대해#util
과 같은 약어를 정의할 수 있습니다. 자세한 내용은 제 책 "Node.js를 사용한 셸 스크립팅"의 "패키지: 소프트웨어 배포를 위한 JavaScript 단위" 장을 참조하세요.
Visual Studio Code
자동으로 생성된 import에서 로컬 import에 대한 모듈 지정자가 마음에 들지 않으면 다음 두 설정을 살펴볼 수 있습니다.
javascript.preferences.importModuleSpecifierEnding
typescript.preferences.importModuleSpecifierEnding
기본적으로 VSC는 이제 필요한 경우 파일 이름 확장자를 추가할 만큼 충분히 스마트해야 합니다.
요약
이 블로그 게시물에 대한 조사를 마친 후 현재 사용하고 있는 "기본" 설정은 다음과 같습니다.
다음 하위 섹션에서는 다양한 사용 사례에 맞게 이 설정을 조정하는 방법을 설명합니다.
{
"include": ["src/**/*", "test/**/*"],
"compilerOptions": {
// 명시적으로 지정 (소스 파일 경로에서 파생하지 않음)
"rootDir": ".",
"outDir": "dist",
//===== 출력: JavaScript =====
"target": "ES2024",
"module": "Node16", // "moduleResolution" 설정
// 빈 import된 모듈은 존재해야 함
"noUncheckedSideEffectImports": true,
//
"sourceMap": true, // .js.map 파일
//===== 상호 운용성: 외부 도구 지원 =====
// 타입 전용 import 등에 `type` 한정자를 적용하여
// .ts를 .js로 컴파일하는 도구를 지원합니다.
"verbatimModuleSyntax": true,
//===== 타입 검사 =====
"strict": true, // 여러 유용한 옵션 활성화
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
//===== 기타 옵션 =====
// JSON 파일 가져오기 허용
"resolveJsonModule": true,
}
}
'Javascript' 카테고리의 다른 글
더 이상 느린 리액트는 없다: 리액트 컴파일러 완벽 가이드 (0) | 2025.02.04 |
---|---|
React useState 업데이트 함수의 비밀: 최신 상태를 참조하는 마법 (디버깅으로 더 쉽게!) (1) | 2025.01.22 |
타입스크립트 Enum 정복: 실전 가이드 & 완벽 대안 (0) | 2025.01.21 |
자바스크립트 정규 표현식의 새로운 힘! 패턴 수정자 알아볼까요? (0) | 2025.01.13 |
pnpm 10 주요 변경 사항 정리 (0) | 2025.01.13 |