Resolver란 무엇인가?
다음 코드를 생각해 보겠습니다:
import Button from "@/components/Button";
JavaScript 엔진이 실제로 보는 것은 다음뿐입니다:
specifier = "@/components/Button"
그다음 해결해야 할 문제는 다음과 같습니다:
specifier
↓
真实模块
이 과정을 Resolution이라고 합니다.
Node.js ESM 명세에 정의된 진입점 알고리즘:
ESM_RESOLVE(specifier, parentURL)
이 알고리즘은 본질적으로 이 매핑 과정을 수행합니다.
Node가 기본적으로 지원하는 specifier
Node의 기본 Resolver는 네 가지 유형만 지원합니다:
경로 모듈입니다.
패키지 모듈입니다.
Package Imports 방식입니다.
URL 모듈입니다.
그 외에는 다음과 같습니다:
Node는 이를 해석할 수 없습니다.
대신 곧바로 다음 예외 경로로 들어갑니다:
Module Not Found
이 경로에서 Module Not Found 예외가 발생합니다.
Alias가 동작하는 이유
다음 코드가 있다고 가정해 보겠습니다:
Webpack 설정은 다음과 같습니다:
Webpack은 실제 모듈 해석을 수행하기 전에 먼저:
@/components/Button
Alias Resolver를 거칩니다:
/project/src/components/Button
그다음 일반적인 모듈 해석 단계로 넘어갑니다.
따라서 Alias는 새로운 모듈 유형이 아닙니다.
변환 과정은 다음과 같습니다:
旧 specifier
↓
新 specifier
한 번의 specifier 변환입니다.
Vite Alias
Vite 설정은 다음과 같습니다:
실행 흐름은 Webpack과 완전히 같습니다.
다음 코드를 생각해 보겠습니다:
Vite는 먼저 다음 변환을 수행합니다:
@ -> /project/src
다음 결과를 얻습니다:
/project/src/utils/request
그다음 후속 모듈 해석을 계속 진행합니다.
따라서 다음은:
Webpack Alias
Vite Alias
Rspack Alias
본질적으로 모두 같은 메커니즘입니다.
차이는 단순히 Resolver 구현이 다르다는 점입니다.
TypeScript Paths가 자주 동작하지 않는 이유
설정은 다음과 같습니다:
많은 사람이 다음과 같은 현상을 발견합니다:
VSCode 正常
TypeScript 正常
Node 运行失败
원인은 다음과 같습니다:
paths
paths는 런타임 Resolver가 아닙니다.
TypeScript가 paths를 사용하는 단계는 다음과 같습니다:
类型检查
IDE 跳转
编译阶段
이 세 단계가 TypeScript가 paths를 사용하는 전부입니다.
Node 런타임은 tsconfig.json을 전혀 읽지 않습니다:
따라서 다음과 같은 설정으로는:
Node가 다음 import를 인식할 수 없습니다:
별도로 추가할 구성은 다음과 같습니다:
Webpack Alias
Vite Alias
Rspack Alias
Node Loader
이 중 하나가 필요합니다.
Node Loader가 Alias를 구현하는 방법
Node는 다음 API를 제공합니다:
이 API를 사용하면 모듈 해석 과정을 가로챌 수 있습니다.
예를 들어:
이 경우 다음 import는:
다음과 같이 변환됩니다:
file:///project/src/utils.js
그다음 일반적인 모듈 로딩 과정으로 들어갑니다.
브라우저 Import Map
브라우저에는 다음이 없습니다:
node_modules
따라서 다음 import는:
작동하지 않습니다.
Import Map을 사용하면 다음과 같이 매핑할 수 있습니다:
다음 import를 실행할 때:
이때:
브라우저가 먼저 다음과 같이 매핑합니다:
react
↓
/vendor/react.js
그다음 로드를 계속 진행합니다.
Resolver의 본질
다음 항목들을 살펴보면:
Webpack Alias
Vite Alias
Rspack Alias
TS Paths
Node Loader
Import Map
모두 같은 작업을 수행한다는 것을 알 수 있습니다:
specifier
↓
mapping
↓
new specifier
↓
real URL
차이는 오직 매핑 규칙을 저장하는 위치에 있습니다:
映射规则存放的位置不同
모듈 그래프의 시작점
다음 코드를 생각해 보겠습니다:
컴파일러는 먼저 다음 과정을 수행합니다:
specifier
↓
resolver
↓
real module
다음과 같은 실제 모듈을 얻게 됩니다:
App.tsx
그다음 재귀적으로 계속 진행합니다:
App.tsx
↓
Button.tsx
↓
request.ts
↓
...
최종적으로 다음 모듈 그래프를 구성합니다:
Module Graph
Tree Shaking, Code Splitting, HMR, Chunk 분할은 모두 이 그래프를 기반으로 합니다.
따라서 Resolver는 현대적인 프론트엔드 도구 체인의 진정한 출발점입니다.