[tsconfig의 λͺ¨λ“  것] Compiler options / Modules

    [tsconfig의 λͺ¨λ“  것] Compiler options / Modules


    이번 ν¬μŠ€νŒ…μ—μ„œλŠ” μ§€λ‚œ [tsconfig의 λͺ¨λ“  것] Compiler options / Type Checking ν¬μŠ€νŒ…μ— 이어 tsconfig의 컴파일 μ˜΅μ…˜ 쀑 λͺ¨λ“ˆκ³Ό κ΄€λ ¨λœ μ˜΅μ…˜λ“€μ— λŒ€ν•œ 이야기λ₯Ό 해보렀고 ν•œλ‹€.

    이 μ˜΅μ…˜λ“€μ€ νƒ€μž…μŠ€ν¬λ¦½νŠΈλ₯Ό μ»΄νŒŒμΌν•  λ•Œ λͺ¨λ“ˆλ“€μ΄ μ–΄λ–€ λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ λ”°λ₯΄λ„둝 ν•  것인지, μ–΄λ–€ κ²½λ‘œμ— μžˆλŠ” νŒŒμΌλ“€μ„ 컴파일 ν•  것인지, λΉŒλ“œλœ μžλ°”μŠ€ν¬λ¦½νŠΈ νŒŒμΌλ“€μ΄ μ–΄λ–€ λͺ¨λ“ˆ 방식을 λ”°λ₯΄κ²Œ ν•  것인지 등을 컨트둀 ν•  수 μžˆλŠ” 것듀인데, 일반적인 μ„œλΉ„μŠ€λ₯Ό λ§Œλ“œλŠ” κ²½μš°λ³΄λ‹€λŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈλ‘œ μž‘μ„±λœ 라이브러리λ₯Ό λ§Œλ“€ λ•Œ 자주 λ‹€λ£¨κ²Œ λ˜λŠ” μ˜΅μ…˜λ“€μ΄κΈ°λ„ ν•˜λ‹€.

    allowUmdGlobalAccess

    κ°’ μ„€λͺ…
    true umd λͺ¨λ“ˆλ‘œμ˜ 접근을 ν—ˆμš©ν•œλ‹€
    false (default) umd λͺ¨λ“ˆλ‘œμ˜ 접근을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ”λ‹€

    allowUmdGlobalAccess μ˜΅μ…˜μ€ νƒ€μž…μŠ€ν¬λ¦½νŠΈ λͺ¨λ“ˆμ΄ μ „μ—­ 객체에 λͺ¨λ“ˆμ„ ν¬ν•¨μ‹œμΌœ λ‚΄λ³΄λ‚΄λŠ” UMD(Universal Module Definition)ν˜•νƒœμ˜ λͺ¨λ“ˆμ— 접근이 κ°€λŠ₯ν•˜κ²Œ ν•  것인지λ₯Ό 컨트둀 ν•˜λŠ” μ˜΅μ…˜μ΄λ‹€.

    λ§Œμ•½ 이 μ˜΅μ…˜μ΄ κΊΌμ Έ μžˆλ‹€λ©΄ jQuery의 $와 같은 μ „μ—­ λ³€μˆ˜μ— κ·Έλƒ₯ μ ‘κ·Όν•˜λŠ” 것이 λΆˆκ°€λŠ₯해지고, 무쑰건 import 문을 ν†΅ν•΄μ„œ λͺ¨λ“ˆμ„ 가져와야 ν•œλ‹€.

    UMD 방식을 μ‚¬μš©ν•˜μ—¬ λ§Œλ“€μ–΄μ§„ λΌμ΄λΈŒλŸ¬λ¦¬λ“€μ€ 보톡 이런 ν˜•νƒœμ˜ κΈ€λ‘œλ²Œ νƒ€μž… 선언을 가지고 μžˆλ‹€.

    export const doThing1: () => number;
    export const version: string;
    
    export interface AnInterface {
      foo: string;
    }
    
    export as namespace myLibrary;

    μ΄λ ‡κ²Œ νƒ€μž… 파일이 λ„€μž„μŠ€νŽ˜μ΄μŠ€λ₯Ό export ν•˜λ„λ‘ μ„ μ–Έλ˜μ–΄ μžˆλŠ” 경우, νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 이 λͺ¨λ“ˆμ΄ μ•”λ¬΅μ μœΌλ‘œ μ „μ—­ λ³€μˆ˜μΈ myLibrary에 ν• λ‹Ήλœλ‹€κ³  νŒλ‹¨ν•œλ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— 이런 νƒ€μž… 선언을 λ‚΄ μ†ŒμŠ€μ— ν¬ν•¨μ‹œν‚€λ©΄ myLibraryλΌλŠ” λ„€μž„μŠ€νŽ˜μ΄μŠ€λ₯Ό 톡해 이 λͺ¨λ“ˆμ˜ λ‚΄μš©λ¬Όμ— μ ‘κ·Όν•  μˆ˜κ°€ μžˆλŠ” 것이닀.

    λ§Œμ•½ allowUmdGlobalAccess μ˜΅μ…˜μ΄ κΊΌμ ΈμžˆλŠ” μƒνƒœλΌλ©΄ 이런 μ—λŸ¬λ₯Ό λ§Œλ‚˜κ²Œ λœλ‹€.

    const b = myLibrary.doThing1();
    // 'myLibrary' refers to a UMD global, but the current file is a module. Consider adding an import instead.ts(2686)
    
    export {}

    μ΄λ•Œ allowUmdGlobalAccess μ˜΅μ…˜μ„ 켜게 되면 λ„€μž„μŠ€νŽ˜μ΄μŠ€λ‘œ export된 UMD λͺ¨λ“ˆμ— μ ‘κ·Όν•˜λŠ” 것이 ν—ˆμš©λœλ‹€. ν•˜μ§€λ§Œ ꡳ이 μ•”λ¬΅μ μœΌλ‘œ μ „μ—­ λ³€μˆ˜μ— μ„ μ–Έλ˜μ–΄μžˆλŠ” λͺ¨λ“ˆμ— μ ‘κ·Όν•΄μ„œ μ‚¬μš©ν•˜λŠ” 것보닀 import ν‚€μ›Œλ“œλ‘œ λͺ…μ‹œμ μœΌλ‘œ λͺ¨λ“ˆμ„ κ°€μ Έμ™€μ„œ μ‚¬μš©ν•˜λŠ” 것이 더 μ•ˆμ „ν•˜λ‹ˆ, ν”ΌμΉ˜ λͺ»ν•˜λŠ” κ²½μš°κ°€ μ•„λ‹ˆλΌλ©΄ 가급적 ν•΄λ‹Ή μ˜΅μ…˜μ„ μΌœλ‘λ„λ‘ ν•˜μž.

    baseUrl

    baseUrl μ˜΅μ…˜μ€ μƒλŒ€ 경둜둜 λͺ¨λ“ˆμ˜ 경둜λ₯Ό 지정할 λ•Œ 기쀀이 λ˜λŠ” μœ„μΉ˜λ₯Ό 지정할 수 μžˆλŠ” μ˜΅μ…˜μ΄λ‹€.

    myProject
    β”œβ”€β”€ index.ts
    β”œβ”€β”€ utils
    β”‚   └── foo.ts
    └── tsconfig.json

    예λ₯Ό λ“€μ–΄ μœ„μ™€ 같은 상황일 λ•Œ, index.tsμ—μ„œ μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•˜μ—¬ foo.ts λͺ¨λ“ˆμ„ κ°€μ Έμ˜€λ €λ©΄ ./utils/foo와 같이 ν˜„μž¬ 기쀀이 λ˜λŠ” μœ„μΉ˜λ₯Ό ./ 처럼 μ§€μ •ν•œ ν›„ ν•΄λ‹Ή λͺ¨λ“ˆμ— μ ‘κ·Όν•˜κ²Œ λœλ‹€.

    ν•˜μ§€λ§Œ 잘 생각해보면 아무리 μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•œλ‹€κ³  해도, 기쀀이 λ˜λŠ” μœ„μΉ˜ μžμ²΄κ°€ λ³€ν•˜λŠ” κ²½μš°λŠ” λ§Žμ§€ μ•Šλ‹€λŠ” 것을 μ•Œ 수 μžˆλ‹€.

    myProject
    β”œβ”€β”€ index.ts
    β”œβ”€β”€ utils
    β”‚   └── foo.ts
    β”œβ”€β”€ remotes
    β”‚   └── bar.ts
    └── tsconfig.json

    λ§Œμ•½ 이런 ꡬ쑰의 ν”„λ‘œμ νŠΈμ˜ remotes/bar λͺ¨λ“ˆμ—μ„œ utils/foo λͺ¨λ“ˆμ— μ ‘κ·Όν•˜λ €κ³  ν•œλ‹€λ©΄ κ²°κ΅­ ../utils/foo 처럼 ν”„λ‘œμ νŠΈμ˜ λ£¨νŠΈκΉŒμ§€ μ˜¬λΌκ°„ ν›„ λ‹€μ‹œ λ‚΄λ €μ˜€λŠ” λ°©μ‹μœΌλ‘œ 접근을 ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

    κ·Έλž˜μ„œ baseUrl μ˜΅μ…˜μ€ μƒλŒ€κ²½λ‘œλ₯Ό μ‚¬μš©ν•  λ•Œλ§ˆλ‹€ λ°˜λ³΅λ˜λŠ” β€œλ£¨νŠΈλ‘œμ˜ 여정”을 μ—†μ• μ£ΌλŠ” 역할을 ν•œλ‹€.

    {
      "include": ["src/*"],
      "compilerOptions": {
        "baseUrl": "./"
      }
    }

    μ΄λ•Œ baseUrl에 μž…λ ₯ν•˜λŠ” μƒλŒ€ 경둜의 기쀀은 tsconfigκ°€ μœ„μΉ˜ν•˜λŠ” 곳이기 λ•Œλ¬Έμ— μΌλ°˜μ μœΌλ‘œλŠ” ν”„λ‘œμ νŠΈμ˜ λ£¨νŠΈκ°€ λœλ‹€. 즉, μ΄λ ‡κ²Œ μ„€μ •ν•œ ν›„ μš°λ¦¬κ°€ λͺ¨λ“ˆμ— μ ‘κ·Όν•˜κΈ° μœ„ν•΄ μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•˜κ²Œ 되면, νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” baseUrl에 μž…λ ₯된 루트의 μœ„μΉ˜μ—μ„œλΆ€ν„° ν•΄λ‹Ή λͺ¨λ“ˆμ„ μ°Ύμ•„κ°€κ²Œ λ˜λŠ” 것이닀.

    import { foo } from 'utils/foo'; // 사싀 .(루트)/utils/foo
    import { bar } from 'remotes/bar'; // 사싀 .(루트)/remotes/bar

    μ΄λ ‡κ²Œ baseUrl μ˜΅μ…˜μ„ μ‚¬μš©ν•˜λ©΄ 맀번 ./λ‚˜ ../ 등을 μ‚¬μš©ν•˜μ—¬ 루트λ₯Ό λ¨Όμ € 찾은 ν›„ λͺ¨λ“ˆμ— μ ‘κ·Όν•˜λŠ” μƒλŒ€ 경둜λ₯Ό μ ˆλŒ€ 경둜처럼 μ‚¬μš©ν•  수 μžˆλ‹€.

    paths

    paths μ˜΅μ…˜μ€ νŠΉμ •ν•œ λͺ¨λ“ˆ 이름을 μ§€μ •ν–ˆμ„ λ•Œ μ»΄νŒŒμΌλŸ¬κ°€ μ–΄λ””μ„œ λΆ€ν„° λͺ¨λ“ˆμ„ 탐색해야 ν•  지λ₯Ό 지정할 수 μžˆλŠ” 맡을 μ œκ³΅ν•œλ‹€.

    {
      "compilerOptions": {
        "baseUrl": "./src",
        "paths": {
          "app/*": ["app/*"],
          "config/*": ["app/_config/*"],
          "environment/*": ["environments/*"],
          "shared/*": ["app/_shared/*"],
          "helpers/*": ["helpers/*"],
          "tests/*": ["tests/*"]
        }
      }
    }

    맡에 ν¬ν•¨λ˜λŠ” κ²½λ‘œλ“€μ€ baseUrl을 κΈ°μ€€μœΌλ‘œ μƒλŒ€ 경둜둜 κ³„μ‚°λ˜κΈ° λ•Œλ¬Έμ—, paths μ˜΅μ…˜μ„ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ°˜λ“œμ‹œ baseUrl μ˜΅μ…˜μ— 값을 μ±„μ›Œμ£Όμ–΄μ•Ό ν•œλ‹€. 즉, μœ„ μ˜ˆμ‹œμ˜ app/*λŠ” ./src/app/*λ₯Ό μ˜λ―Έν•˜λ©°, λͺ¨λ“ˆμ„ 뢈러올 λ•Œ import foo from 'app/math'와 같이 μ ‘κ·Όν•˜κ²Œ 되면, νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” μžλ™μœΌλ‘œ 맡에 ν•΄λ‹Ήν•˜λŠ” 경둜인 ./src/app/mathλ₯Ό νƒμƒ‰ν•œ ν›„ λͺ¨λ“ˆμ„ κ°€μ Έμ˜€κ²Œ λœλ‹€.

    λ§Œμ•½ paths μ˜΅μ…˜μ— μ„€μ •λœ μœ„μΉ˜λ“€μ„ μ „λΆ€ νƒμƒ‰ν–ˆλŠ”λ°λ„ λͺ¨λ“ˆμ„ 찾지 λͺ» ν–ˆλ‹€λ©΄, moduleResolution μ˜΅μ…˜μ—μ„œ μ„€μ •ν•œ μ „λž΅μ— 따라 좔가적인 탐색을 μ§„ν–‰ν•˜κ²Œ λœλ‹€.

    사싀 패슀 맀핑 μžμ²΄λŠ” μ›Œλ‚™ κ°„λ‹¨ν•œ 섀정이기 λ•Œλ¬Έμ— 크게 μ–΄λ €μšΈ 것이 μ—†μ§€λ§Œ, μ’…μ’… 패슀 맀핑이 λ°˜λ“œμ‹œ baseUrl을 κΈ°μ€€μœΌλ‘œ μ‹œμž‘ν•œλ‹€λŠ” 점을 κΉŒλ¨Ήμ–΄μ„œ μ‹€μˆ˜λ₯Ό ν•˜κ³€ ν•œλ‹€.

    myProject
    β”œβ”€β”€ src
    β”‚   └── index.ts
    β”œβ”€β”€ node_modules
    β”‚   └── foo
    └── tsconfig.json

    μœ„μ™€ 같은 ꡬ쑰의 ν”„λ‘œμ νŠΈμ—μ„œ baseUrl둜 ./srcλ₯Ό μ§€μ •ν•œ 상황을 μƒκ°ν•΄λ³΄μž. μš°λ¦¬κ°€ fooλΌλŠ” νŒ¨μŠ€μ— node_modules/fooλ₯Ό λ§€ν•‘ν•˜λ €λ©΄ μ–΄λ–»κ²Œ ν•΄μ•Όν• κΉŒ?

    μ΄λ•Œ 자주 ν•˜λŠ” μ‹€μˆ˜λŠ” tsconfig.json의 μœ„μΉ˜λ₯Ό κΈ°μ€€μœΌλ‘œ "foo": "./node_modules/foo"라고 μ„€μ •ν•˜λŠ” 것이닀. μ•„λ¬΄λž˜λ„ λ‚΄κ°€ μ§€κΈˆ 패슀 맀핑 μž‘μ—…μ„ ν•˜κ³  μžˆλŠ” 파일이 tsconfig.jsonμ΄λ‹ˆ 별 생각없이 ν˜„μž¬ 파일 κΈ°μ€€μœΌλ‘œ 경둜λ₯Ό μ§€μ •ν•˜λŠ” 것이닀.

    ν•˜μ§€λ§Œ 패슀 맀핑은 baseUrl을 κΈ°μ€€μœΌλ‘œ μ§„ν–‰λ˜κΈ° λ•Œλ¬Έμ— 이 κ²½μš°μ—λŠ” src 디렉토리가 기쀀점이 λœλ‹€. 즉, "foo": "../node_modules/foo"둜 맀핑을 진행해주어야 ν•œλ‹€λŠ” 것이닀.

    κ°„ν˜Ή 이 점을 잊고 tsconfig.json의 μœ„μΉ˜λ₯Ό κΈ°μ€€μœΌλ‘œ 패슀 맀핑을 μ§„ν–‰ν–ˆλ‹€κ°€ λͺ¨λ“ˆ 탐색에 μ‹€νŒ¨ν•˜λŠ” κ²½μš°κ°€ μ™•μ™• μžˆμœΌλ‹ˆ μ£Όμ˜ν•˜λ„λ‘ ν•˜μž.

    module

    κ°’ μ„€λͺ…
    CommonJS (default) CommonJS ν˜•μ‹μœΌλ‘œ λͺ¨λ“ˆμ„ μ»΄νŒŒμΌν•œλ‹€.
    AMD Asynchronous Module Definition ν˜•μ‹μœΌλ‘œ λͺ¨λ“ˆμ„ μ»΄νŒŒμΌν•œλ‹€.
    UMD Universal Module Definition λ””μžμΈ νŒ¨ν„΄μ„ μ‚¬μš©ν•˜μ—¬ λͺ¨λ“ˆμ„ μ»΄νŒŒμΌν•œλ‹€.
    System System.js ν˜•μ‹μœΌλ‘œ λͺ¨λ“ˆμ„ μ»΄νŒŒμΌν•œλ‹€.
    ES6, ES2015, ES2020, ESNext ESM(ES Module) λ°©μ‹μœΌλ‘œ λͺ¨λ“ˆμ„ μ»΄νŒŒμΌν•œλ‹€.

    module μ˜΅μ…˜μ€ μ»΄νŒŒμΌμ„ 마친 μžλ°”μŠ€ν¬λ¦½νŠΈ λͺ¨λ“ˆμ΄ μ–΄λ–€ λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•  지λ₯Ό μ„€μ •ν•˜λŠ” μ˜΅μ…˜μ΄λ‹€.

    λ¬Όλ‘  ECMAScriptμ—μ„œ μ§€μ •ν•œ 곡식 λͺ¨λ“ˆ μ‹œμŠ€ν…œμ€ import, export ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” ESM λ°©μ‹μ΄κΈ°λŠ” ν•˜μ§€λ§Œ, ν˜„μ‹€μ μœΌλ‘œ μ΄λŸ¬ν•œ λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ μ§€μ›ν•˜λŠ” λΈŒλΌμš°μ €κ°€ 아직 λ§Žμ§€ μ•Šκ³ , NodeJS 같은 경우 μ§€λ‚œ 12.0.0 λ²„μ „μ—μ„œ --experimental-modules ν”Œλž˜κ·Έ 없이 ESM을 μ‚¬μš©ν•  수 μžˆλŠ” κΈ°λŠ₯이 μΆ”κ°€λ˜κΈ°λŠ” ν–ˆμ§€λ§Œ, 아직 μƒνƒœκ³„ 전체에 ESM μ‹œμŠ€ν…œμ΄ νΌμ ΈμžˆλŠ” 상황은 μ•„λ‹ˆλ‹€. (NodeJS μ§„μ˜μ€ 아직 CommonJSλ₯Ό 많이 μ‚¬μš©ν•œλ‹€)

    이런 μ΄μœ λ“€λ‘œ 인해 우리의 λͺ¨λ“ˆμ΄ 무쑰건 ESM μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜λ„λ‘ μ»΄νŒŒμΌν•˜κΈ°λŠ” ν˜„μ‹€μ μœΌλ‘œ μ–΄λ ΅κΈ° λ•Œλ¬Έμ—, 적절히 상황에 λ§žλŠ” λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ 선택할 수 μžˆμ–΄μ•Ό ν•˜λŠ” 것이닀.

    이 ν¬μŠ€νŒ…μ—μ„œ μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ λͺ¨λ“ˆ μ‹œμŠ€ν…œμ— λŒ€ν•œ λͺ¨λ“  것을 λ‹€λ£° μˆ˜λŠ” μ—†μœΌλ‹ˆ, 각 λͺ¨λ“ˆ μ‹œμŠ€ν…œλ“€μ˜ νŠΉμ§• μ •λ„λ§Œ κ°„λ‹¨ν•˜κ²Œ μ•Œμ•„λ³΄κ³  λ„˜μ–΄κ°€λ„λ‘ ν•˜κ² λ‹€. ν•œλ²ˆ κ°„λ‹¨ν•˜κ²Œ 두 개의 λͺ¨λ“ˆλ‘œ κ΅¬μ„±λœ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μƒμƒν•΄λ³΄μž.

    // utils/math.ts
    export const add = (x: number) => (y: number) => x + y;
    // index.ts
    import { add } from './utils/math';
    
    export const add2 = add(2);

    μœ„ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ index.ts λͺ¨λ“ˆμ€ utils/math.ts λͺ¨λ“ˆμ—μ„œ addλΌλŠ” ν•¨μˆ˜λ₯Ό κ°€μ Έμ™€μ„œ 컀링을 톡해 add2 ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κ³  λ‹€μ‹œ λ‚΄λ³΄λ‚΄λŠ” λͺ¨λ“ˆμ΄λ‹€. 이제 이 μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ„ 각각의 λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜λ„λ‘ μ»΄νŒŒμΌν•˜λ©΄ index.tsκ°€ μ–΄λ–»κ²Œ λ³€κ²½λ˜λŠ”μ§€, 그리고 각 λͺ¨λ“ˆ μ‹œμŠ€ν…œμ˜ νŠΉμ§•μ΄ 무엇인지 μ‚΄νŽ΄λ³΄λ„λ‘ ν•˜μž.

    CommonJS

    "use strict";
    exports.__esModule = true;
    exports.add2 = void 0;
    var math_1 = require("./utils/math");
    exports.add2 = (0, math_1.add)(2);

    CommonJSλŠ” Commonμ΄λΌλŠ” μ΄λ¦„λ‹΅κ²Œ, μžλ°”μŠ€ν¬λ¦½νŠΈ λͺ¨λ“ˆμ„ λΈŒλΌμš°μ € 뿐 μ•„λ‹ˆλΌ μ„œλ²„ ν™˜κ²½μ΄λ‚˜ λ°μŠ€ν¬νƒ‘ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ λ‚΄μ—μ„œλ„ 자유둭게 μ‚¬μš©ν•˜λŠ” 것을 ν‘œλ°©ν•˜κ³  μžˆλŠ” λͺ¨λ“ˆ μ‹œμŠ€ν…œμ΄λ‹€.

    그런 이유둜 μ„œλ²„ μ‚¬μ΄λ“œμ—μ„œ 주둜 μ‚¬μš©ν•˜λŠ” λŸ°νƒ€μž„μΈ NodeJS 같은 κ²½μš°μ—λŠ” μ•„μ§κΉŒμ§€ CommonJS μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜κ³  μžˆλŠ” ν™˜κ²½μ΄ 많기 λ•Œλ¬Έμ— μ„œλ²„ μ‚¬μ΄λ“œμ—μ„œ μ‚¬μš©ν•  라이브러리 등을 λ§Œλ“ λ‹€λ©΄ ν•΄λ‹Ή CommonJS μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜λŠ” 것을 고민해봐야 ν•œλ‹€.

    CommonJSλŠ” exports λ˜λŠ” module.exports 객체의 ν”„λ‘œνΌν‹°μ— λͺ¨λ“ˆμ„ ν• λ‹Ήν•˜κ³ , μ „μ—­ν•¨μˆ˜ requireλ₯Ό 톡해 λ™κΈ°μ μœΌλ‘œ λͺ¨λ“ˆμ„ 직접 κ°€μ Έμ˜€λŠ” 방식을 μ‚¬μš©ν•œλ‹€. μœ„ μ˜ˆμ‹œμ—μ„œλ„ math_1μ΄λΌλŠ” λ³€μˆ˜μ— requireλΌλŠ” ν•¨μˆ˜λ₯Ό 톡해 λͺ¨λ“ˆμ„ ν• λ‹Ήν•˜κ³ , exports.add2 ν”„λ‘œνΌν‹°μ— add2 ν•¨μˆ˜λ₯Ό λ‹€μ‹œ ν• λ‹Ήν•˜κ³  μžˆλŠ” 것을 λ³Ό 수 μžˆλ‹€.

    μ΄λ ‡κ²Œ require ν•¨μˆ˜κ°€ 싀행될 λ•Œ λ™κΈ°μ μœΌλ‘œ λͺ¨λ“ˆμ„ κ°€μ Έμ˜€λŠ” CommonJS의 νŠΉμ§• 덕뢄에 if 문을 μ‚¬μš©ν•˜μ—¬ β€œμ΄λŸ΄ 땐 A λͺ¨λ“ˆ, μ €λŸ΄ 땐 B λͺ¨λ“ˆμ„ 가져와!” 같은 짓을 ν•˜λŠ” 것도 κ°€λŠ₯ν•˜λ‹€.

    AMD

    define(["require", "exports", "./utils/math"], function (require, exports, math_1) {
        "use strict";
        exports.__esModule = true;
        exports.add2 = void 0;
        exports.add2 = (0, math_1.add)(2);
    });

    AMD(Asynchronous Module Definition)λŠ” 이름에 걸맞게 λΉ„λ™κΈ°μ μœΌλ‘œ λͺ¨λ“ˆμ„ κ°€μ Έμ˜€λŠ” 방식을 μ‚¬μš©ν•˜λŠ” λͺ¨λ“ˆ μ‹œμŠ€ν…œμ΄λ‹€.

    기쑴의 CommonJSλŠ” λ™κΈ°μ μœΌλ‘œ λͺ¨λ“ˆμ„ κ°€μ Έμ˜€λŠ” 것을 μ „μ œλ‘œ κ°œλ°œλ˜μ—ˆκΈ° λ•Œλ¬Έμ— 항상 λΉ„λ™κΈ°μ μœΌλ‘œ λͺ¨λ“ˆμ„ κ°€μ Έμ˜€λŠ” 방식을 κ΅¬ν˜„ν•˜κΈ° μœ„ν•œ ν™œλ°œν•œ λ…Όμ˜κ°€ μžˆμ—ˆλŠ”λ°, 이 λ…Όμ˜ κ³Όμ • μ†μ—μ„œ 기쑴의 CommonJS의 정신인 β€œλͺ¨λ“  ν™˜κ²½μ—μ„œ μž‘λ™ν•˜λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ λͺ¨λ“ˆβ€μ— κ³΅κ°ν•˜μ§€ λͺ»ν•˜λŠ” μ‚¬λžŒλ“€μ΄ λ‚˜μ™”κ³ , 이 μ‚¬λžŒλ“€μ΄ λ”°λ‘œ λ…λ¦½ν•˜μ—¬ AMD 그룹을 λ§Œλ“€κ²Œ λ˜μ—ˆλ‹€.

    사싀 λΈŒλΌμš°μ € ν™˜κ²½μ€ μ• μ΄ˆμ— μ„œλ²„μ™€ λ‹€λ₯΄κ²Œ, ν•„μš”ν•œ λͺ¨λ“ˆλ“€μ„ μ„œλ²„λ‘œλΆ€ν„° λ°›μ•„μ™€μ„œ μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” ν™˜κ²½μ΄λ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— ν•„μš”ν•œ λͺ¨λ“  λͺ¨λ“ˆμ„ ν•œλ²ˆμ— λ°›μ•„μ„œ μ‹€ν–‰ν•˜λŠ” 것보닀 λͺ¨λ“ˆ μ€‘μ—μ„œ ν•„μš”ν•œ λΆ€λΆ„λ§Œ μ„œλ²„λ‘œλΆ€ν„° λΉ„λ™κΈ°μ μœΌλ‘œ κ°€μ Έμ™€μ„œ μ‚¬μš©ν•˜λŠ” 것이 더 효율적인 것이닀. ν•˜μ§€λ§Œ β€œλͺ¨λ“  ν™˜κ²½μ—μ„œ μž‘λ™ν•˜λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ λͺ¨λ“ˆβ€μ„ λͺ©ν‘œλ‘œ ν•˜λŠ” CommonJS κ·Έλ£Ήμ—μ„œλŠ” 이런 ν™˜κ²½ 차이λ₯Ό ν†΅ν•©ν•˜κΈ°κ°€ 쉽지 μ•Šμ•˜λ‹€.

    이 κ³Όμ •μ—μ„œ β€œλΈŒλΌμš°μ €λ§Œμ΄λΌλ„ μ œλŒ€λ‘œ ν•΄λ³΄μžβ€λΌλŠ” μ˜κ²¬μ„ 가진 μ‚¬λžŒλ“€μ΄ λ…λ¦½ν•˜μ—¬ AMD μ‹œμŠ€ν…œμ΄ νƒ„μƒν•˜κ²Œ λ˜μ—ˆκ³ , 그런 이유둜 AMD μ‹œμŠ€ν…œμ€ λΈŒλΌμš°μ €μ—μ„œμ˜ 비동기 λͺ¨λ“ˆ ν˜ΈμΆœμ— μ΄ˆμ μ„ λ§žμΆ”κ³  κ°œλ°œλ˜μ—ˆλ‹€. (λ¬Όλ‘  CommonJS도 이후 비동기 λͺ¨λ“ˆ λ‘œλ”© κΈ°λŠ₯을 λ”°λ‘œ μΆ”κ°€ν–ˆλ‹€)

    ν•˜μ§€λ§Œ κ²°κ΅­ CommonJSμ—μ„œ λ–¨μ–΄μ Έ λ‚˜μ˜¨ 그룹인만큼 CommonJS와 AMDλŠ” μ„œλ‘œ ν˜Έν™˜ν•  수 μžˆλŠ” κΈ°λŠ₯듀을 많이 μ œκ³΅ν•˜κ³  있기 λ•Œλ¬Έμ—, 기쑴의 CommonJS λͺ¨λ“ˆμ„ AMD λ°©μ‹μœΌλ‘œ λž˜ν•‘ν•΄μ„œ μ‚¬μš©ν•˜λŠ” λ“±μ˜ μ‘μš©λ„ κ°€λŠ₯ν•˜λ‹€.

    μœ„ μ˜ˆμ‹œμ—μ„œλ„ AMD μ‹œμŠ€ν…œμ˜ define ν•¨μˆ˜κ°€ 기본적으둜 CommonJS의 require, export ν•¨μˆ˜λ₯Ό κ°€μ Έμ™€μ„œ μ‚¬μš©ν•˜κ³  μžˆλŠ” λͺ¨μŠ΅μ„ λ³Ό 수 μžˆλ‹€. 기본적으둜 λ‚΄λΆ€ κ΅¬μ‘°λŠ” CommonJS와 λΉ„μŠ·ν•˜μ§€λ§Œ, requireν•¨μˆ˜λ₯Ό 톡해 λͺ¨λ“ˆμ„ κ°€μ Έμ˜€λŠ” 것이 μ•„λ‹ˆλΌ, define ν•¨μˆ˜μ˜ 3번째 인자인 math_1을 톡해 λͺ¨λ“ˆμ„ μ£Όμž…λ°›κ³  μžˆλ‹€λŠ” 것이 CommonJS μ‹œμŠ€ν…œκ³Όμ˜ 결정적인 차이이닀.

    UMD

    (function (factory) {
        if (typeof module === "object" && typeof module.exports === "object") {
            var v = factory(require, exports);
            if (v !== undefined) module.exports = v;
        }
        else if (typeof define === "function" && define.amd) {
            define(["require", "exports", "./utils/math"], factory);
        }
    })(function (require, exports) {
        "use strict";
        exports.__esModule = true;
        exports.add2 = void 0;
        var math_1 = require("./utils/math");
        exports.add2 = (0, math_1.add)(2);
    });

    UMD(Universal Module Definition)은 λͺ¨λ“ˆ μ‹œμŠ€ν…œμ΄λΌκΈ°λ³΄λ‹€ λ‹€μ–‘ν•œ ν™˜κ²½μ—μ„œ Universal, 즉, λ²”μš©μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλŠ” ν˜•νƒœμ˜ λ””μžμΈ νŒ¨ν„΄μ΄λΌκ³  λ³Ό 수 μžˆλ‹€.

    κ·Έλ ‡κΈ° λ•Œλ¬Έμ— UMD νŒ¨ν„΄μ€ 보톡 RequireJS 같은 라이브러리λ₯Ό 톡해 μ‚¬μš©ν•˜λŠ” AMDλ‚˜ CommonJS와 λ‹€λ₯΄κ²Œ, 직접 κ°œλ°œμžκ°€ 직접 UMD νŒ¨ν„΄μ„ μ‚¬μš©ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ€˜μ•Ό ν•œλ‹€. μ‰½κ²Œ 말해 μœ„μ˜ μ˜ˆμ‹œμ²˜λŸΌ IIFE(Immediately Invoked Function Expression, μ¦‰μ‹œμ‹€ν–‰ν•¨μˆ˜)λ₯Ό μ‚¬μš©ν•˜μ—¬ κ°œλ°œμžκ°€ 직접 μ†ŒμŠ€ μ½”λ“œ λ‚΄μ—μ„œ λΆ„κΈ°λ₯Ό μ³μ£ΌλŠ” 것이 κ²°κ΅­ UMD νŒ¨ν„΄μ΄λΌλŠ” 것이닀.

    μœ„ μ˜ˆμ‹œλ₯Ό 보면 moduleκ³Ό module.exportsκ°€ μ‘΄μž¬ν•œλ‹€λ©΄ CommonJS μ‹œμŠ€ν…œμ„, define ν•¨μˆ˜κ°€ μ‘΄μž¬ν•œλ‹€λ©΄ AMD μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜λŠ” 것을 λ³Ό 수 μžˆλ‹€. 이처럼 UMD νŒ¨ν„΄μ€ λͺ¨λ“ˆμ΄ μ‚¬μš©λ˜λŠ” ν™˜κ²½μ΄ μ–΄λ–€ λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜λŠ”μ§€ 여뢀와 상관없이 항상 λ™μΌν•œ κ²½ν—˜μ„ μ œκ³΅ν•  수 μžˆλ‹€λŠ” μž₯점이 μžˆλ‹€.

    all taken UMD === 뭘 좋아할 지 몰라서 모든 모듈 시스템을 다 준비했어

    μΆ”κ°€μ μœΌλ‘œ, λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜λŠ” ν™˜κ²½μ΄ CommonJS, AMD 두 μ‹œμŠ€ν…œ λͺ¨λ‘ μ§€μ›ν•˜μ§€ μ•ŠλŠ” κ²½μš°μ—λŠ” UMD νŒ¨ν„΄μ„ μ‚¬μš©ν•˜μ—¬ μ „μ—­ 객체인 globalThis, window, global λ“±μ˜ ν”„λ‘œνΌν‹°λ‘œ λͺ¨λ“ˆμ„ λ„£λŠ” μ΅œν›„μ˜ 방법도 μžˆκΈ°λŠ” ν•˜λ‹€.

    ν•˜μ§€λ§Œ 이 방법은 μ „μ—­ μŠ€μ½”ν”„λ₯Ό μ˜€μ—Όμ‹œν‚€κΈ° λ•Œλ¬Έμ— 가급적이면 ν”Όν•˜λŠ” 것이 쒋기도 ν•˜κ³ , 졜근의 μžλ°”μŠ€ν¬λ¦½νŠΈ λŸ°νƒ€μž„ ν™˜κ²½μ—μ„œ λͺ¨λ“ˆ μ‹œμŠ€ν…œ μžμ²΄κ°€ μ§€μ›λ˜μ§€ μ•ŠλŠ” ν™˜κ²½μ΄ κ°•μ œλ˜λŠ” κ²½μš°λŠ” ν”μΉ˜ μ•ŠμœΌλ―€λ‘œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 이런 λ°©λ²•κΉŒμ§€λŠ” μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” κ²ƒμœΌλ‘œ 보인닀.

    System

    System.register(["./utils/math"], function (exports_1, context_1) {
        "use strict";
        var math_1, add2;
        var __moduleName = context_1 && context_1.id;
        return {
            setters: [
                function (math_1_1) {
                    math_1 = math_1_1;
                }
            ],
            execute: function () {
                exports_1("add2", add2 = math_1.add(2));
            }
        };
    });

    UMD νŒ¨ν„΄μ΄ β€œλ„€κ°€ 뭘 μ’‹μ•„ν•  지 λͺ°λΌμ„œ λ‹€ μ€€λΉ„ν–ˆμ–΄β€μ˜ λ””μžμΈ νŒ¨ν„΄ 버전이라면, SystemJSλŠ” 이걸 라이브러리둜 κ΅¬ν˜„ν•œ λͺ¨λ“ˆ λ‘œλ”μ΄λ‹€. (ν•œ 술 더 λ–΄λ‹€)

    즉, SystemJSλŠ” λͺ¨λ“ˆ λ‘œλ”μ΄κΈ° λ•Œλ¬Έμ— λͺ¨λ“ˆμ„ μ–΄λ–»κ²Œ μ •μ˜ν•˜λŠ”μ§€μ— λŒ€ν•œ 것은 κ΄€μ—¬ν•˜μ§€ μ•Šκ³ , κ·Έμ € 이미 CommonJS, AMD, ESM λ°©μ‹μœΌλ‘œ μ •μ˜λœ λͺ¨λ“ˆμ„ λ‘œλ“œν•΄μ£ΌκΈ°λ§Œ ν•˜λŠ” 녀석이닀.

    SystemJSλŠ” 2016λ…„ ECMA μž¬λ‹¨μ΄ ES6의 곡식 λͺ¨λ“ˆ μŠ€νŽ™μœΌλ‘œ import, export ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” ESM νŒ¨ν„΄μ„ λ°œν‘œν–ˆμ„ λ•Œ 즈음 κ½€λ‚˜ 많이 μ‚¬μš©λ˜λ˜ 녀석인데, κ·Έ μ΄μœ λŠ” 이 λ‹Ήμ‹œ λΈŒλΌμš°μ €λ“€μ΄ 이 μŠ€νŽ™μ„ μ§€μ›ν•˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

    λͺ¨λ“ˆλ§μ— λŒ€ν•œ 곡식 μŠ€νŽ™μ€ μ •ν•΄μ‘ŒμœΌλ‚˜ μ •μž‘ κ·Έκ±Έ μ‹€ν–‰μ‹œν‚¬ λΈŒλΌμš°μ € λ²€λ”λ“€μ˜ λŒ€μ‘μ΄ λŠ¦λŠ” 상황이라 ESM을 μ‚¬μš©ν•˜κ³  싢어도 ν•  μˆ˜κ°€ μ—†λŠ” μƒν™©μ΄μ—ˆλŠ”λ°, μ΄λ•Œ SystemJSκ°€ 이 쀑간 닀리 역할을 ν•΄μ€¬μ—ˆλ‹€. λ‹Ήμ‹œμ—λŠ” es-module-loaderλΌλŠ” 폴리필을 μ‚¬μš©ν•˜μ—¬ ESM λ°©μ‹μ˜ λͺ¨λ“ˆμ„ λΆˆλŸ¬μ˜€λ„λ‘ κ΅¬ν˜„λ˜μ–΄μžˆμ—ˆλ‹€.

    그런데 μ—¬κΈ°μ„œ ν•œ 가지 의문이 λ“œλŠ” 것이 β€œλΈŒλΌμš°μ €κ°€ μ§€μ›ν•˜μ§€ μ•ŠλŠ” λͺ¨λ“ˆμ„ λΆˆλŸ¬μ™€λ„ νŠΈλžœμŠ€νŒŒμΌλ§μ„ ν•˜μ§€ μ•ŠμœΌλ©΄ 싀행을 μ‹œν‚¬ 수 μ—†λŠ” κ²½μš°λ„ μžˆμ„ν…λ°, SystemJSλŠ” 이 문제λ₯Ό μ–΄λ–»κ²Œ ν•΄κ²°ν•˜λŠ” 걸까?β€λΌλŠ” 것인데, 정닡은 μ˜μ™Έλ‘œ κ°€κΉŒμš΄ 곳에 μžˆμ—ˆλ‹€.

    cool 복잡한 건 잘 모르겠고, 그냥 babel 가져와서 런타임에 쿨하게 트랜스파일링 돌리자

    κ·Έλ ‡λ‹€. 이 방법을 μ“°λ©΄ 뢈러올 λŒ€μƒ λͺ¨λ“ˆμ˜ μ–Έμ–΄κ°€ ES2020λ‚˜ νƒ€μž…μŠ€ν¬λ¦½νŠΈλΌλ„ 아무 λ¬Έμ œκ°€ μ—†κ³ , λͺ¨λ“ˆ μ‹œμŠ€ν…œμœΌλ‘œ CommonJSλ₯Ό μ‚¬μš©ν•˜λ˜ AMDλ₯Ό μ‚¬μš©ν•˜λ˜ 아무 λ¬Έμ œκ°€ μ—†λ‹€. λ¬Όλ‘  이 κΈ°λŠ₯은 SystemJS의 κΈ°λ³Έ κΈ°λŠ₯이 μ•„λ‹ˆλΌμ„œ systemjs-babelμ΄λΌλŠ” μ΅μŠ€ν…μ…˜μ„ μ‚¬μš©ν•΄μ•Ό ν•˜μ§€λ§Œ, 문제 ν•΄κ²° 방식 μžμ²΄κ°€ μƒλ‹Ήνžˆ ν™”λˆν•œ 것이 인상적이닀.

    ν•˜μ§€λ§Œ λŸ°νƒ€μž„μ— λͺ¨λ“ˆμ„ νŠΈλžœμŠ€νŒŒμΌλ§ν•œλ‹€λŠ” 것은 λ‹¨μˆœνžˆ 트랜슀파일링만의 λ¬Έμ œκ°€ μ•„λ‹ˆλΌ λͺ¨λ“ˆ κ°„μ˜ μ˜μ‘΄κ΄€κ³„λ„ νŒŒμ•…ν•΄μ•Όν•˜κ³ , 심지어 νƒ€μž…μŠ€ν¬λ¦½νŠΈλ₯Ό μ‚¬μš©ν•˜λŠ” κ²½μš°μ—λŠ” 정적 νƒ€μž… 체크λ₯Ό 거친 μ»΄νŒŒμΌκΉŒμ§€ ν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμ—, λ‹Ήμ—°νžˆ λΉŒλ“œ νƒ€μž„μ— 이런 무거운 μž‘μ—…μ„ μˆ˜ν–‰ν•΄λ²„λ¦¬λ©΄ νΌν¬λ¨ΌμŠ€κ°€ λ–¨μ–΄μ§ˆ 수 밖에 μ—†λ‹€.

    2021λ…„ ν˜„μž¬, λͺ¨λ‘κ°€ μ•Œλ‹€μ‹œν”Ό 이런 무거운 μž‘μ—…λ“€μ€ λͺ¨λ‘ λΉŒλ“œ νƒ€μž„μ— 진행해도 아무 λ¬Έμ œκ°€ μ—†μœΌλ‹ˆ, ꡳ이 λŸ°νƒ€μž„μ— 이런 짓을 λ²Œμ΄λŠ” SystemJSλŠ” νŠΉμˆ˜ν•œ 상황이 μ•„λ‹ˆλ©΄ 쓰이지 μ•ŠλŠ” λΆ„μœ„κΈ°μΈ 것 κ°™λ‹€.

    ES Module

    import { add } from './utils/math';
    export var add2 = add(2);

    ESM(ES Module) 방식은 ECMA μž¬λ‹¨μ—μ„œ κ³΅μ‹μœΌλ‘œ μ •μ˜ν•œ μžλ°”μŠ€ν¬λ¦½νŠΈ μƒνƒœκ³„μ˜ λͺ¨λ“ˆ μ‹œμŠ€ν…œμ΄λ‹€.

    그런 이유둜 requireλ‚˜ define 같은 λ³„λ„μ˜ ν•¨μˆ˜μ— μ˜μ‘΄ν•˜μ—¬ λͺ¨λ“ˆμ„ λΆˆλŸ¬μ˜€λŠ” CommonJSλ‚˜ AMD와 λ‹€λ₯΄κ²Œ import, exportλΌλŠ” ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ λͺ¨λ“ˆμ„ λΆˆλŸ¬μ˜€λŠ” 것을 λ³Ό 수 μžˆλ‹€.

    2015년에 λ“±μž₯ν•œ ESM은 2009λ…„λΆ€ν„° μ‚¬μš©ν•˜λ˜ CommonJSλ‚˜ AMD에 λΉ„ν•˜λ©΄ ν›„λ°œ 주자인 μ£Όμ œμ—, λͺ¨λ“ˆμ— use strict λ””λ ‰ν‹°λΈŒκ°€ λ°˜λ“œμ‹œ ν¬ν•¨λ˜μ–΄μ•Ό ν•œλ‹€λ˜κ°€, thisκ°€ μ „μ—­ 객체인 windowλ₯Ό 바라보지 μ•ŠλŠ” λ“±μ˜ λ³€κ²½ 사항이 λ§Žμ•˜λ˜ μŠ€νŽ™μ΄κΈ°λ„ ν–ˆλ‹€.

    κ·Έλž˜μ„œ 이런 μ œμ•½μ΄ μ—†λŠ” CommonJSλ‚˜ AMD μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜λ˜ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜λ“€μ΄ μ†μ‰½κ²Œ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ„ ν•  수 μžˆλŠ” 상황이 μ•„λ‹ˆμ˜€κ³ , κ·Έ 상황이 μ§€κΈˆκΉŒμ§€λ„ μ΄μ–΄μ Έμ˜€κ³  μžˆλ‹€.

    그런 이유둜 였늘 날에도 λ„€μ΄ν‹°λΈŒ μžλ°”μŠ€ν¬λ¦½νŠΈ ν™˜κ²½μ—μ„œ ESM을 μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œ script νƒœκ·Έμ— type="module" 속성을 μΆ”κ°€ν•˜κ±°λ‚˜, package.json에 "type": "module"μ΄λΌλŠ” ν•„λ“œλ₯Ό μΆ”κ°€ν•΄μ€˜μ•Ό ν•˜λŠ” λ“± λ³„λ„μ˜ μž‘μ—…μ΄ ν•„μš”ν•œ 것이닀.

    λ¬Όλ‘  μ˜ˆμ „μ— λΉ„ν•˜λ©΄ 졜근 λ§Žμ€ 벀더듀이 ESM을 μ§€μ›ν•˜κ³  μžˆμ§€λ§Œ, κ·Έλž˜λ„ μ•„μ§κΉŒμ§€ ESM을 μ•ˆμ „ν•˜κ²Œ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” Webpackμ΄λ‚˜ Babel같은 λ²ˆλ“€λŸ¬μ™€ 트랜슀파일러λ₯Ό μ‘°ν•©ν•˜μ—¬ λΉŒλ“œ νƒ€μž„μ— λͺ¨λ“ˆ κ°„μ˜ 의쑴 관계λ₯Ό νŒŒμ•…ν•˜κ³  λŸ°νƒ€μž„μ΄ μ•Œμ•„λ“€μ„ 수 μžˆλŠ” ν˜•νƒœλ‘œ λ³€ν™˜ν•΄μ£ΌλŠ” 과정이 ν•„μš”ν•˜κΈ° λ•Œλ¬Έμ—, λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜λŠ” ν™˜κ²½μ΄ μ–΄λ–€ ν™˜κ²½μΈμ§€μ— λ”°λΌμ„œ λ•Œλ‘œλŠ” μ‚¬μš©ν•˜κΈ° 번거둜운 포맷이 될 수 μžˆλ‹€λŠ” 것을 염두에 두어야 ν•œλ‹€.

    λ‹€λ§Œ ESM은 이런 단점을 λͺ¨λ‘ 씹어먹을 수 μžˆμ„ μ •λ„μ˜ ν•œ 가지 μž₯점을 가지고 μžˆλŠ”λ°, λ°”λ‘œ Webpack으둜 λͺ¨λ“ˆμ„ λ²ˆλ“€λ§ν•  λ•Œ νŠΈλ¦¬μ‰μ΄ν‚Ήμ΄ μˆ˜μ›”ν•˜λ‹€λŠ” 것이닀.

    Webpack의 ModuleConcatenationPlugin은 λͺ¨λ“ˆλ“€μ„ ν•˜λ‚˜μ˜ ν΄λ‘œμ €λ‘œ ν†΅ν•©ν•˜μ—¬ λΈŒλΌμš°μ € ν™˜κ²½μ—μ„œ 더 높은 퍼포먼슀λ₯Ό λ§Œλ“€μ–΄λ‚΄λŠ”λ°, μ΄λ•Œ CommonJS와 ESM 쀑 μ–΄λ–€ λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜λƒμ— 따라 결과물이 많이 λ‹¬λΌμ§€κ²Œ λœλ‹€.

    // CommonJS
    
    (() => {
      "use strict";
      /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12);
      var add2 = (0,_utils__WEBPACK_IMPORTED_MODULE_0__/* .add */ .DG)(2);
    })();

    Webpack을 톡해 λ²ˆλ“€λ§ ν–ˆμ„ λ•Œ CommonJS μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•œ λͺ¨λ“ˆμ€ __webpack_require__λΌλŠ” ν•¨μˆ˜λ₯Ό 톡해 λΆˆλŸ¬μ™€μ§€κ²Œ λ˜λŠ”λ°, λ¬Έμ œλŠ” 이게 λͺ¨λ“ˆ 내뢀에 μžˆλŠ” add ν•¨μˆ˜λ§Œ λΆˆλŸ¬μ˜€λŠ” 것이 μ•„λ‹ˆλΌ 전체 λͺ¨λ“ˆμ„ λ‹€ λΆˆλŸ¬μ˜€λŠ” μ½”λ“œλΌλŠ” 것이닀.

    λ‹€μŒ 라인인 (0,_utils__WEBPACK_IMPORTED_MODULE_0__/* .add */ .DG)λ₯Ό 보면, 뢈러온 λͺ¨λ“ˆμ˜ DGλΌλŠ” ν”„λ‘œνΌν‹°μ— μ ‘κ·Όν•˜κ³  μžˆλŠ” 것을 λ³Ό 수 μžˆλŠ”λ°, 이 DGκ°€ λ‚œλ…ν™”λœ add ν•¨μˆ˜μ˜ 이름이닀.

    μ• μ΄ˆμ— CommonJSλŠ” exportsλΌλŠ” μ „μ—­ 객체의 ν”„λ‘œνΌν‹°μ— 값듀을 ν• λ‹Ήν•˜λŠ” 방식을 μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ—, β€œμ΄ λͺ¨λ“ˆμ—μ„œ AλΌλŠ” ν•¨μˆ˜λ§Œ μ‚¬μš©ν•œλ‹€β€λΌλŠ” 것을 νŒŒμ•…ν•˜κΈ°κ°€ 쉽지가 μ•Šμ€ 것이닀.

    ν•˜μ§€λ§Œ ESM μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•œ λͺ¨λ“ˆμ€ 각각의 export ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μ›ν•˜λŠ” 값을 λ”°λ‘œ 내보낼 수 있고, 그둜 인해 μƒμ„Έν•œ 의쑴 관계λ₯Ό νŒŒμ•…ν•˜κΈ°κ°€ ν•œκ²° μˆ˜μ›”ν•˜λ‹€.

    // ESM
    
    /******/ (() => { // webpackBootstrap
    /******/    "use strict";
    // CONCATENATED MODULE: ./utils/math.js**
    var add = (a) => (b) => a + b;
    // CONCATENATED MODULE: ./index.js**
    var add2 = add(2);
    /******/ })();

    Webpack을 톡해 λ²ˆλ“€λ§ν•œ ESM μ‹œμŠ€ν…œμ˜ λͺ¨λ“ˆμ„ 보면, μ• μ΄ˆμ— λͺ¨λ“ˆμ„ λΆˆλŸ¬μ˜€λŠ” μ½”λ“œ μžμ²΄κ°€ μ—†κ³  심지어 λͺ¨λ“ˆ 내뢀에 있던 ν•¨μˆ˜λ₯Ό 인라인으둜 박아버렸닀.

    λ¬Όλ‘  ν•„μžκ°€ 뢈러온 add ν•¨μˆ˜λŠ” 맀우 μž‘μ€ ν•¨μˆ˜λΌ 이런 μ‹μœΌλ‘œ ν‘œν˜„λ  수 μžˆλŠ” κ²ƒμ΄κΈ°λŠ” ν•˜μ§€λ§Œ, μ€‘μš”ν•œ 점은 λͺ¨λ“ˆ 내뢀에 μžˆλŠ” β€œνŠΉμ • ν•¨μˆ˜β€λ§Œ κ°€μ Έμ™”λ‹€λŠ” 것이닀.

    μ•žμ„œ 이야기 ν–ˆλ“―μ΄ ESM은 import, export ν‚€μ›Œλ“œλ₯Ό ν†΅ν•œ 상세 의쑴 관계 νŒŒμ•…μ΄ μ‰¬μš΄ 편이기 λ•Œλ¬Έμ— λΉŒλ“œ νƒ€μž„μ— 효율적으둜 μ‚¬μš©ν•˜λŠ” μ½”λ“œμ™€ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” μ½”λ“œλ₯Ό λ°œλΌλ‚΄λŠ” 것이 κ°€λŠ₯ν•˜λ‹€.

    μ΄λ ‡κ²Œ νŠΈλ¦¬μ‰μ΄ν‚Ήμ΄ μ‰½λ‹€λŠ” κ°•λ ₯ν•œ 이유 λ•Œλ¬Έμ— Lodashλ₯Ό μ œμ™Έν•œ 유λͺ…ν•œ λΌμ΄λΈŒλŸ¬λ¦¬λ“€μ€ λŒ€λΆ€λΆ„ ESM 방식을 κ³΅μ‹μ μœΌλ‘œ μ§€μ›ν•˜κ³  μžˆλ‹€.

    moduleResolution

    κ°’ μ„€λͺ…
    Node Node μ „λž΅μ„ μ‚¬μš©ν•˜μ—¬ λͺ¨λ“ˆμ„ νƒμƒ‰ν•œλ‹€. module μ˜΅μ…˜μ΄ CommonJS일 λ•Œ Default.
    Classic Classic μ „λž΅μ„ μ‚¬μš©ν•˜μ—¬ λͺ¨λ“ˆμ„ νƒμƒ‰ν•œλ‹€. module μ˜΅μ…˜μ΄ CommonJSκ°€ 아닐 λ•Œ Default.

    moduleResolution μ˜΅μ…˜μ€ νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ λͺ¨λ“ˆμ„ 뢈러올 λ•Œ 이 λͺ¨λ“ˆμ΄ μ •ν™•νžˆ 무엇을 μ°Έμ‘°ν•˜λŠ” 지λ₯Ό ν™•μΈν•˜λŠ” ν”„λ‘œμ„ΈμŠ€λ₯Ό λ‹€λ£¨λŠ” μ˜΅μ…˜μ΄λ‹€. 이건 κ·Έλƒ₯ 말둜 μ„€λͺ…ν•˜λ©΄ λ„ˆλ¬΄ μ–΄λ €μš°λ‹ˆ 직접 μ½”λ“œλ₯Ό λ³΄λ©΄μ„œ μ•Œμ•„λ³΄λ„λ‘ ν•˜μž.

    μš°μ„  νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 크게 μƒλŒ€ κ²½λ‘œμ™€ μ ˆλŒ€ 경둜, 2가지 방법을 μ‚¬μš©ν•˜μ—¬ λͺ¨λ“ˆμ˜ 경둜λ₯Ό μ •μ˜ν•˜λŠ” 방법을 μ‚¬μš©ν•˜κ³  μžˆλ‹€.

    import { add } from './utils/math'; // μƒλŒ€ 경둜
    import { debounce } from 'lodash'; // μ ˆλŒ€ 경둜

    μƒλŒ€ 경둜의 κ²½μš°μ—λŠ” ν˜„μž¬ μœ„μΉ˜λ₯Ό λ‚˜νƒ€λ‚΄λŠ” .μ΄λ‚˜ μƒμœ„ 디렉토리λ₯Ό λ‚˜νƒ€λ‚΄λŠ” .. λ“±μ˜ μ‹λ³„μžλ₯Ό μ‚¬μš©ν•˜μ—¬ 탐색을 μœ„ν•œ 기점을 μ§€μ •ν•¨μœΌλ‘œμ¨ μ •ν™•ν•œ λͺ¨λ“ˆμ˜ μœ„μΉ˜λ₯Ό λ‚˜νƒ€λ‚Ό 수 μžˆλŠ” 반면, μ ˆλŒ€ 경둜의 κ²½μš°μ—λŠ” λ‹¨μˆœνžˆ λͺ¨λ“ˆμ˜ μ΄λ¦„λ§Œ 적고 있기 λ•Œλ¬Έμ— μ •ν™•ν•œ λͺ¨λ“ˆμ˜ μœ„μΉ˜λ₯Ό μ°ΎκΈ° μœ„ν•΄μ„œ νŠΉμ •ν•œ κ·œμΉ™μ„ 기반으둜 λͺ¨λ“ˆμ„ μ°Ύμ•„λ‚˜μ„œλŠ” 여행을 λ– λ‚˜μ•Ό ν•œλ‹€.

    λ˜ν•œ μƒλŒ€ 경둜, μ ˆλŒ€ 경둜 두 방법 λͺ¨λ‘ λͺ¨λ“ˆμ˜ ν™•μž₯μžκΉŒμ§€λŠ” 적고 μžˆμ§€ μ•ŠκΈ° λ•Œλ¬Έμ—, 이 λͺ¨λ“ˆμ΄ math.ts, math.d.ts λ“± μ–΄λ–€ ν™•μž₯자λ₯Ό 가지고 μžˆλŠ” νŒŒμΌμΈμ§€λ„ μ•Œμ•„λ‚΄μ–΄μ•Ό ν•œλ‹€.

    κ·Έλ ‡κΈ° λ•Œλ¬Έμ— νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” λͺ¨λ“ˆμ˜ μ •ν™•ν•œ μœ„μΉ˜λ₯Ό μ°Ύμ•„λ‚΄λŠ” μ „λž΅μ„ μ„Έμ›Œμ•Ό ν•˜λŠ”λ°, μ΄λ•Œ moduleResolution μ˜΅μ…˜μœΌλ‘œ Classicκ³Ό Node 쀑 μ–΄λ–€ μ „λž΅μ„ μ‚¬μš©ν•  것인지λ₯Ό 선택할 수 μžˆλŠ” 것이닀.

    사싀 두 μ „λž΅ λͺ¨λ‘ λͺ¨λ“ˆμ˜ μœ„μΉ˜λ₯Ό μ°ΎλŠ” 기본적인 방법 μžμ²΄λŠ” 크게 λ‹€λ₯΄μ§€ μ•Šλ‹€.

    λ§Œμ•½ import { add } from './math'와 같이 μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•œ λͺ¨λ“ˆμ„ 찾을 λ•ŒλŠ” μ°Ύμ•„ λ³Ό 디렉토리가 λͺ…ν™•ν•˜κΈ° λ•Œλ¬Έμ— ν•΄λ‹Ή 디렉토리 λ‚΄μ—μ„œλ§Œ νŒŒμΌμ„ 찾아보고, import { add } from 'math'와 같이 μ ˆλŒ€ 경둜λ₯Ό μ‚¬μš©ν•œ λͺ¨λ“ˆμ„ 찾을 λ•ŒλŠ” μ°Ύμ•„μ•Ό ν•  디렉토리가 λͺ…ν™•ν•˜μ§€ μ•ŠμœΌλ‹ˆ, μš°μ„  ν•΄λ‹Ή λͺ¨λ“ˆμ„ 뢈러온 파일이 μœ„μΉ˜ν•œ 디렉토리뢀터 뒀져보고, μ—†λ‹€λ©΄ ν•œ 단계 μƒμœ„ λ””λ ‰ν† λ¦¬λ‘œ, κ·Έλž˜λ„ μ—†λ‹€λ©΄ 또 ν•œ 단계 μƒμœ„ λ””λ ‰ν† λ¦¬λ‘œ 거슬러 μ˜¬λΌκ°€λŠ” λ°©μ‹μœΌλ‘œ λͺ¨λ“ˆμ„ μ°ΎλŠ”λ‹€.

    μ΄λ•Œ 찾고자 ν•˜λŠ” 파일의 ν™•μž₯μžκ°€ 무엇인지, 그리고 λͺ¨λ“ˆμ„ 찾을 λ•Œ μ–΄λ””λ₯Ό λ¨Όμ € μ°Ύμ•„λ³΄λŠ”μ§€μ— λ”°λΌμ„œ Classic μ „λž΅κ³Ό Node μ „λž΅ κ°„μ˜ 차이가 λ°œμƒν•œλ‹€.

    Classic

    Classic μ „λž΅μ€ 사싀 상 νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ κΈ°λ³Έ λͺ¨λ“ˆ 탐색 μ „λž΅μœΌλ‘œ, μ˜ˆμ „λΆ€ν„° μ‚¬μš©ν•˜λ˜ μ „λž΅μ΄κΈ°λ„ ν•΄μ„œ ν•˜μœ„ λ²„μ „μ˜ νƒ€μž…μŠ€ν¬λ¦½νŠΈκ³Όμ˜ ν˜Έν™˜μ„±μ„ 맞좜 λ•Œμ—λ„ μ‚¬μš©ν•œλ‹€.

    Classic μ „λž΅μ€ μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•˜μ—¬ 뢈러온 λͺ¨λ“ˆμ„ 찾을 λ•Œ, ν•΄λ‹Ή λͺ¨λ“ˆμ„ λΆˆλŸ¬μ˜€λŠ” λͺ¨λ“ˆμ˜ μœ„μΉ˜λ₯Ό 기점으둜 *.ts, *.d.ts의 μˆœμ„œλ‘œ 탐색을 μ‹œμž‘ν•œλ‹€.

    // /root/src/index.ts
    import { add } from './math';
    1. /root/src/math.ts
    2. /root/src/math.d.ts

    μ•žμ„œ 이야기 ν–ˆλ“―μ΄ μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•œ κ²½μš°μ—λŠ” 찾아봐야 ν•˜λŠ” λ””λ ‰ν† λ¦¬μ˜ μœ„μΉ˜κ°€ λͺ…ν™•ν•˜κΈ° λ•Œλ¬Έμ— 후보가 λ˜λŠ” ν™•μž₯자만 탐색을 μ§„ν–‰ν•œ ν›„, ν•΄λ‹Ή λͺ¨λ“ˆμ΄ μ—†λŠ” 경우 탐색을 μ’…λ£Œν•˜κ²Œ λœλ‹€.

    반면 import { add } from 'math'와 같이 μ ˆλŒ€ 경둜λ₯Ό μ‚¬μš©ν•œ λͺ¨λ“ˆμ˜ κ²½μš°μ—λŠ” ν˜„μž¬ λͺ¨λ“ˆμ„ 뢈러온 κ²½λ‘œλΆ€ν„° μ‹œμž‘ν•΄μ„œ ν•œ 단계씩 λΆ€λͺ¨ λ””λ ‰ν† λ¦¬λ‘œ 거슬러 μ˜¬λΌκ°€λ©΄μ„œ 탐색을 μ§„ν–‰ν•˜κ²Œ λœλ‹€.

    // /root/src/index.ts
    import { add } from 'math';
    1. /root/src/math.ts
    2. /root/src/math.d.ts
    3. /root/math.ts
    4. /root/math.d.ts
    5. /math.ts

    μ ˆλŒ€ 경둜λ₯Ό μ‚¬μš©ν•œ 경우, κ°€μž₯ 처음으둜 λͺ¨λ“ˆμ„ 뢈러온 파일이 μœ„μΉ˜ν•œ /root/srcμ—μ„œλΆ€ν„° 탐색을 μ‹œμž‘ν•œλ‹€. 이후 이 λ””λ ‰ν† λ¦¬μ—μ„œ λͺ¨λ“ˆμ„ 찾지 λͺ» ν•˜λ©΄ ν•œ 단계 μ”© 거슬러 μ˜¬λΌκ°€λ©° λ‹€μ‹œ 탐색을 μ§„ν–‰ν•˜κ³ , λ£¨νŠΈκΉŒμ§€ νƒμƒ‰ν–ˆλŠ”λ°λ„ ν•΄λ‹Ή λͺ¨λ“ˆμ΄ μ—†λŠ” 경우 탐색을 μ’…λ£Œν•˜κ²Œ λœλ‹€.

    Node

    Node μ „λž΅μ€ 이름 κ·ΈλŒ€λ‘œ NodeJSκ°€ λͺ¨λ“ˆμ„ μ°ΎλŠ” 방식을 κ·ΈλŒ€λ‘œ λͺ¨λ°©ν•˜λŠ” μ „λž΅μ΄λ©°, Node μ „λž΅μ€ Classic μ „λž΅κ³Ό λ‹€λ₯΄κ²Œ *.tsλ‚˜ *.d.ts ν™•μž₯자λ₯Ό νƒμƒ‰ν•˜λŠ” 것을 λ„˜μ–΄μ„œμ„œ 쑰금 더 λ‹€μ–‘ν•œ ν˜•νƒœμ˜ λͺ¨λ“ˆκΉŒμ§€ 탐색을 ν•œλ‹€.

    μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•œ κ²½μš°λŠ” Classic μ „λž΅κ³Ό λΉ„μŠ·ν•˜κ²Œ, μ§€μ •λœ 디렉토리 λ‚΄λΆ€μ—μ„œ λ‹€μŒκ³Ό 같은 μˆœμ„œλ‘œ νŒŒμΌμ„ νƒμƒ‰ν•˜κ²Œ λœλ‹€.

    // /root/src/index.ts
    import { add } from './math';
    1. /root/src/math.ts
    2. /root/src/math.tsx
    3. /root/src/math.d.ts
    4. /root/src/math/package.json (types ν•„λ“œλ₯Ό μ‚¬μš©ν•˜λŠ” κ²½μš°μ— ν•œν•΄)
    5. root/src/math/index.ts
    6. root/src/math/index.tsx
    7. root/src/math/index.d.ts

    λ­”κ°€ 많이 μΆ”κ°€λœ 것 κ°™μ§€λ§Œ, κΈ°λ³Έμ μœΌλ‘œλŠ” Classic μ „λž΅κ³Ό λ§ˆμ°¬κ°€μ§€λ‘œ μ§€μ •λœ 디렉토리 λ‚΄λΆ€μ˜ νŒŒμΌμ„ 찾되, *.tsx ν™•μž₯자λ₯Ό 가진 λͺ¨λ“ˆκ³Ό package.json λ‚΄λΆ€μ˜ types 속성, 그리고 λͺ¨λ“ˆ 이름과 λ™μΌν•œ λ””λ ‰ν† λ¦¬μ˜ index.* νŒŒμΌμ„ μΆ”κ°€λ‘œ 탐색할 뿐이닀.

    이처럼 μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•œ λͺ¨λ“ˆμ„ 탐색할 λ•ŒλŠ” Node μ „λž΅κ³Ό Classic μ „λž΅ λͺ¨λ‘ λΉ„μŠ·ν•œ μˆœμ„œλ‘œ 디렉토리 트리λ₯Ό νƒμƒ‰ν•˜μ§€λ§Œ, μ ˆλŒ€ 경둜둜 μ§€μ •λœ λͺ¨λ“ˆμ„ 찾을 λ•ŒλŠ” 차이가 컀진닀.

    μ™œλƒν•˜λ©΄ Node μ „λž΅μ€ Classicκ³Ό λ‹€λ₯΄κ²Œ, μ ˆλŒ€ 경둜λ₯Ό μ‚¬μš©ν•œ λͺ¨λ“ˆμ„ 찾을 λ•ŒλŠ” node_modules 디렉토리λ₯Ό νƒμƒ‰ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

    // /root/src/index.ts
    import { add } from 'math';
    1. /root/src/node_modules/math.ts
    2. /root/src/node_modules/math.tsx
    3. /root/src/node_modules/math.d.ts
    4. /root/src/node_modules/math/package.json (types ν•„λ“œλ₯Ό μ‚¬μš©ν•˜λŠ” κ²½μš°μ— ν•œν•΄)
    5. /root/src/node_modules/@types/math.d.ts
    6. /root/src/node_modules/math/index.ts
    7. /root/src/node_modules/math/index.tsx
    8. /root/src/node_modules/math/index.d.ts
    9. /root/node_modules/math.ts (λΆ€λͺ¨ λ””λ ‰ν† λ¦¬λ‘œ 이동 ν›„ 반볡)

    …

    Node μ „λž΅μ„ μ‚¬μš©ν•˜κ²Œ 되면 κ°€μž₯ λ¨Όμ € math λͺ¨λ“ˆμ„ ν˜ΈμΆœν•œ 파일이 μœ„μΉ˜ν•œ /root/src 디렉토리에 μžˆλŠ” node_modules λ””λ ‰ν† λ¦¬μ—μ„œ 탐색을 μ§„ν–‰ν•œλ‹€.

    μ΄λ•Œ μƒλŒ€ κ²½λ‘œμ—μ„œμ™€ λ™μΌν•˜κ²Œ *.ts, *.tsx, *.d.ts 파일과 package.json의 types ν•„λ“œλ₯Ό 찾아보며, κ·Έ μ΄ν›„μ—λŠ” @types 디렉토리와 λͺ¨λ“ˆ 이름과 λ™μΌν•œ 이름을 가진 디렉토리 밑에 μžˆλŠ” index νŒŒμΌμ„ νƒμƒ‰ν•œλ‹€.

    μ΄λ ‡κ²Œ ν•œ 번의 탐색 과정이 λλ‚˜λ„ μ›ν•˜λŠ” λͺ¨λ“ˆμ„ 찾지 λͺ»ν–ˆλ‹€λ©΄, λΆ€λͺ¨ λ””λ ‰ν† λ¦¬λ‘œ μ΄λ™ν•œ ν›„ 이 과정을 λ‹€μ‹œ λ°˜λ³΅ν•œλ‹€. κ·Έ ν›„ λ£¨νŠΈμ— λ„λ‹¬ν•˜μ—¬ 둜컬 머신에 μ „μ—­ μ„€μΉ˜λœ λͺ¨λ“ˆμ΄ μžˆλŠ”μ§€κΉŒμ§€ νƒμƒ‰ν–ˆλŠ”λ°λ„ ν•΄λ‹Ή λͺ¨λ“ˆμ΄ μ—†λ‹€λ©΄ 탐색을 μ’…λ£Œν•˜κ²Œ λœλ‹€.

    μ΄λŸ¬ν•œ 탐색 과정은 NodeJSκ°€ λͺ¨λ“ˆμ„ μ°ΎλŠ” κ³Όμ •κ³Ό λ™μΌν•˜μ§€λŠ” μ•Šμ§€λ§Œ, 맀우 λΉ„μŠ·ν•˜λ‹€. NodeJSλŠ” λ¨Όμ € λͺ¨λ“ˆκ³Ό λ™μΌν•œ μ΄λ¦„μ˜ νŒŒμΌμ„ 찾은 ν›„, package.json의 main ν•„λ“œμ— 적힌 경둜의 νŒŒμΌμ„ μ°Ύμ•„ 보고, λ§ˆμ§€λ§‰μœΌλ‘œ λͺ¨λ“ˆκ³Ό λ™μΌν•œ 디렉토리 밑에 μžˆλŠ” index νŒŒμΌμ„ νƒμƒ‰ν•˜λŠ”λ°, νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ Node μ „λž΅μ΄ λ°”λ‘œ 이 탐색 과정을 λͺ¨λ°©ν•œ 것이기 λ•Œλ¬Έμ΄λ‹€.

    νƒ€μž…μŠ€ν¬λ¦½νŠΈ κ³΅ν™ˆμ—λŠ” NodeJSκ°€ μ‚¬μš©ν•˜λŠ” 방식과 λΉ„κ΅ν•΄μ„œ 크게 λ³΅μž‘ν•˜μ§€ μ•ŠμœΌλ‹ˆ 걱정말라고 ν•˜μ§€λ§Œ, 사싀 μ• μ΄ˆμ— NodeJSκ°€ μ‚¬μš©ν•˜κ³  μžˆλŠ” νŒ¨ν‚€μ§€ 탐색 방식 μžμ²΄κ°€ λΉ„νš¨μœ¨μ μ΄κΈ°λŠ” ν•˜λ‹€. 그런 이유둜 νƒ€μž…μŠ€ν¬λ¦½νŠΈ 4.0 λ²„μ „λΆ€ν„°λŠ” λΆˆλŸ¬μ˜€μ§€ μ•Šμ€ λͺ¨λ“ˆμ— λŒ€ν•΄μ„œλŠ” 더 이상 μœ„μ™€ 같은 과정을 톡해 νƒ€μž… 정보λ₯Ό 찾지 μ•Šλ„λ‘ μ—…λ°μ΄νŠΈκ°€ λ˜μ—ˆλ‹€.

    noResolve

    κ°’ μ„€λͺ…
    false (default) μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ— ν¬ν•¨λœ λͺ¨λ“  λͺ¨λ“ˆμ„ ν•΄μ„ν•œλ‹€.
    true λͺ…μ‹œμ μœΌλ‘œ μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ— ν¬ν•¨ν•˜κΈ°λ‘œν•œ λͺ¨λ“ˆλ§Œ ν•΄μ„ν•œλ‹€.

    기본적으둜 νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ»΄νŒŒμΌλŸ¬λŠ” μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ— ν¬ν•¨λœ λͺ¨λ“  λͺ¨λ“ˆμ„ μ»΄νŒŒμΌν•˜λ €κ³  μ‹œλ„ν•œλ‹€. 즉, tsconfig 루트의 includeλ‚˜ files ν•„λ“œμ— ν¬ν•¨ν•˜μ§€ μ•Šμ€ 파일이라고 해도, μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ λ‚΄μ—μ„œ 직접 import λ¬Έμ΄λ‚˜ /// <reference path="..." /> 같은 λ””λ ‰ν‹°λΈŒλ₯Ό μ‚¬μš©ν•˜μ—¬ λͺ¨λ“ˆμ„ λΆˆλŸ¬μ™”λ‹€λ©΄, κ·Έ λͺ¨λ“ˆ λ˜ν•œ 컴파일 λŒ€μƒμ΄λΌλŠ” 것이닀.

    μ–΄μ°Œλ³΄λ©΄ μ•”μ‹œμ μœΌλ‘œ λͺ¨λ“ˆμ„ 컴파일 λŒ€μƒμ— ν¬ν•¨μ‹œν‚¨λ‹€λŠ” 이야기인데, μ΄λ•Œ noResolve μ˜΅μ…˜μ„ true둜 μ„€μ •ν•˜λ©΄ 이런 μ•”μ‹œμ μΈ λͺ¨λ“ˆ μ»΄νŒŒμΌμ„ 막을 수 μžˆλ‹€.

    // tsconfig.json
    {
      "include": ["src/index.ts"],
      "compilerOptions": {
        "outDir": "./dist",
        "noResolve": true
      }
    }
    // src/index.ts
    
    import { add } from './utils/math';
    export const add2 = add(2);

    μœ„ μ˜ˆμ‹œμ˜ include ν•„λ“œμ—λŠ” src/index.ts만 ν¬ν•¨λ˜μ–΄ 있고, utils/math λͺ¨λ“ˆμ€ ν¬ν•¨λ˜μ–΄μžˆμ§€ μ•Šλ‹€.

    이런 경우일 λ•Œ noResolve μ˜΅μ…˜μ˜ 값이 false라면 index.tsμ—μ„œ μ‚¬μš©ν•˜λŠ” utils/math λͺ¨λ“ˆλ„ 아무 문제 없이 μ»΄νŒŒμΌλ˜μ§€λ§Œ, true인 κ²½μš°μ—λŠ” ν•΄λ‹Ή λͺ¨λ“ˆμ„ 찾을 수 μ—†λ‹€λŠ” μ—λŸ¬κ°€ λ°œμƒν•˜κ²Œ λœλ‹€.

    src/index.ts:1:21 - error TS2307: Cannot find module './utils/math' or its corresponding type declarations.
    
    1 import { add } from './utils/math';

    즉, λ°˜λ“œμ‹œ λͺ…μ‹œμ μœΌλ‘œ includeλ‚˜ files ν•„λ“œμ— μ„ μ–Έλœ λͺ¨λ“ˆλ“€λ§Œ μ»΄νŒŒμΌμ„ ν•˜κ³  μžˆλŠ” 것이닀. 이와 λ§ˆμ°¬κ°€μ§€ 이유둜 /// <reference path="..." /> 같이 λ””λ ‰ν‹°λΈŒλ₯Ό μ‚¬μš©ν•˜μ—¬ 뢈러온 λͺ¨λ“ˆλ“€λ„ ν•΄λ‹Ή ν•„λ“œμ— ν¬ν•¨λ˜μ–΄ μžˆμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 컴파일 λŒ€μƒμ—μ„œ μ œμ™Έλœλ‹€.

    noResolve μ˜΅μ…˜μ€ κ°œλ°œμžκ°€ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ 컴파일 λŒ€μƒμ„ λͺ…μ‹œμ μœΌλ‘œ μ„ μ–Έν•˜κ²Œ λ§Œλ“¦μœΌλ‘œμ¨ μˆ˜μ›”ν•œ λͺ¨λ“ˆ 관리λ₯Ό λ„μ™€μ£ΌκΈ°λŠ” ν•˜μ§€λ§Œ, λ””λ ‰ν‹°λΈŒλ₯Ό μ‚¬μš©ν•˜μ—¬ 뢈러온 λͺ¨λ“ˆκΉŒμ§€ 컴파일 λŒ€μƒμ—μ„œ μ œμ™Έν•œλ‹€λŠ” νŠΉμ„± λ•Œλ¬Έμ— λ””λ ‰ν‹°λΈŒλ₯Ό μ‚¬μš©ν•˜μ—¬ νƒ€μž… μ •μ˜ νŒŒμΌμ„ λΆˆλŸ¬μ˜€λŠ” NextJS 같은 라이브러리λ₯Ό μ‚¬μš©ν•˜κ³  μžˆλŠ” μƒν™©μ—μ„œλŠ” κ·Έλƒ₯ κΈ°λ³Έ 값인 false둜 μ‚¬μš©ν•˜λŠ” 것이 μ •μ‹  건강에 이둭닀.

    resolveJsonModule

    κ°’ μ„€λͺ…
    false (default) *.json ν™•μž₯자둜 λλ‚˜λŠ” λͺ¨λ“ˆμ˜ importλ₯Ό ν—ˆμš©ν•˜μ§€ μ•ŠλŠ”λ‹€.
    true *.json ν™•μž₯자둜 λλ‚˜λŠ” λͺ¨λ“ˆμ˜ importλ₯Ό ν—ˆμš©ν•œλ‹€.

    resolveJsonModule μ˜΅μ…˜μ€ 이름 κ·ΈλŒ€λ‘œ JSON 파일둜 κ΅¬ν˜„λœ λͺ¨λ“ˆμ„ λŒμ–΄λ‹€ μ“Έ 수 있게 ν—ˆμš©ν•  것인지에 λŒ€ν•œ μ—¬λΆ€λ₯Ό κ²°μ •ν•œλ‹€.

    λ§Œμ•½ ν•΄λ‹Ή μ˜΅μ…˜μ΄ false라면, JSON λͺ¨λ“ˆμ„ 가져왔을 λ•Œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” ν•΄λ‹Ή λͺ¨λ“ˆμ„ 찾을 수 μ—†λ‹€λŠ” μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¨λ‹€.

    // me.json
    {
      "name": "evan-moon",
      "age": 12,
      "role": "Frontend Engineer"
    }
    import me from './me.json';
    // Cannot find module './settings.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.

    resolveJsonModule μ˜΅μ…˜μ„ 켜게 되면 일반적인 νƒ€μž…μŠ€ν¬λ¦½νŠΈ λͺ¨λ“ˆκ³Ό λ™μΌν•˜κ²Œ JSON λͺ¨λ“ˆμ„ κ°€μ Έμ™€μ„œ μ‚¬μš©ν•  수 있게 되고, 심지어 ν•΄λ‹Ή νŒŒμΌμ„ λΆ„μ„ν•˜μ—¬ μžλ™μœΌλ‘œ νƒ€μž… μΆ”λ‘ κΉŒμ§€ ν•΄μ€€λ‹€.

    ν•˜μ§€λ§Œ 이 경우 λ‹Ήμ—°νžˆ Enumμ΄λ‚˜ Union Type을 μ‚¬μš©ν•œ 좔둠은 λΆˆκ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ— λͺ¨λ“  값듀은 stringμ΄λ‚˜ number λ“±μ˜ μ›μ‹œ νƒ€μž…μœΌλ‘œ μΆ”λ‘ λœλ‹€. κ·ΈλŸ¬λ‹ˆ λ§Œμ•½ κ°•λ ₯ν•œ νƒ€μž… 선언을 κ°•μ œν•΄μ•Όν•˜λŠ” 경우라면 JSON λͺ¨λ“ˆμ΄ μ•„λ‹ˆλΌ νƒ€μž…μŠ€ν¬λ¦½νŠΈ λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜μ—¬ λͺ¨λΈμ„ μ„ μ–Έν•΄μ£ΌλŠ” 것이 μ’‹λ‹€.

    rootDir

    rootDir μ˜΅μ…˜μ€ λͺ¨λ“ˆμ„ 컴파일 ν•œ 이후 μ–΄λ–€ 디렉토리λ₯Ό 루트둜 ν•˜μ—¬ ν˜„μž¬ ꡬ쑰λ₯Ό μœ μ§€ν•  것 인지λ₯Ό κ²°μ •ν•œλ‹€.

    기본적으둜 νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” μ»΄νŒŒμΌμ„ μˆ˜ν–‰ν•  λ•Œ μž…λ ₯된 λ””λ ‰ν† λ¦¬μ˜ ꡬ쑰λ₯Ό κ·ΈλŒ€λ‘œ μœ μ§€ν•˜λ©° 컴파일된 νŒŒμΌλ“€μ„ 좜λ ₯ν•˜λŠ”λ°, μ΄λ•Œ rootDir μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ—¬ μ–΄λ–€ 디렉토리λ₯Ό 루트둜 μ„€μ •ν•  것인지λ₯Ό μ •ν•˜λŠ” 것이닀.

    λ§Œμ•½ 이 μ˜΅μ…˜μ„ λ”°λ‘œ μ„€μ •ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄, νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” μžλ™μœΌλ‘œ ν•΄λ‹Ή λͺ¨λ“ˆμ˜ μ—”νŠΈλ¦¬ ν¬μΈνŠΈκ°€ λ˜λŠ” νŒŒμΌμ„ μ°Ύκ³ , ν•΄λ‹Ή 파일이 μœ„μΉ˜ν•œ 디렉토리λ₯Ό 루트둜 μ„€μ •ν•˜μ—¬ 좜λ ₯ 디렉토리 ꡬ쑰λ₯Ό μ„€μ •ν•˜κ²Œ λœλ‹€.

    myProject
    β”œβ”€β”€ src
    β”‚   β”œβ”€β”€ index.ts
    β”‚   └── utils
    β”‚       └── math.ts
    └── tsconfig.json
    // tsconfig.json
    {
      "compilerOptions": {
        "outDir": "./dist"
      }
    }

    μœ„μ™€ 같은 ꡬ쑰의 ν”„λ‘œμ νŠΈκ°€ μžˆλ‹€κ³  μƒκ°ν•΄λ³΄μž. rootDir μ˜΅μ…˜μ΄ 주어지지 μ•Šμ•˜μ„ λ•Œμ˜ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 이 μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ—”νŠΈλ¦¬ 포인트인 src/index.tsλ₯Ό μ°Ύμ•„λ‚΄κ³ , 이 파일의 μœ„μΉ˜μΈ srcλ₯Ό 루트둜 μΈμ‹ν•˜κ²Œ λœλ‹€.

    κ·Έλ ‡κΈ° λ•Œλ¬Έμ— 좜λ ₯ λ””λ ‰ν† λ¦¬λŠ” 루트λ₯Ό src λ””λ ‰ν† λ¦¬λ‘œ ν•˜λŠ” λ‹€μŒκ³Ό 같은 ꡬ쑰λ₯Ό κ°€μ§€κ²Œ λœλ‹€.

    dist
    β”œβ”€β”€ index.ts
    └── utils
        └── math.ts

    κ·ΈλŸ¬λ‚˜ λ§Œμ•½ 이 μƒνƒœμ—μ„œ rootDir μ˜΅μ…˜μ„ ν˜„μž¬ 경둜λ₯Ό μ˜λ―Έν•˜λŠ” .둜 μ„€μ •ν•˜κ²Œ 되면, 좜λ ₯ λ””λ ‰ν† λ¦¬μ˜ ꡬ쑰가 λ³€κ²½λ˜κ²Œ λœλ‹€.

    // tsconfig.json
    {
      "compilerOptions": {
        "outDir": "./dist",
        "rootDir": "."
      }
    }
    dist
    └── src
        β”œβ”€β”€ index.ts
        └── utils
            └── math.ts

    ν•„μžκ°€ 루트 디렉토리λ₯Ό ν˜„μž¬ tsconfig.json이 μœ„μΉ˜ν•œ myProject λ””λ ‰ν† λ¦¬λ‘œ λ³€κ²½ν–ˆκΈ° λ•Œλ¬Έμ—, 좜λ ₯ 디렉토리인 distλŠ” κΈ°μ‘΄ ν”„λ‘œμ νŠΈ 디렉토리와 μ™„μ „νžˆ λ™μΌν•œ ꡬ쑰λ₯Ό κ°€μ§€κ²Œ 되고, 이둜 인해 디렉토리 내뢀에 src λ””λ ‰ν† λ¦¬κΉŒμ§€ ν•¨κ»˜ μƒμ„±λœ ν˜•νƒœλ‘œ 좜λ ₯되게 λœλ‹€.

    μ–΄μ°Œλ³΄λ©΄ ꡉμž₯히 κ°„λ‹¨ν•œ λ™μž‘μ΄μ§€λ§Œ, 이 μ˜΅μ…˜μ„ μ‚¬μš©ν•  λ•ŒλŠ” ν•œ 가지 μ£Όμ˜ν•΄μ•Ό ν•  점이 μžˆλ‹€. λ°”λ‘œ rootDir μ˜΅μ…˜μ€ 컴파일 λŒ€μƒμ— μ•„λ¬΄λŸ° 영ν–₯을 λΌμΉ˜μ§€ μ•ŠλŠ”λ‹€λŠ” 것이닀.

    즉, λ§Œμ•½ rootDir μ˜΅μ…˜μ„ μ‚¬μš©ν•œλ‹€λ©΄ λͺ¨λ“  컴파일 λŒ€μƒ νŒŒμΌμ€ ν•΄λ‹Ή 디렉토리 밑에 μœ„μΉ˜ν•΄μ•Ό ν•œλ‹€λŠ” 것이닀.

    myProject
    β”œβ”€β”€ src
    β”‚   β”œβ”€β”€ index.ts
    β”‚   └── utils
    β”‚       └── math.ts
    β”œβ”€β”€ foo.ts
    └── tsconfig.json
    {
      "compilerOptions": {
        "outDir": "./dist",
        "rootDir": "./src",
        "include": "*"
      }
    }

    μœ„ 섀정을 μ‚΄νŽ΄λ³΄λ©΄ 루트 λ””λ ‰ν† λ¦¬λ‘œ src 디렉토리λ₯Ό μ„€μ •ν•˜κ³ , include μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ—¬ λͺ¨λ“  νŒŒμΌμ„ 컴파일 ν•  것이라고 μ„€μ •ν•΄μ£Όμ—ˆλ‹€.

    λ¬Έμ œλŠ” 이 β€œλͺ¨λ“  νŒŒμΌβ€μ˜ λŒ€μƒ 쀑 ν•˜λ‚˜μΈ foo.tsλŠ” src 디렉토리에 λ“€μ–΄κ°€ μžˆμ§€ μ•Šμ€ λ…€μ„μ΄λΌλŠ” 것이닀. 즉, 섀정에 λͺ¨μˆœμ΄ λ°œμƒν•œ 것이닀.

    μ΄λ ‡κ²Œ rootDir μ˜΅μ…˜μœΌλ‘œ 루트 디렉토리λ₯Ό μ •ν–ˆλ‹€κ³  해도, νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” μžλ™μœΌλ‘œ foo.tsλ₯Ό 컴파일 λŒ€μƒμ— ν¬ν•¨μ‹œν‚€μ§€ μ•Šκ³ , 이런 μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¨λ‹€.

    File β€˜/Users/john/myProject/foo.ts’ is not under β€˜rootDir’ β€˜/Users/john/myProejct/src’. β€˜rootDir’ is expected to contain all source files.

    이 μ—λŸ¬λ₯Ό 보면 μ•Œ 수 μžˆλ“―μ΄, rootDir μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ—¬ 루트 디렉토리λ₯Ό μ„€μ •ν–ˆλ‹€λ©΄ λ°˜λ“œμ‹œ λͺ¨λ“  μ†ŒμŠ€ νŒŒμΌλ“€μ€ 루트 디렉토리 내뢀에 λ“€μ–΄μžˆμ–΄μ•Ό ν•˜λ©°, λ§Œμ•½ include 등을 μ‚¬μš©ν•˜μ—¬ 루트 디렉토리 밖에 μžˆλŠ” νŒŒμΌμ„ 컴파일 λŒ€μƒμœΌλ‘œ μ§€μ •ν–ˆλ‹€κ³  해도 μžλ™μœΌλ‘œ 컴파일 ν•΄μ£Όκ±°λ‚˜ ν•˜μ§€ μ•ŠλŠ”λ‹€.

    rootDirs

    rootDirs μ˜΅μ…˜μ€ μΌμ’…μ˜ 가상 루트λ₯Ό λ§Œλ“€μ–΄ 쀄 수 μžˆλŠ” μ˜΅μ…˜μ΄λ‹€. 이 μ˜΅μ…˜μ€ 말둜 μ„€λͺ…ν•˜κΈ° 보닀 μ½”λ“œλ‘œ λ³΄λŠ” 것이 훨씬 이해가 νŽΈν•˜λ‹ˆ, λ°”λ‘œ μ˜ˆμ‹œλ₯Ό 보도둝 ν•˜μž.

    myProject
    β”œβ”€β”€ core
    β”‚   └── index.ts
    β”œβ”€β”€ utils
    β”‚   └── math.ts
    └── tsconfig.json

    λ§Œμ•½ 이런 ꡬ쑰의 μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μžˆλ‹€κ³  μƒκ°ν•΄λ³΄μž. λ§Œμ•½ core/index.tsμ—μ„œ utils/math.ts λͺ¨λ“ˆμ„ κ°€μ Έμ˜€κ³  μ‹Άλ‹€λ©΄ μ–΄λ–»κ²Œ ν•΄μ•Όν• κΉŒ?

    λ§Œμ•½ paths μ˜΅μ…˜μ„ μ‚¬μš©ν•˜μ§€ μ•Šμ•˜λ‹€λ©΄, μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•˜μ—¬ ν•œ 단계 μƒμœ„ λ””λ ‰ν† λ¦¬λ‘œ 거슬러 μ˜¬λΌκ°€μ„œ ν•΄λ‹Ή λͺ¨λ“ˆμ— μ ‘κ·Όν•  것이닀.

    // core/index.ts
    import math from '../utils/math';

    rootDirs μ˜΅μ…˜μ€ 이런 상황일 λ•Œ κ°€μƒμ˜ 루트λ₯Ό λ§Œλ“€μ–΄μ„œ, core 디렉토리와 utils 디렉토리 내뢀에 μžˆλŠ” λͺ¨λ“ˆλ“€μ΄ 마치 β€œν•˜λ‚˜μ˜ 디렉토리” 내뢀에 μžˆλŠ” κ²ƒμ²˜λŸΌ μ‚¬μš©ν•  수 μžˆλ„λ‘ λ§Œλ“€μ–΄μ€€λ‹€.

    {
      "compilerOptions": {
        "outDir": "./dist",
        "rootDirs": ["core", "utils"]
      }
    }
    // core/index.ts
    import math from './utils/math'; // 마치 같은 디렉토리에 μžˆλŠ” κ²ƒμ²˜λŸΌ μ‚¬μš©ν•œλ‹€

    λ§Œμ•½ core/components/Foo/index.tsx와 같이 디렉토리 κΉŠμ΄κ°€ κΉŠλ‹€κ³  해도 rootDirs에 ν•΄λ‹Ή 디렉토리λ₯Ό λ“±λ‘ν•˜κ²Œ 되면, rootDirs에 λ“±λ‘λœ 디렉토리 λΌλ¦¬λŠ” 항상 같은 디렉토리에 μžˆλŠ” κ²ƒμ²˜λŸΌ λͺ¨λ“ˆμ„ 뢈러올 수 μžˆλ‹€.

    그리고 rootDirs μ˜΅μ…˜μ€ μΌμ’…μ˜ β€œκ°€μƒ 디렉토리”λ₯Ό λ§Œλ“€μ–΄μ„œ 이런 κΈ°λŠ₯을 κ΅¬ν˜„ν•˜λŠ” 방식을 μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ—, μ»΄νŒŒμΌν•œ μ΄ν›„μ˜ 좜λ ₯ 디렉토리 κ΅¬μ‘°μ—λŠ” μ „ν˜€ 영ν–₯을 주지 μ•ŠλŠ”λ‹€. 말 κ·ΈλŒ€λ‘œ 가상이닀.

    이처럼 rootDirs μ˜΅μ…˜μ€ λ””λ ‰ν† λ¦¬μ˜ κΉŠμ΄κ°€ κΉŠμ€ 상황에도 κ°„λ‹¨ν•˜κ²Œ μƒλŒ€ 경둜λ₯Ό μ‚¬μš©ν•  수 μžˆλ„λ‘ λ§Œλ“€μ–΄ μ£ΌκΈ° λ•Œλ¬Έμ— μ–΄μ°Œ 보면 νŽΈν•˜λ‹€κ³  생각할 μˆ˜λ„ μžˆλ‹€.

    ν•˜μ§€λ§Œ 이런 섀정을 μ‚¬μš©ν•˜κ²Œ 되면 μ‹€μ œ 디렉토리 ꡬ쑰와 μ½”λ“œμ—μ„œ λͺ¨λ“ˆμ— μ ‘κ·Όν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” 경둜 κ°„μ˜ 괴리가 λ°œμƒν•˜κ²Œ 됨으둜써 직관적인 이해가 μ–΄λ €μš΄ μ½”λ“œκ°€ 될 μˆ˜λ„ 있으며, 심지어 이 괴리의 원인을 ν™•μΈν•˜κΈ° μœ„ν•΄μ„œλŠ” tsconfig.json을 κΉŒλ΄μ•Ό ν•˜λŠ” μŠ¬ν”ˆ 상황이 λ°œμƒν•  μˆ˜λ„ μžˆλ‹€λŠ” 점을 κΌ­ 염두에 두도둝 ν•˜μž.

    typeRoots

    기본적으둜 νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” @types νŒ¨ν‚€μ§€ 디렉토리 밑에 μžˆλŠ” νŒŒμΌλ“€μ„ μžλ™μœΌλ‘œ 컴파일 λŒ€μƒμœΌλ‘œ ν¬ν•¨ν•œλ‹€. μ΄λ•Œ μ•žμ„œ μ„€λͺ…ν–ˆλ˜ resolve μ „λž΅μ— 따라 ./node_modules/@types, ../node_modules/@types λ“± 디렉토리λ₯Ό 거슬러 μ˜¬λΌκ°€λ©΄μ„œ node_modules 내뢀에 μžˆλŠ” @types 디렉토리λ₯Ό νƒμƒ‰ν•˜λŠ” 것이닀.

    ν•˜μ§€λ§Œ typeRoots μ˜΅μ…˜μ„ μ‚¬μš©ν•˜λ©΄ νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ μ°Ύμ•„ ν—€λ§€λŠ” νƒ€μž… νŒŒμΌλ“€μ΄ νŠΉμ •ν•œ 곳에 μžˆλ‹€κ³  지정할 수 μžˆλ‹€.

    {
      "compilerOptions": {
        "typeRoots": ["./typings", "./node_modules/@types"]
      }
    }

    typeRoots μ˜΅μ…˜μ— μ μš©ν•˜λŠ” κ²½λ‘œλŠ” tsconfig.json κΈ°μ€€μ˜ μƒλŒ€ κ²½λ‘œμ΄λ‹€. λ˜ν•œ μœ„ μ˜ˆμ‹œμ—μ„œλŠ” ./node_modules/@types 디렉토리λ₯Ό μ˜΅μ…˜μ— ν¬ν•¨μ‹œμΌ°μ§€λ§Œ, 사싀 없어도 아무 문제 μ—†λ‹€.

    μ΄λ ‡κ²Œ typeRootsλ₯Ό μ§€μ •ν•œ 경우, νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 기쑴의 λͺ¨λ“ˆ 탐색 μ „λž΅μ„ 버리고 배열에 λ“€μ–΄μžˆλŠ” κ²½λ‘œμ—μ„œλ§Œ νƒ€μž… μ„ μ–Έ λͺ¨λ“ˆλ“€μ„ μ°ΎκΈ° λ•Œλ¬Έμ—, 계속 λΆ€λͺ¨ λ””λ ‰ν† λ¦¬λ‘œ 거슬러 μ˜¬λΌκ°€λ©° νƒ€μž… μ„ μ–Έ λͺ¨λ“ˆμ„ μ°ΎλŠ” 기쑴의 λͺ¨λ“ˆ 탐색 μ „λž΅λ³΄λ‹€ 효율적인 탐색 μ „λž΅μ„ κ°€μ Έκ°ˆ 수 μžˆλ‹€.

    types

    μ•žμ„œ μ„€λͺ…ν–ˆλ“―이 νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” @types νŒ¨ν‚€μ§€ 디렉토리 밑에 μžˆλŠ” λͺ¨λ“  νŒŒμΌλ“€μ„ μžλ™μœΌλ‘œ 컴파일 λŒ€μƒμœΌλ‘œ ν¬ν•¨ν•˜κ³ , 이 κ³Όμ •μ—μ„œ νƒ€μž… 선언을 μ „μ—­ μŠ€μ½”ν”„μ— λΏŒλ €λ²„λ¦°λ‹€.

    이 νƒ€μž… 선언이 μ „μ—­ μŠ€μ½”ν”„μ— μ‘΄μž¬ν•˜κΈ° λ•Œλ¬Έμ— μš°λ¦¬κ°€ @types/node 같은 λͺ¨λ“ˆ 내뢀에 ν¬ν•¨λœ process 객체 같은 녀석듀을 λ³„λ„μ˜ νƒ€μž… μ„ μ–Έ 없이도 μ‚¬μš©ν•  수 μžˆλŠ” 것이닀.

    ν•˜μ§€λ§Œ types μ˜΅μ…˜μ„ μ‚¬μš©ν•˜λ©΄, νŠΉμ •ν•œ νŒ¨ν‚€μ§€λ“€μ˜ νƒ€μž…λ§Œ μ „μ—­ μŠ€μ½”ν”„μ— ν¬ν•¨μ‹œν‚¬ 수 μžˆλ‹€.

    {
      "compilerOptions": {
        // @types/node, @types/jest, @types/express만 κ°€μ Έμ˜¨λ‹€
        "types": ["node", "jest", "express"]
      }
    }

    μœ„μ™€ 같이 μ„€μ •ν•  경우, node, jest, express νŒ¨ν‚€μ§€μ˜ νƒ€μž…μ€ μ „μ—­ μŠ€μ½”ν”„μ— ν¬ν•¨λ˜μ–΄ import express from 'express';λΌλŠ” ꡬ문만 적어도 μžλ™μœΌλ‘œ νƒ€μž… 평가가 μ§„ν–‰λ˜μ§€λ§Œ, 여기에 ν¬ν•¨λ˜μ§€ μ•Šμ€ λ‹€λ₯Έ λΌμ΄λΈŒλŸ¬λ¦¬λ“€μ€ 직접 νƒ€μž… μ„ μ–Έ λͺ¨λ“ˆμ„ 가져와야 ν•œλ‹€.

    μ—¬κΈ°μ„œ μ£Όμ˜ν•΄μ•Όν•  점은 types μ˜΅μ…˜μ˜ λŒ€μƒ μžμ²΄κ°€ μ• μ΄ˆμ— @types νŒ¨ν‚€μ§€ 디렉토리 내뢀에 μ‘΄μž¬ν•˜λŠ” νƒ€μž… μ„ μ–Έ λͺ¨λ“ˆλ“€μ΄λΌλŠ” 것이닀.

    예λ₯Ό λ“€μ–΄ λ‚ μ§œ κ΄€λ ¨ 라이브러리인 momentλŠ” @types/moment 같은 νƒ€μž… νŒ¨ν‚€μ§€λ₯Ό μΆ”κ°€μ μœΌλ‘œ μ„€μΉ˜ν•˜μ§€ μ•Šκ³ , 자체적으둜 λ‚΄μž₯ν•˜κ³  μžˆλŠ” νƒ€μž… μ„ μ–Έ λͺ¨λ“ˆμ„ μ‚¬μš©ν•œλ‹€.

    {
        "name": "moment",
        // ...
        "main": "./moment.js",
        "jsnext:main": "./dist/moment.js",
        "typings": "./moment.d.ts",
        // 자체적으둜 νƒ€μž… μ„ μ–Έ νŒŒμΌμ„ ν¬ν•¨ν•˜κ³  μžˆλ‹€
    }

    이런 κ²½μš°λŠ” import moment from 'moment' ꡬ문으둜 이 라이브러리λ₯Ό κ°€μ Έμ˜΄κ³Ό λ™μ‹œμ— moment.d.ts도 μžλ™μœΌλ‘œ 컴파일 λŒ€μƒμœΌλ‘œ ν¬ν•¨λ˜λ―€λ‘œ, λ‹Ήμ—°νžˆ μ œλŒ€λ‘œ νƒ€μž…μ„ μ‚¬μš©ν•  수 μžˆλ‹€.

    즉, types μ˜΅μ…˜μ˜ λŒ€μƒμ€ μ–΄λ””κΉŒμ§€λ‚˜ @types/* νŒ¨ν‚€μ§€ 내뢀에 ν¬ν•¨λœ νƒ€μž… μ„ μ–Έ λͺ¨λ“ˆμ΄κ³ , νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ ν•΄λ‹Ή νƒ€μž… μ„ μ–Έ λͺ¨λ“ˆμ„ μ „μ—­ 곡간에 λΏŒλ¦¬λŠ” κ²½μš°μ—λ§Œ ν•΄λ‹Ήλœλ‹€λŠ” 점을 ν—·κ°ˆλ¦¬μ§€ 말자.

    마치며

    μ΄λ ‡κ²Œ tsconfig 3번째 μ‹œλ¦¬μ¦ˆμΈ Modules νŽΈμ„ 마무리 ν–ˆλ‹€. λͺ¨λ“ˆκ³Ό κ΄€λ ¨λœ μ˜΅μ…˜λ“€μ˜ 개수 μžμ²΄λŠ” λ§Žμ§€ μ•Šμ§€λ§Œ, μ•„λ¬΄λž˜λ„ 컴파일 κ³Όμ •μ—μ„œ μ–΄λ–€ λͺ¨λ“ˆ μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•  지, μ–΄λ–€ λͺ¨λ“ˆ 탐색 μ „λž΅μ„ μ‚¬μš©ν•  지 등을 λ‹€λ£¨λŠ” μ˜΅μ…˜μ΄λ‹€λ³΄λ‹ˆ λΆ€μ—° μ„€λͺ…이 길어진 것 κ°™λ‹€.

    그리고 μ—¬κΈ°κΉŒμ§€ 적고 λ‚˜μ„œ μƒˆμ‚ΌμŠ€λŸ½κ²Œ λ“œλŠ” 생각은…

    tsconfig 어...? 아직도 여기까지밖에 못 썼다고...?

    사싀 tsconfig μ˜΅μ…˜μ΄ λ§Žμ€ 쀄은 이미 μ•Œκ³  μžˆμ—ˆμ§€λ§Œ μ΄λ ‡κ²ŒκΉŒμ§€ νž˜λ“€ 쀄은 상상도 λͺ» ν–ˆλ‹€. (κΈ€ μ“°λ‹€κ°€ 손λͺ©μ΄ μ•„ν”ˆ 적은 또 μ²˜μŒβ€¦)

    λ¬Όλ‘  곡식 λ¬Έμ„œλ₯Ό λ²ˆμ—­ν•˜λŠ” λŠλ‚ŒμœΌλ‘œ μ­‰μ­‰ 써내렀간닀면 금방 λλ‚΄κ² μ§€λ§Œ, μ• μ΄ˆμ— 이 ν¬μŠ€νŒ… μ‹œλ¦¬μ¦ˆλ₯Ό μ‹œμž‘ν•œ 것은 κ·Έ μ •λ„μ˜ 정보λ₯Ό μ›ν•΄μ„œκ°€ μ•„λ‹ˆλΌ tsconfigλ₯Ό μ™„μ „ λΆ„μ„ν•΄λ³΄μžλŠ” λͺ©μ μ΄μ—ˆμœΌλ―€λ‘œ ν•œλ²ˆ 달렀보도둝 ν•˜κ² λ‹€.

    λ‹€μŒ ν¬μŠ€νŒ…μ—μ„œλŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ 좜λ ₯ νŒŒμΌμ„ 생성할 λ•Œμ˜ λ™μž‘λ“€μ„ λ‹€λ£¨λŠ” 방법에 λŒ€ν•œ μ˜΅μ…˜λ“€μ— λŒ€ν•΄μ„œ 이야기해볼 μ˜ˆμ •μ΄λ‹€.

    μ΄μƒμœΌλ‘œ [tsconfig의 λͺ¨λ“  것] Compiler options / Modules ν¬μŠ€νŒ…μ„ λ§ˆμΉœλ‹€.

    Evan Moon

    🐒 거뢁이처럼 μ‚΄μž

    κ°œλ°œμ„ μž˜ν•˜κΈ° μœ„ν•΄μ„œκ°€ μ•„λ‹Œ κ°œλ°œμ„ 즐기기 μœ„ν•΄ λ…Έλ ₯ν•˜λŠ” κ°œλ°œμžμž…λ‹ˆλ‹€. μ‚¬μ†Œν•œ 생각 정리뢀터 νŠœν† λ¦¬μ–Ό, μ‚½μ§ˆκΈ° 정도λ₯Ό 주둜 끄적이고 μžˆμŠ΅λ‹ˆλ‹€.