Programming language/Typescript

TypeScript, tsconfig.json 주요 설정

iKay 2021. 1. 7. 03:49
반응형

서론

TypeScript를 더 잘 사용하기 위해 컴파일 하기 위한 설정, 동작방식을 정의하는 tsconfig를 어느 정도 이해할 필요가 있다고 생각한다. Node.js 프로젝트에 TypeScript를 설정하기 위해서 공식문서를 참고하는 것이 가장 정확하겠지만 정보의 양이 방대해서 주요한 몇몇 설정만 정리해 보고자 한다. 앞으로 지식과 경험이 더 넓어지면 내용을 계속 덧붙여 나갈 계획이다. 자세한 설명은 당연히 공식문서를 참조하는 것이 좋겠다.

 

tsconfig.json을 작성하는 것은 옳고 그름의 문제가 아니다. 프로젝트 마다, 팀마다 각각 상황에 맞게 설정하면 되는 것이기 때문에 무조건 따라야 하는 설정은 없다. 잘 모르겠으면 권장하는대로, 기본값대로 사용해도 될 것이다. 다만, 각 프로젝트 마다 성격에 맞게 사용하기 위해, "어떤 설정이 있었고, 이 설정이 무엇을 뜻했던 것 같다." 정도는 알고 있는 것이 좋을 것 같아서 정리해 보고자 한다. 만약 TypeScript 설정에 당장은 관심이 없고 잘은 모르겠지만 TypeScript를 사용해야 하는 상황이라면 공식 문서에서 추천해주는 대로 extends 해서 일단 사용하는 것도 큰 노력을 사용하지 않는 좋은 방법일 것이다.

 

TypeScript가 컴파일 될 때, 보통 TypeScript 프로젝트의 root에 tsconfig.json 파일이 위치하는데, compile하기 위한 소스코드의 위치, compile 옵션을 이곳에 json 파일로 나타내면 된다. 만약, tsc 명령어로 compile 하는데 input file이 주어지지 않으면 현재 디렉토리에서 부터 부모 디렉토리 체인으로 거슬러 올라가면서 tsconfig.json 파일을 찾는다. 반대로, tsc 명령어에 input file이 주어지면 tsconfig.json 파일을 검색하지 않는다.

 

설정들에 대한 설명들을 레퍼런스 문서에서 나누고 있는 큰 범주대로 나누었다. 공식문서에는 알파벳 순으로 설정들을 나열하고 있지만 여기서는 개인적으로 중요하다고 생각되는 것을들 먼저 나열할 수도 있다.  

 

1. File Inclusion

TypeScript가 compile에 포함하거나 제거할 파일들을 명시하고, compile 하기 위해 참조해야하는 설정이 있는 파일들을 설정한다.

include, exclude

컴파일 할때 포함하거나 제외할 filenames 배열이나 패턴을 명시한다. 패턴을 명시할 때 glob pattern을 사용하고, filenames의 path는 tsconfig.json 이 있는 곳으로 부터 상대경로에 있는 것들이다.

주의할 점은 exclude는 include와 함께 동작하면서 include 될 것을 exclude 시키는 것이지, code에 있는 것을 제외시키는 것은 아니다.

 

include는 defalut로 다음과 같은 값을 갖는다.

["**/*"]

exclude는 default로 다음과 같은 값을 갖는다.

// 만약 outDir이 명시되어 있으면 그것도 default에 포함된다.
["node_modules", "bower_components", "jspm_packages"]

{
  "include": ["src/**/*", "tests/**/*"],
  "exclude": ["tests/modules/*"]
}

extends

상속받을 다른 base file을 명시한다. 설정들은 base file이 우선 load 되고, 상속하는 file이 override 하게 동작한다. 팀 내에서 이런식으로 tsconfig.json 파일을 npm으로 관리해도 좋을 것 같다.

files

include 시킬 파일들을 개별적으로 포함시킨다. 보통의 경우 include만으로 해결될 것이기 때문에 아마 잘 사용되지 않을 것 같다.

 

{
  "compilerOptions": {},
  "files": [
    "core.ts",
    "sys.ts",
    "types.ts",
    "scanner.ts",
    "parser.ts",
    "utilities.ts",
    "binder.ts",
    "checker.ts",
    "tsc.ts"
  ]
}

2. Compiler Options

Project Options

TypeScript가 compile 되는 방식, 동작하는 방식을 설정한다.

 

사용할 수 있는 많은 옵션들이 있다. 중요하다고 생각되는 것들, 자주 사용될 것 같은 것 위주로 정리해 보고자 한다. 

 

