yarn pnp를 사용하는 이유
ghost dependencies의 방지
- yarn v1 및 npm은 패키지의 중복 다운로드를 방지하기 위해서, 중복되는 패키지는 node_modules에서 끌어올리는 hoisting을 진행합니다.
- intellisense에서 프로젝트 자체의 package.json에 존재하지 않는 package까지 import할 수 있게 되면서, 의존성 관리에 실패하게 됩니다.
- yarn pnp에서는 각 모듈의 경로를 lookup table에서 찾도록 설정해서 package.json에 존재하는 모듈만 import될 수 있도록 만듭니다.
module resolution시 무수한 I/O 발생
- build 또는 dev 과정에서, import를 호출하는 파일의 디렉토리부터 루트 디렉토리까지
/node_modules/[package]디렉토리 스캐닝을 진행합니다. - 무수한 I/O 스캐닝으로 개발 서버 시작 및 빌드가 느려집니다.
- yarn pnp에서는 node.js의 module resolution 알고리즘을 덮어씌우고 .pnp.cjs lookup table을 참고하여 개발 서버 시작 및 빌드 속도가 향상됩니다.
mise ls-remote yarn
stable version은 https://yarnpkg.com/ 에서 확인 가능합니다.
글 작성 시점 기준으로 yarn stable version이 4.12.0이므로 해당 버전으로 설치합니다.
mise use yarn@4.12.0
yarn pnp 모드가 적용되어 있는지 확인합니다. yarn berry(yarn v2+)에서는 자동으로 설치되어 있습니다.
yarn config get nodeLinker
만약 현재 설정이 'pnp'가 아니라 'node-modules'로 되어 있다면, 다음을 입력해서 nodeLinker로 바꿔줍시다.
yarn config set nodeLinker pnp
다시 nodeLinker 세팅값을 확인해서 yarn pnp가 제대로 적용되어 있는지 확인합니다.
yarn config get nodeLinker
.yarnrc.yml 파일에서 값이 잘 적용된 것도 볼 수 있습니다.
nodeLinker: pnp
마이그레이션
- 프로젝트에서 node_modules 폴더 완전히 제거하기
-
.bin폴더의 바이너리 실행 파일을 제거하기 -
node명령어 호출을yarn node로 모두 대체하기 - 명령어가 실행되기 전에 자동으로 끼워 넣는 훅은 모두 직접 호출하도록 바꾸기(
yarn prestart)
code editor와 연동하기
- ZipFS extension을 설치하면, zip으로 압축된 패키지 소스코드 내부를 확인할 수 있다.
-
yarn dlx @yarnpkg/sdks vscode를 사용하면, 프로젝트의 typescript를 사용하도록 만들어줄 수 있다. - 타입스크립트 파일이 포커스된 상태에서
Typescript: Select Typescript Version...을 클릭 >Use Workspace Version을 클릭한다.
vite project에서 yarn dev 실행시 node_modules가 생기는 원인
-
yarn dev와 같이 개발 서버를 실행할 때는 tree shaking이 진행되지 않아, 사용되지 않는 export가 제거되지 않는다. - 그 결과 개발 서버 url로 접속했을 때 브라우저는 import되는 모듈 파일을 하나씩 다운로드 받는다.
- http 1.1에서 브라우저가 동일 오리진에 보낼 수 있는 요청의 개수는 보통 6개로 정해져있어, 네트워크 병목이 발생한다.
- vite는 개발 서버 실행 단계에서 패키지의 파일을 하나로 묶어서 node_modules/.vite 디렉토리에 캐싱한다. 이를 pre bundling이라고 부른다.
사실상 node_modules라는 이름으로 의존성이 되고 있는 것이 아니라, 개발 서버 실행 과정에서 재사용되는 캐시 파일이 저장되고 있는 것이다. node_modules 디렉토리의 본래 용도는 의존성 저장 용도이기에, yarn pnp에서 node_modules 디렉토리가 존재한다는 것이 어색하다고 판단하여 캐싱되는 디렉토리를 옮겼다.
-
vite.config.ts에 cacheDir 디렉토리를.vite로 설정하고.gitignore에 추가하였다.
export default defineConfig({
plugins: [react()],
cacheDir: ".vite",
});
-
.gitignore에 해당 디렉토리를 추가하여, git history에 등록되지 않도록 한다.
# vite
.vite
실험
lodash-es를 설치하고 pre bundling 과정이 없다면 실제로 네트워크 요청이 무수히 날아가는지 확인해보았다.
-
lodash-es를 의존성에 추가한다.
yarn add lodash-es
- vite.config.ts에서 최적화 대상에
lodash-es를 제외한다.
export default defineConfig({
plugins: [react()],
cacheDir: ".vite",
optimizeDeps: {
exclude: ["lodash-es"],
},
});
- 개발 서버를 실행하고 url로 접속한다. 브라우저 개발자 도구의 네트워크 탭을 확인한다.
yarn dev
결과
600개가 넘는 요청이 보내졌다. 요청을 전부 처리하는데 800ms~1000ms가 걸렸다. 타임라인을 확인해보라.
이제 pre-bundling 옵션을 적용한 뒤에 결과를 확인하겠다. pre-bundling은 최적화를 꺼놓지 않는 이상 자동으로 실행되기에, (2)에서 진행한 과정을 모두 제거하고 결과를 확인하였다.
4ms가 걸렸다. 모듈 다운로드 속도가 1000ms => 4ms로 250배가 빨라진 샘이다.
nodeLinker: node-modules로 실행할 경우 결과
pre-bundling을 껐을 경우 결과
timeline에서 확인했을 때 다운로드하는데 800ms~1000ms가 걸렸다.
pre-bundling을 켰을 경우 결과
다운로드에서 20ms가 걸렸다. 40배~50배 다운로드 속도가 빨라졌다.
https://vite.dev/guide/dep-pre-bundling
https://vite.dev/guide/dep-pre-bundling#caching




Top comments (0)