tsconfig 옵션 의미 (with NestJS)
제목 : Nest CLI 프로젝트 생성 후 tsconfig 옵션 의미들
NPM 전역 도구로 NestJS 프로젝트 생성 CLI 도구를 설치하고 나면,
nest 프로젝트를 진행하기 위한 준비를 깔끔하게 만들어 준다.
그런데, Udemy 강의를 듣던 중, Nest CLI 로 자동 생성되는 옵션들에 대해 의구심을 가졌다.
Nest CLI 를 사용하지 않고, 직접 프로젝트를 생성 했을 때
tsconfig.json
:
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
위의 설정 파일에서 의미하는 바는 이렇다.
module
: 빌드 후 결과물은require()
를 사용하는 Node.js 엔진 기반 결과물을 송출하겠다.target
: TypeScript 로 작성되는 문법은es2017
을 따르며, 이는 최신이다. (당연히 완전 최신은 아니다..)experimentalDecorators
: 타입스크립트에서 아직 실험적 용도(정식 사양에 반영은 x) 로 사용되는 데코레이터를 사용하겠다는 것이다.emitDecoratorMetadata
:reflect-metadata
npm 모듈과 함께, 메타데이터를 사용하여 NestJS 의 의존성과 편의성을 개선한다.
Nest CLI 를 사용하고, 프로젝트를 생성 했을 때,
$ nest g <프로젝트 폴더명>
을 통해 손쉽게 생성할 수 있다.
이후, 내부 폴더에 들어가면 tsconfig.json
에서 훨씬 더 많은 옵션들이 존재한다.
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
그렇다면, 위에서부터 천천히 하나씩 짚어가며 어떤 것을 의미하는지 알아보자.
module - CommonJS
CommonJS
: 동기적 모듈 로딩 시스템 (require
) 로 변환AMD
: 브라우저에 초점을 둔 모듈 , 비동기 모듈 로딩 시스템(require
) 로 변환UMD
: 비동기, 동기 모듈 로딩 시스템으로 나누어져 통합되지 않기에 나온 모듈 로딩 시스템.System
: 옛날에 브라우저에서 SystemJS 와 같이 사용되던 모듈. 레거시라서 요즘은 사용하지 않음.ESNext
: 출시 예정, 혹은 실험적인 "다음 버전" 을 의미한다. 비동기, 동기 모듈 시스템 지원.import
사용ES2015 / ES6 / ES2020 / ES2022
: 특정 버전을 의미. 비동기, 동기 모듈 시스템 지원.import
사용
declaration - true
true
로 설정 시, 모든 프로젝트의 모든 TypeScript 혹은 JavaScript 파일에 대해 d.ts
파일을 생성한다.
이 d.ts
파일들은, 해당 파일의 타입을 정의하여,
TypeScript 가 타입이 지정되지 않은 코드에 대해 정확한 유형을 제공할 수 있다.
연계된 옵션으로 declarationDir
를 설정하지 않으면, 같은 위치에 .d.ts
파일이 생성되고,
declarationDir : ./types
로 설정한다면, ./types/xxx.d.ts
파일이 생성된다.
removeComments - true
프로젝트의 Typescript 코드를 JavaScript 로 변환 할 때
내부의 모든 주석을 제거한다는 의미이다.
emitDecoratorMetadata - true
experimentalDecorator
와 연계되는 옵션이며,
실험적 데코레이터를 활성화 하며, 사용된 데코레이터의 메타데이터를
reflect-metadata
라는 외부 API 와 연계하여 작업하는 모듈이다.
NestJS 프로젝트에서 꼭 필요한 옵션이다. - 기본적으로 NestJS 는 reflect-metadata
를 사용하기 때문.
experimentalDecorators - true
아직 정식 사양에 들어가지 않은 데코레이터 (Ex - @Deco()
) 를 사용할 수 있게 허용한다.
이 또한 NestJS 프로젝트에서 꼭 필요한 옵션이다. - NestJS 는 데코레이터를 사용하기 때문.
allowSyntheticDefaultImports - true
우리는 어떠한 파일에서 지정한 객체, 인터페이스, 타입을 가져오기 위해
import React from "react"
와 같은 형식으로 편하게 가져온다.
그런데, 원래의 형태는 이렇다고 한다.
import * as React from "react"
예를 들어,
// 파일 이름 : getMyName.js
const getMyName = (me) => me.name;
module.exports = {
getMyName
};
// 파일 이름 : index.ts
import utils from "./getMyName";
// Error! : "xxxxxxx/getMyName" 은 기본 export 가 없습니다.
// import * as utils from "./getMyName" - 에러 해결.
const me = {
name: "Jason",
age: 200,
}
const name = utils.getMyName(me);
이 에러가 나는 이유는, module.exports
에 default
를 설정하지 않았기 때문이다.
우리가 getMyName.js
파일에서 가져오는 객체는 module.exports
이다.
만약에 * as xxx
으로 가져오지 않고 바로 전체를 가져오고자 한다면,
module.exports.default
를 설정 해 주어야 한다.
이 모듈을 사용하는 사람들을 위해 조금 공학적으로 바꾼다면 :
getMyName.js
const getMyName = (me) => me.name;
const allFunctions = {
getMyName
};
module.exports = allFunctions;
module.exports.default = allFunctions;
이렇게 한다면,
import * as utils from 'getMyName'
import utils from 'getMyName'
둘 다 에러 없이 사용 할 수 있다.
하지만, allowSyntheticDefaultImports
가 true 이더라도,
module.exports.default
가 정의되지 않는다면, 원하는 값으로 가져오지 않는다.
target - ES2021
지금의 현대적은 브라우저는 모든 ES6
모듈을 지원하고 있으므로, 그 이상이 좋은 선택이다.
하지만, 오래된 환경을 고려해서 만들어야 한다면, ES5
로 만들거나, 그것보다 더 낮은 버전으로 만든다.
만약에 ES5
거나, 더 낮은 버전이라면, () => {...}
문법을 function () { ... }
로 바꾼다.
만약에 ESNext
를 사용한다면, 현재 테스팅 혹은 실험중인 기능도 사용할 수 있게 되므로,
사용에 유의해야 한다.
이는 ESNext
를 사용중인데, 미래에 정식 사양에 들지 못한 기능을 사용했다면,
프로젝트에서 에러가 날 수도 있기 때문이다.
sourceMap - true
빌드 할 때, 해당 TypeScript 파일에 해당하는 .js
파일을 생성 할 뿐만 아니라, .js.map
파일도 생성한다.
.js.map
파일은 json
형태로 존재하는데, 내부의 정보는 원래의 TypeScript
정보를 담고 있다.
이 파일이 존재하는 이유는, 디버깅을 하기 위해서이다.
코드를 실행하다고 에러가 나면, 타입스크립트의 코드 어디에서 에러가 났는지 알려주는 것이다.
타입스크립트로 제작된 코드가 자바스크립트로 변환되면, 굉장히 난잡하게 이루어져 있다.
.js.map
파일이 존재한다면, 에러가 났을 때 타입스크립트 어디서 에러가 났는지 알려주는 것이다.
outDir - "./dist"
이 옵션은, 타입스크립트가 컴파일 되고 나서 생성 된 .js
파일을 어디에 배치시킬 건지에 대한 내용이다.
만약에 이를 설정하지 않는다면, .js
, .js.map
파일이 같은 폴더에 생성 될 것이다. (src/...
경로 내부에.)
outDir : "dist"
로 설정한다면,
컴파일 된 파일이 dist/xxx.js
로 빌드 된다.
baseUrl - "."
보통 baseUrl : "."
를 사용하며,
이는 모듈 이름들을 지정 및 확인하기 위해 기본 디렉토리를 설정하는 옵션이다.
"."
로 설정하면, tsconfig.json
와 동일한 폴더에서 시작하여 찾기 시작한다.
하지만, node_modules
(다운로드 받은 모듈) 이 우선순위를 갖는다.
incremental - true
이것을 한글로 "증분 컴파일" 이라고 한다.
컴파일 된 프로젝트 소스코드에 tsconfig.build.tsbuildinfo
가 있다.
{"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/typescript/lib/lib.es2017.d.ts","../node_modules/typescript/lib/lib.es2018.d......
몇백개의 줄이 존재하는데,
내부에는 파일의 이름들이 존재한다.
이것은 빌드 시간을 굉장히 단축시키기 위한 파일인데,
변경된 사항만 포착하여, 해당 부분만 다시 컴파일하는 방식으로 빌드 시간을 단축시킨다.
예를 들어,
$ tsc --watch
NestJS :
$ npm run start:dev
가 있다.
위의 명령어를 통해 변경 감지 모드로 실행하고,
실제로 타입스크립트의 코드를 변경 한 후 저장한다면, 실시간으로 컴파일을 해 준다.
skipLibCheck - true
npm 의 모듈은 정말 많으며, 같은 유형의 객체 혹은 타입을 중복해서 선언 할 수도 있다.
이 때문에, 외부 라이브러리를 불러오는 과정에서 동일한 타입이 중복되어 선언될 수도 있다.
외부 라이브러리의 .d.ts
파일을 엄격하게 적용할 경우,
같은 타입이 중복되어 선언되어 에러가 날 수도 있다는 것이다.
타입 체킹을 엄격하게 하지 않는다고 참조가 불가능 한 것은 아니다.
strictNullChecks - false
NestJS 프로젝트에서는 false
로 설정되어 있고,
이 옵션은 null
, undefined
를 신경쓰냐, 마냐의 문제이다.
true
라면, 타입 체킹 과정에서,
타입이 정해져 있는 장소에 null
과 undefined
를 할당하지 못한다.
false
라면, 그러한 에러를 내지 않고 할당될 수 있는데,
이는 런타임 시 에러를 야기할 수 있다고 한다.
noImplicitAny - false
간단한 옵션으로, any
타입을 사용하지 못하도록 만드는 것이다.
모든 변수 혹은 객체가 타입을 가져야만 한다.
strictBindCallApply - true
call
, bind
, apply
와 같이 내장되어 있는 함수에,
TypeScript 의 타입 체킹 시스템을 제공한다.
forceConsistentCasingInFileNames - false
파일을 참조 할 때, 대문자 소문자를 구별할 것이냐는 것이다.
true
라면, file.ts
와 File.ts
는 다르다.
false
라면, file.ts
와 File.ts
는 같다.
noFallthroughCasesInSwitch - false
switch-case
문법에서의 오류를 보고하지 않는다는 의미이다.
이 구문에서 구현된 외부 모듈들이 에러가 많은가 보다.
참고 사이트
타입스크립트 공식 사이트의 모듈에 관하여
https://www.typescriptlang.org/tsconfig/#module
토스 테크 - CommonJS 와 ESM 에 모두 대응하는 라이브러리 개발하기
https://toss.tech/article/commonjs-esm-exports-field
Beomy Github 블로그
https://beomy.github.io/tech/javascript/cjs-amd-umd-esm/
devts Github 블로그
안동민 개발노트 블로그
https://andongmin.com/docs/typescript/ch9/ch9-4#google_vignette