lib

lib는 target과 함께 사용되기도 해서 target과 함께 구분해가면서 봐두면 좋다고 생각한다.

 

lib는 타입스크립트는 built-in JS API(예, Math)와 브라우저 환경(예, document) 등에서 사용할 수 있는 타입을 기본적으로 제공한다. JS 버전마다 사용할 수 있는 문법이나 API 등이 조금씩 다른다. 보통 계속 추가되면서 하위호환을 유지하는 편이다. 이런 것들 중 target에 설정한 버전에 부합하는 API 등의 타입을 사용할 수 있게 해준다.

 

예를 들어,

  • target에 ES6라고 설정한 경우, ES6+부터 Map이라는 것을 JS에서 네이티브하게 사용할 수 있기 때문에 Map을 사용하기 위한 타입을 제공한다.
  • 백엔드 개발을 하는 경우 브라우저 환경의 개발환경을 설정할 필요가 없다. 그래서 이런 경우, "dom" 타입을 설정할 필요가 없다.
  •  polyfill을 사용하거나, nodeJS가 native 적으로 ECMAScript 버전을 일부 지원하는 경우 target 버전과 완전히 동일하지 않아도 liib로 선택적으로 버전을 올려서 사용할 수 있다.

타입이 어떻게 설정될까 궁금해서 조금 찾아보다가, TypeScript의 lib에 대한 소스코드에서 동작하는 방식을 추론할 수 있었는데, 소스코드에서 볼 수 있듯이 상위버전은 하위 버전을 포함해서 사용할 수 있게 되고, lib.esXXXX.d.ts는 ECMAScript의 XXXX 버전에 해당하는 모든 것을 포함한다는 것도 볼 수 있다. 

// 예, lib.es2019.d.ts 파일의 소스코드

/// <reference no-default-lib="true"/>


/// <reference lib="es2018" />
/// <reference lib="es2019.array" />
/// <reference lib="es2019.object" />
/// <reference lib="es2019.string" />
/// <reference lib="es2019.symbol" />

 

Node.JS 가 지원하는 ES 버전에 맞는 맞는 lib 설정을 할 필요가 있을 것이다. 그렇다면 Node.JS가 어떤 버전의 ES를 지원하는지 잘 알아야 한다. 그런 곳을 정리해 놓은 곳이 있는데, 이것을 보고 lib에 ES 버전을 적절히 올리거나 필요한 것만 추가해서 사용하면 될 것 같다. 예를 들어, Node12 버전을 사용하는 경우, ES2019는 100% 지원하고 ES2020은 BigInt, Promose.allSettled 등을 일부 지원하므로 이런 것을 추가해서 사용하면 될 것 같다.

 

default는 ES3이고, 현대 JavaScript 브라우저와 애플리케이션은 보통은 ES6+에서 동작하므로 ES6(ES2015)로 설정하기를 개인적으로 권장한다. 만약 target을 설정했는데 lib를 생략했다면, lib는 target과 같은 버전으로 변경되어 동작하게 된다고 공식문서에서 설명하고 있다. 다음은 공식문서의 target 부분에서 인용한 것이다. 이 부분 때문이라도 lib를 target과 함께 봐두면 좋은 것 같다.

Changing target also changes the default value of lib. You may “mix and match” target and lib settings as desired, but you could just set target for convenience.

 

target

target은 TypeScript가 컴파일 되는 결과물인 JavaScript 코드로 변환되는 버전을 결정한다. 즉, 위의 lib 설정에서 밝힌 것 처럼, 만약 백엔드 Node.JS를 운영할 것이라면, 운영하는 Node.JS가 지원하는 버전의 ES 버전에 맞게 target을 지정하면 될 것이다. 프론트엔드라면 개발하려는 브라우져 버전에 맞게 target을 설정하면 될 것 같다.   

 

allowJs

TypeScript는 기본적으로 JavaScript 위에서 동작하는 언어이고, JavaScript를 TypeScript로 점차적으로 변환시켜서 사용할 수도 있다. 이 점으로 볼 때, .ts 파일에서 .js 파일도 import 있게 할 수 있어야 하는 경우도 있을 것 같다. 그래서 allowJS는 .ts, .tsx 파일에서 .js 파일을 import 할 수 있게 해준다. 

 

기본적으로 false 이지만, JavaScript 프로젝트를 점차 TypeScript로 변환시키는 경우 true로 설정하는 것이 필요할 것이다.

 

