DEV Community

Chan
Chan

Posted on

yarn pnp 세팅하기

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
Enter fullscreen mode Exit fullscreen mode

stable version은 https://yarnpkg.com/ 에서 확인 가능합니다.

글 작성 시점 기준으로 yarn stable version이 4.12.0이므로 해당 버전으로 설치합니다.

mise use yarn@4.12.0
Enter fullscreen mode Exit fullscreen mode

yarn pnp 모드가 적용되어 있는지 확인합니다. yarn berry(yarn v2+)에서는 자동으로 설치되어 있습니다.

yarn config get nodeLinker
Enter fullscreen mode Exit fullscreen mode

만약 현재 설정이 'pnp'가 아니라 'node-modules'로 되어 있다면, 다음을 입력해서 nodeLinker로 바꿔줍시다.

yarn config set nodeLinker pnp
Enter fullscreen mode Exit fullscreen mode

다시 nodeLinker 세팅값을 확인해서 yarn pnp가 제대로 적용되어 있는지 확인합니다.

yarn config get nodeLinker
Enter fullscreen mode Exit fullscreen mode

.yarnrc.yml 파일에서 값이 잘 적용된 것도 볼 수 있습니다.

nodeLinker: pnp
Enter fullscreen mode Exit fullscreen mode

마이그레이션

  • 프로젝트에서 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가 생기는 원인

  1. yarn dev와 같이 개발 서버를 실행할 때는 tree shaking이 진행되지 않아, 사용되지 않는 export가 제거되지 않는다.
  2. 그 결과 개발 서버 url로 접속했을 때 브라우저는 import되는 모듈 파일을 하나씩 다운로드 받는다.
  3. http 1.1에서 브라우저가 동일 오리진에 보낼 수 있는 요청의 개수는 보통 6개로 정해져있어, 네트워크 병목이 발생한다.
  4. vite는 개발 서버 실행 단계에서 패키지의 파일을 하나로 묶어서 node_modules/.vite 디렉토리에 캐싱한다. 이를 pre bundling이라고 부른다.

사실상 node_modules라는 이름으로 의존성이 되고 있는 것이 아니라, 개발 서버 실행 과정에서 재사용되는 캐시 파일이 저장되고 있는 것이다. node_modules 디렉토리의 본래 용도는 의존성 저장 용도이기에, yarn pnp에서 node_modules 디렉토리가 존재한다는 것이 어색하다고 판단하여 캐싱되는 디렉토리를 옮겼다.

  1. vite.config.ts에 cacheDir 디렉토리를 .vite로 설정하고 .gitignore에 추가하였다.
export default defineConfig({
  plugins: [react()],
  cacheDir: ".vite",
});
Enter fullscreen mode Exit fullscreen mode
  1. .gitignore에 해당 디렉토리를 추가하여, git history에 등록되지 않도록 한다.
# vite
.vite
Enter fullscreen mode Exit fullscreen mode

실험

lodash-es를 설치하고 pre bundling 과정이 없다면 실제로 네트워크 요청이 무수히 날아가는지 확인해보았다.

  1. lodash-es를 의존성에 추가한다.
yarn add lodash-es
Enter fullscreen mode Exit fullscreen mode
  1. vite.config.ts에서 최적화 대상에 lodash-es를 제외한다.
export default defineConfig({
  plugins: [react()],
  cacheDir: ".vite",
  optimizeDeps: {
    exclude: ["lodash-es"],
  },
});
Enter fullscreen mode Exit fullscreen mode
  1. 개발 서버를 실행하고 url로 접속한다. 브라우저 개발자 도구의 네트워크 탭을 확인한다.
yarn dev
Enter fullscreen mode Exit fullscreen mode

결과

네트워크 탭1

600개가 넘는 요청이 보내졌다. 요청을 전부 처리하는데 800ms~1000ms가 걸렸다. 타임라인을 확인해보라.

이제 pre-bundling 옵션을 적용한 뒤에 결과를 확인하겠다. pre-bundling은 최적화를 꺼놓지 않는 이상 자동으로 실행되기에, (2)에서 진행한 과정을 모두 제거하고 결과를 확인하였다.

네트워크 탭2

4ms가 걸렸다. 모듈 다운로드 속도가 1000ms => 4ms로 250배가 빨라진 샘이다.

nodeLinker: node-modules로 실행할 경우 결과

pre-bundling을 껐을 경우 결과

Pre bundling을 껐을 경우 결과

timeline에서 확인했을 때 다운로드하는데 800ms~1000ms가 걸렸다.

pre-bundling을 켰을 경우 결과

pre bundling을 껐을 경우 결과

다운로드에서 20ms가 걸렸다. 40배~50배 다운로드 속도가 빨라졌다.

https://vite.dev/guide/dep-pre-bundling
https://vite.dev/guide/dep-pre-bundling#caching

https://yarnpkg.com/migration/pnp

Top comments (0)