티스토리 뷰


[Typescript] tsconfig.json의 lib

1. lib 옵션의 사용

타입스크립트가 빌드 될 때 참조하는 tsconfig.json의 컴파일 옵션중에 lib이라는 항목이 있습니다. 이 항목의 의미를 알아봅시다.

// tsconfig.json

{
  "CompilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "lib": [
      "dom",
      "es5",
      "es2015.promise" 
    ]
  }
}

tsconfig.json 에 등장하는 중간에 lib의 내용을 보면 배열형태로 사용할 라이브러리들을 정의하고 있습니다. 만약 lib 항목을 정의하지 않았다면 target 항목에서 지정한 ECMAScript의 버전에 따라 기본값이 정의됩니다.

  • ES5의 기본 값: dom, es5, scripthost
  • ES6의 기본 값: dom, dom.iterable, es6, scripthost

위의 기본 값 대신에 커스텀하게 라이브러리를 쓰려고 할 때, lib을 정의하여 사용합니다.

사용 예를 하나 들겠습니다. 위 예제처럼 빌드 될 targetes5로 정의되어 있습니다. 그런데 작성한 코드나, 코드에서 참조하는 모듈들(node_modules)에서 ES6에서 등장한 Promise 를 사용하려면 es2015.promise 라는 라이브러리를 위처럼 정의하여 라이브러리 인젝션을 해줘야 합니다.

2. lib 옵션에 사용할 수 있는 값들

lib에는 사용할 수 있는 문자열은 아래와 같습니다.

  • ES5
  • ES6
  • ES2015
  • ES7
  • ES2016
  • ES2017
  • ESNext
  • DOM
  • DOM.Iterable
  • WebWorker
  • ScriptHost
  • ES2015.Core
  • ES2015.Collection
  • ES2015.Generator
  • ES2015.Iterable
  • ES2015.Promise
  • ES2015.Proxy
  • ES2015.Reflect
  • ES2015.Symbol
  • ES2015.Symbol.WellKnown
  • ES2016.Array.Include
  • ES2017.object
  • ES2017.SharedMemory
  • ES2017.TypedArrays
  • esnext.asynciterable
  • esnext.array
  • esnext.promise

아시겠지만, ES6ES2015와 동일하고 ES7ES2016 와 동일합니다. 따라서 ES6를 사용하게 되면, ES2015와 동일하며, ES2015.* 즉, ES2015의 모든 항목들을 전부 로딩합니다.

3. 실제로 참조하는 lib은 무엇일까?

코드를 분석하며 좀 더 딥하게 다이브(Deep Dive) 해봅시다.

3.1. lib 파일들은 어디에 있을까요?

보통 설치한 타입스크립트 모듈에 존재합니다. 예를 들어, npm install -g typescript 와 같이 글로벌 영역에 타입스크립트를 설치 했다면, Linux/macOS 에서는 /usr/local/lib/node_modules/typescript 에 설치가 됩니다. 죄송하지만 Windows에서는 잘 모르겠습니다. ^_^;

$ ls -l /usr/local/lib/node_modules/typescript/lib
... (중략)
lib.d.ts
lib.dom.d.ts
...
lib.es2015.d.ts
lib.es2015.promise.d.ts
...

위 처럼 아까 보셨던 문자열과 거의 동일한 이름으로 lib 파일이 정의되어 있습니다. 파일들을 열어보면 ECMAScript 규격에 정의된 자바스크립트 객체들에 대한 인터페이스들이 정의되어 있는 것을 보실 수 있습니다.

// lib.es5.d.ts

... (중략)

interface Function {
  apply(this: Function, thisArgs: any, argArray?: any): any;
  call(this: Function, thisArgs: any, ...argArray: any[]): any;
  ...
}

interface Number {
  toString(radix?: number): string;
  ...
  valueOf(): number;
}

...

위 처럼 원시 타입(Primitive Type)의 인터페이스들도 정의되어 있구요. Math 같은 자바스크립트 내장 객체들의 인터페이스도 정의되어 있습니다.

3.2. 라이브러리 로딩 과정

이제 tsc.js 의 코드를 보며 실제 컴파일 과정에서 라이브러리를 읽는 과정을 잠깐 보겠습니다.

//tsc.js