removeComments

TypeScript 코드를 JavaScript로 컴파일 시킬 때, 주석(comments)을 모두 제거 시키는 옵션이다. 

 

기본적으로 false 이지만, 번들 사이드 등을 줄이기 위해 소스코드에서 주석을 제거하고 싶다면 true로 하면 된다. 

 

sourceMap

sourcemap fiile(xxx.js.map)을 생성할 것인지 유무를 설정하는 것이다. 이 file은 JavaScript와 함께 생성되면, debugger나 다른 툴이 본래의 TypeScript 소드 코드를 볼 수 있게 해주도록 한다. hello.ts를 컴파일 하고 sourceMap을 true로 하면 hello.js와 함께 hello.js.map 파일이 생성되는 것을 볼 수 있을 것이다.

 

기본값은 false인데, 디버깅이 필요한 개발환경에서는 true로 하고, 배포 사이즈를 줄여야 하는 배포환경에서는 false 해, 서로 다른 환경에서 적절하게 컴파일 되도록 설정하면 될 것 같다.  

 

Strict Checks

Type을 얼마나 엄격하게 검사할지를 검사하는 옵션이라고 보면 될 것 같다. strict 에는 8가지 옵션이 있다. 

 

1) strict

2) alwaysStrict

3) noImplicityAny

4) noImplycityThis

5) strictBindCallApply

6) strictFunctionTypes

7) strictNullChecks

8) strictPropertyInitialization

 

위에서도 밝혔지만, 여기서는 각 옵션이 어떤 것인지를 간단히만 밝히고 있다. 더 자세한 설명은 공식문서를 보는 것이 더 잘 이해가 될 것이고 정확하다. 

1) strict

만약 이 모든 옵션을 모두 사용하려면 단지, { "strict": true } 로 설정하면 될 것 같다. 하지만, JavaScript에서 TypeScript로 옮기는 경우에는 바로 strict 를 적용할 수 없으니 여러 옵션들 중 선택해서 사용해야 하는 경우도 있을 것이다. 그래서 나머지 strict 특징들을 알아두었다가 필요할 때 적절히 사용하는 것도 좋을 것 같다. 또한 나머지 각각의 특징을 이해하는 것이 strict를 이해하는 것이기도 할 것 같다. 그리고 strict: true로 설정한 후 TypsScript 버전을 올렸을 때 추가되는 속성이 있다면 그 시점에 타입에러가 발생할 수도 있을 것이다.  

 

default는 false 이지만 true가 권장된다.

2) alwaysStrict

strict과 비슷해서 헷갈릴 수 있을 것 같다. strict는 위에서 설명한대로 모든 옵션을 사용하겠다라는 것이지만 alwaysStrict는 ECMAScript strict 모드를 사용하겠다는 것이다. 

 

default는 false 이지만 true가 권장된다.

 

3) noImplicityAny

noImplicityAny설정을 끄고, argument 등의 type을 명시하지 않으면 arguemnt는 any로 type이 추론된다. 하지만 설정을 켜면 TypeScript는 이렇게 any로 추론되는 것을 에러로 간주하게 된다. 

 

default는 false 이지만 true가 권장된다.

 

4) noImplycityThis

this의 contex가 any를 추론하게 된다면 현재 객체의 context가 아니라는 것을 뜻하게 되어서 이 경우 에러를 발생시킨다. 

 

default는 false 이지만 true가 권장된다.

 

5) strictBindCallApply

TypeScript가 call, bind 그리고 apply 를 사용할 때, 함수, 메소드의 argument의 타입을 추론하여 사용할 수 있게 한다. 옵션이 켜져잇을 경우 잘못된 타입의 argument를 넘기면 에러가 발생한다. 

 

default는 false 이지만 true가 권장된다.

 

6) strictFunctionTypes

옵션을 켜면 함수, 메소드의 argument의 타입을 더 정확히 추론하여 사용할 수 있게 해준다. 

 

default는 false 이지만 true가 권장된다.

 

7) strictNullChecks

strictNullChecks 옵션이 꺼져 있다면, null 또는 undefined 체킹이 무시되는데 이것은 런타임 에러를 발생시킬 가능성이 크다. 컴파일 시간에 이런 에러를 잡지 못하면 보통 오타, 값의 미대입 등으로 존재하지 않는 개체의 속성, 값이 없는 속성에 접근하는 경우가 많이 발생하게 되어 런타임 에러오 이어지는 것이다.

 

default는 false 이지만 true가 권장된다.

 

