본문 바로가기
스터디

[Typescript] 모듈 해석

by rious275 2023. 7. 9.

상대적 vs. 비-상대적 모듈 import

모듈 해석(module resolution)은 컴파일러가 import가 무엇을 참조하는지 알아내기 위해 사용하는 프로세스이며, 모듈 참조가 상대(상대 경로)적 혹은 `비-상대적(절대 경로)이냐에 따라 모듈 import는 다르게 해석된다.

상대 경로

상대적 import는 가져온 파일에 상대적으로 해석되고 ambient 모듈 선언으로 해석 될 수 없다.

import Entry from './components/Entry';
import { DefaultHeaders } from '../constants/http';
절대 경로

비-상대적 import는 baseUrl로 해석되거나, 밑에서 다루게 될 경로 매핑으로 해석될 수 있고 ambient 모듈 선언으로도 해석될 수 있다. 외부 의존성을 불러올 때 할 때 주로 사용한다.

import * as $ from 'jquery';
import { Component } from '@angular/core';

모듈 해석 전략

클래식 (Classic)

TypeScript의 디폴트 해석 전략으로 사용되며, 주로 이전 버전과의 호환성을 위해 제공된다. 그리고 상대적 import는 import하는 파일에 상대적으로 해석된다.

// /root/src/folder/hello.ts
import { a } from './utils';

위 import는 아래와 같이 해석할 수 있다.

  1. /root/src/folder/utils.ts
  2. /root/src/folder/utils.d.ts

러나 `비-상대적 모듈 import에서는, 컴파일러가 가지고 온 파일을 갖고 있는 디렉토리부터 시작해 모든 위치를 찾는다.

// 위 예제와 import의 경로만 다르게 설정
import { a } from 'utils';

위 import는 아래와 같이 해석할 수 있다.

  1. /root/src/folder/utils.ts
  2. /root/src/folder/utils.d.ts
  3. /root/src/utils.ts
  4. /root/src/utils.d.ts
  5. /root/utils.ts
  6. /root/utils.d.ts
  7. /utils.ts
  8. /utils.d.ts

노드 (Node)

Node.js가 모듈을 해석하는 방법

통적으로 Node.js의 import는 require 함수를 호출해 수행하며, require상대적 경로 혹은 `비-상대적 경로가 주어지는지에 따라 달라진다.

// /root/src/moduleA.js
var x = require('./moduleB');

Node.js는 위 예제를 아래와 같이 해석한다.

  1. /root/src/moduleB.js라는 파일이 존재하는지 확인.
  2. 만약 main 모듈을 지정하는 코드({ "main": "lib/mainModule.js" })를 포함하는 package.json이 있다면, /root/src/moduleB 폴더 확인.
  3. index.js 파일을 포함하고 있으면, /root/src/moduleB 확인하기. index.js 파일은 폴더의 main 모듈임을 암시적으로 나타낸다.

와 다르게 비-상대적 모듈 해석은 다르게 수행하는데, 로드하려는 모듈을 찾을ㄷ 때까지 각각의node-modules` 폴더에서 모듈을 찾는다.

// /root/src/moduleA.js
var x = require('moduleB');

위 코드에서 Node는 아래와 같이 일치하는 모듈을 모든 경로에서 찾을 떄까지 해석한다.

  1. /root/src/node_modules/moduleB.js
  2. /root/src/node_modules/moduleB/package.json ("main" 설정을 했을 경우)
  3. /root/src/node_modules/moduleB/index.js
  4. /root/node_modules/moduleB.js
  5. /root/node_modules/moduleB/package.json ("main" 설정을 했을 경우)
  6. /root/node_modules/moduleB/index.js
  7. /node_modules/moduleB.js
  8. /node_modules/moduleB/package.json ("main" 설정을 했을 경우)
  9. /node_modules/moduleB/index.js
TypeScript가 모듈을 해석하는 방법

TypeScript는 Node.js의 런타임 해석 전략을 모방한다.

TypeScript는 소스 파일 확장자 .ts, .tsx, .d.ts 를 Node의 해석 로직 위에 씌운다. main의 목적-컴파일러가 정의 파일을 찾기 위해 package.json안에 types 항목을 사용한다.

// /root/src/moduleA.ts
import { b } from './moduleB';

위 코드를 아래와 같은 과정으로 해석한다.

  1. /root/src/moduleB.ts
  2. /root/src/moduleB.tsx
  3. /root/src/moduleB.d.ts
  4. /root/src/moduleB/package.json ("types" 설정을 했을 경우)
  5. /root/src/moduleB/index.ts
  6. /root/src/moduleB/index.tsx
  7. /root/src/moduleB/index.d.ts

상대적인 경로와는 다르게 비-상대적 importNode.js와 마찬가지로 모듈을 찾을 때까지 모든 경로를 탐색한다. (해석 내용은 Node.js 원리와 동일하므로 생략)

기본 URL (Base URL)

baseUrl 설정은 컴파일러에게 어디에서 모듈을 찾을지 알려주며, 값은 아래 중 하나로 결정된다.

  1. baseUrl 명령줄 인수 값 (주어진 경로가 상대적이면, 현재 디렉터리를 기준으로 계산)
  2. tsconfig.json 안의 baseUrl 프로퍼티 값 (주어진 경로가 상대적이면, tsconfig.json의 위치를 기준으로 계산)

참고로, 상대적 모듈 import는 항상 가져온 파일의 상대적으로 해석되기 때문 baseUrl 설정에 영향을 받지 않는다.

경로 매핑 (Path mapping)

가끔 모듈이 baseUrl 아래에 위치하지 않는 경우가 있는데, 그럴 때는 아래와 같이 paths 설정으로 경로를 매핑한다.

// jquery 모듈 매핑
{
  "compilerOptions": {
   // paths가 있는 경우 반드시 지정
    "baseUrl": ".",
    "paths": {
       // 해당 매핑은 baseUrl에 상대적이다.
      "jquery": ["node_modules/jquery/dist/jquery"]
    }
  }
}

여러 위치에서 찾을 떄는 아래와 같이 설정한다.

"paths": {
   "*": [
      "*",
      "generated/*"
   ]
}

제외 목록에 있는 모듈을 컴파일러가 선택하는 경우가 있다. exclude, files 엔트리를 지정하지 않으면 tsconfig.json를 포함하는 폴더 안의 모든 파일과 모든 하위-디렉터리가 컴파일에 포함되며,

컴파일에서 제외하고 싶은 항목이 있다면 exclude 설정을 사용하고 찾게 하는 대신 그 모든 파일을 지정하고 싶다면 files 설정을 사용한다.

'스터디' 카테고리의 다른 글

[Typescript] 네임스페이스와 모듈  (0) 2023.07.14
[Typescript] 네임스페이스 (namespace)  (0) 2023.07.14
[Typescript] 모듈 (module)  (0) 2023.07.07
[Typescript] 유틸리티 타입  (1) 2023.06.19
[Typescript] 제네릭 (Generics)  (0) 2023.06.12