{
  name: "target",
  shortName: "t",
  type: ts.createMapFromTemplate({
    "es3": 0,
    "es5": 1,
    "es6": 2,
    "es2015": 2,
    "es2016": 3,
    "es2017": 4,
    "esnext": 5
  }),
  ...(후략)
},
{
  name: "lib",
  type: "list",
  element: {
    name: "lib",
    type: ts.createMapFromTemplate({
      "es5": "lib.es5.d.ts",
      "es6": "lib.es2015.d.ts",
      "es2015": "lib.es2015.d.ts",
      "es7": "lib.es2016.d.ts",
      ...
      "es2015.core": "lib.es2015.core.d.ts",
      "es2015.generator": "lib.es2015.generator.d.ts",
       ...(후략)
    }),
  }
  ...(후략)
}

위 코드는 tsc.js 컴파일러가 옵션을 파싱하는 부분입니다. target 옵션과 lib 옵션에 대해 위처럼 매핑되어있습니다. lib.es2015.d.ts 파일을 잠깐 열어보면요.

// lib.es2015.d.ts

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

/// <reference path="lib.es2015.core.d.ts" />
/// <reference path="lib.es2015.collection.d.ts" />
/// <reference path="lib.es2015.generator.d.ts" />
/// <reference path="lib.es2015.promise.d.ts" />
/// <reference path="lib.es2015.iterable.d.ts" />
/// <reference path="lib.es2015.proxy.d.ts" />
/// <reference path="lib.es2015.reflect.d.ts" />
/// <reference path="lib.es2015.symbol.d.ts" />
/// <reference path="lib.es2015.symbol.wellknown.d.ts" />
/// <reference path="lib.es5.d.ts" />

보시다시피 XML형식으로 다른 ts 파일들을 참조하도록 되어있습니다. 실제 구현부는 lib.es2015.core.d.ts 같은 파일들에 있습니다.

// tsc.js

function getDefaultLibFileName(options) {
  switch (options.target) {
    case 5: return "lib.esnext.full.d.ts";
    case 4: return "lib.es2017.full.d.ts";
    case 3: return "lib.es2016.full.d.ts";
    case 2: return "lib.es6.d.ts";
    default: return "lib.d.ts";
  }
}

이 함수는 서두에 말씀 드렸던 lib을 정의하지 않았을 때, target 에 대한 기본 라이브러리를 읽어오는 부분입니다. lib.es2015.d.ts 파일이 XML 의 참조 형식을 빌렸기 때문에, 실제 구현부의 집합인 lib.es6.d.ts라는 파일을 추가 정의하여 사용하는것 같습니다. lib.es6.d.ts 파일은 lib.es2015.*.d.ts 의 모든 내용을 합친것과 같습니다. 파일이름에 full 이 붙은 esnext, es2017, es2016 역시 같은 맥락입니다.

4. 결론

타입스크립트를 빌드하며 lib 이 도대체 뭘까 궁금했습니다. 그러다가 rxjs를 공부하는 와중에 target: "es5" 로 컴파일러 옵션을 주고 빌드를 하면 다음과 같은 에러가 발생하는 문제때문에 딥하게 공부해보기 시작했습니다.

node_modules/@reactivex/rxjs/dist/package/Observable.d.ts(58,60): error TS2693: 'Promise' only refers to a type, but is being used as a value here.
node_modules/@reactivex/rxjs/dist/package/Observable.d.ts(73,59): error TS2693: 'Promise' only refers to a type, but is being used as a value here.

일단은 lib: ["es5", "dom", "es2015.promise"] 혹은 lib: ["es6", "dom"] 으로 해결이 되는 문제였습니다. 어떻게 lib을 사용하는것이 효과적인지, 두 방법간에 어떤 차이가 있는지 궁금해서 시작하게 된 공부였습니다.

제 생각은 성능상에는 큰 상관 없을 것 같습니다. ^^ es2015.promise 처럼 라이브러리를 인젝션하면 모듈화를 할때 조금 더 효율적이긴 할 것 같지만 성능상에 큰 문제를 발생시킬것 같지는 않습니다. 아무래도 공부가 더 필요한 모양입니다.

문의사항이나 의견이 있으면 언제든 무엇이든 댓글 남겨주세요.

5. 참고

'Javascript&Typescript > Typescript' 카테고리의 다른 글

[Typescript] tsconfig.json의 lib  (0) 2018.02.01

댓글
댓글쓰기 폼