8) strictPropertyInitialization

strictPropertyInitialization 옵션을 켜두면 초기화 되지 않는 클래스의 멈버 변수에 대해 에러를 발생시킨다. 초기화 시키는 방법으로는 속성에 기본값을 직접 대입하거나 생성자에서 대입하는 두 가지 방법이 있는데, 이런식으로 속성을 초기화 하지 않으면 undefined가 되어서 undefined도 될 수 있다는 타입을 명시할 필요가 있다.

 

 

Module Resolution

모듈의 path를 resolve 하는 설정이다. 

baseurl

TypeScript가 compile할 때 소스코드의 가장 상위 base directory, root directory를 설정하는 것이다. 다른 파일에서 import 등을 하여 경로를 명시해야 할 때, 상대경로를 사용하지 않아도 baseurl이 명시된 곳을 기준으로 절대경로처럼 시작점을 명시해주는 것이다. 실제로 path를 resolve 할 때에도 상대경로가 아니라면 baseurl 부터 찾는다.

paths

모듈 import시 반복되는 path를 사용하거나 깊이가 길어지면 path 가 길어진다. path에 별명을 줘서 map 시켜서 간단하게 사용할 수 있게 해준다. angular를 해보았다면 "@app" 이라던가, "@config" 등의 path를 사용해 본적이 있을 텐데 이것이 그런 설정이다.   

 

Expetrimental

JavaScript에 확실히 추가된 기능은 아닐지라도 실험적으로 사용할 수 있는 것들을 TypeScript도 실험적으로 지원하는 기능들이다. 이런 것들은 버전이 바뀌면서 없어질 수도 있는 안정적인 기능은 아니니 남용해서는 안될 것 같다.

 

experimentalDecorator

현재 decorator 라는 것은 TC39 standard 의 stage 2에 있는데, TypeScript에서 이 decorator를 실험적으로 사용할 수 있게 해준다. 참고로, decorator는 class, method, member variable, argument 등에 사용될 수 있고, decorator를 정의할 때, tartget, propertyKey, descriptor를 가진다. 흡사 Java 기반 프레임워크인 Spring의 annonation과 모습과 기능이 비슷해 보인다. 

emitDecoratorMetadata

reflect-metadata 모듈과 함께 사용되는 decorator를 위한 metadata의 타입을 추론하게 해준다. 

 

기본값으로 false 이다.  

 

Advanced

굳이 번역하자면 "기타"정도의 범주에 속하는 것 같다. 컴파일 하거나 기능 동작에 있어서 크게 중요한 부분을 차지 하지 않는 것 같지만 그래도 몇몇 옵션들은 유용한 것 같으니 알아두면 좋을 것 같다. 

noEmitOnError

noEmitOnError을 true로 설정하면, 에러발생시 JavaScript 소드코드, source-maps, declaration이 dist 디렉토리에 생기지 않는다.

 

기본값으로 false 이지만 사소하더도 error가 있다는 코드가 빌드되는 것을 원치 않는다면 true로 설정하는 것이 좋을 것 같다.  

 

skipLibCheck

사용하는 라이브러리의 파일의 타입 검사를 skip 하게 해, type 검사 정확도를 조금 희생할지라도 컴파일 시간을 줄여준다. 

 

기본값은 false 인데, true로 하는 것이 권장된다. 아마 보통의 경우 라이브러리의 타입을 검사할 필요가 없기 때문인 것 같다. 

 

resolveJsonModule

.json으로 된 파일의 모듈을 import 할 수 있께 해준다. 

 

기본값은 false 인데, 프로젝트에서 설정 파일 등을 .json 파일을 모듈로써 import 해서 사용할 일이 있다면 true로 해주면 될 것 같다. 

 

noErrorTruncation

에러 메시지가 잘리지 않게 한다.

 

기본값으로 false이다. 

 

결론

현재 실제 프로덕트로 Node.JS로 백엔드를 개발하고 있다. 최근에 TypeScript의 버전을 4.0으로 올리고, NodeJs 버전을 12로 올리면서 이에 적절한 tsconfig.json으로 설정할 필요가 있었다. 이번 tsconfig.json 설정 작업을 위해 조사하는 과정에서 tsconfig 설정만이 아니라, TypeScript에 대해 조금 이해도가 높아진 계기가 된 것 같다.

 

아직 다루지 않았지만 중요한 설정이 있을 수 있으니 그 때 마다 돌아와서 이 글을 수정할 예정이다. 

반응형