Hello,

kok nae-ga ha-myun an-dweneun MAGIC...🧚

웹 프로그래밍/공부일지

yarn berry 🍓 !

✿도담도담 2024. 2. 22. 18:45

이전에 npm과 yarn 중에 고민할때 대충 아아 음~ 그렇지 하고 넘겼다가

이제서야 기술 블로그에서 상세하게 설명한 글을 보고 머리를 한대 맞았다 🥺


 

패키지 관리 도구로 대표적으로 npm과 yarn이 있는데 그중에서도 최근 많이 사용중인 yarn berry 버전이

npm과 비교했을때 어떤 이점이 있는지 왜 인기가 많은지 알아보려고한다.

 

🤔 npm의 문제점

비효율적인 의존성 검색

npm은 node_modules 폴더를 통해 파일 시스템을 이용하는데,

node.js에서 제공하는 require.resolve.paths() 함수를 통해 react를 require했을 때

react를 찾기위해 어떤 폴더를 확인하는지 봤다.

$ node
Welcome to Node.js v20.5.1.
Type ".help" for more information.
> require.resolve.paths('react')
[
  '/Users/dodam/Documents/my-apps/dev/my-app/repl/node_modules',
  '/Users/dodam/Documents/my-apps/dev/my-app/node_modules',
  '/Users/dodam/Documents/my-apps/dev/node_modules',
  '/Users/dodam/Documents/my-apps/node_modules',
  '/Users/dodam/Documents/node_modules',
  '/Users/dodam/node_modules',
  '/Users/node_modules',
  '/node_modules',
  '/Users/dodam/.node_modules',
  '/Users/dodam/.node_libraries',
  '/opt/homebrew/Cellar/node/20.5.1/lib/node',
  '/Users/dodam/.node_modules',
  '/Users/dodam/.node_libraries',
  '/opt/homebrew/Cellar/node/20.5.1/lib/node'
]

 

npm은 해당 패키지를 찾지 못하니 계속해서 상위 폴더를 검색하고 있었다!

패키지를 찾지 못한다면 느린 I/O호출이 반복 되게 된다.

이런 비효율적인 탐색 때문에 TypeScript 4.0까지는 패키지를 처음으로 import 하기 전까지

node_modules 내부의 타입 정보를 찾아보지 않기도 했다. (TS 4.0 Changelog)

 

환경에 따라 달라지는 동작

위에서 설명한 이런 비효율적인 검색 때문에 따라오는 두번째 문제점이다.

만약 해당 패키지를 찾지 못했을 경우에 계속 상위 디렉토리의 node_modules를 찾아 떠날 것이고,

이는 어떤 버전의 패키지를 불러올지 모를뿐더러 우리가 같은 상황을 재현하기에 까다로워 진다.

 

 

비효율적인 설치

npm에서 node_modules는 많은 용량을 차지하고 트리 구조로 관리되어 의존성을 검사하려면

많은 I/O작업이 발생되어야 하며 이것은 결국 속도가 느려질 수 밖에 없다. 🥺

이부분의 설명이 나에겐 조금 부족해서 해서 더 찾아봤는데 아직 명쾌하게 발견하진 못했다.

우선 단순히 node_modules의 구조가 복잡해서 비효율적이라고 받아들이고 넘겼다.

다른 사람들의 경험들로 봤을때 npm 사용시 npm install을 했을때 시간이 오래 걸리는 듯 하다..!

 

 

유령 의존성 (Phantom Dependency)

npm및 yarn v1에서는 의존성 트리 평탄화(=호이스팅)을 통해 중복으로 설치 되는 패키지를 관리한다.

 

왼쪽의 의존성 트리에서 중복되는 A(1.0)과 B(1.0) 패키지를 호이스팅하여 (끌어올려잇!) 중복을 줄였다.

여기서 문제가 기존 package-1에서는 B(1.0) 패키지를 require 할수 없다!

하지만 트리 평탄화 작업을 거치면서 package-1이 B(1.0)패키지를 사용할 수 있게 되어 버렸다. 🫨

 

 


 

 

💡 해결해보자!

Plug'n'Play (PnP)

yarn berry는 위의 문제를 PnP를 통해 해결한다.

npm에서 yarn을 설치 후에 버전을 berry로 설정해주면 된다 :)

npm install -g yarn
cd ../path/to/some-package
yarn set version berry

 

사진과 같이 PnP는 node_modules 폴더를 생성하지 않는다.

대진에 .yarn/cache 폴더에 의존성의 정보가 저장되고, .pnp.cjs 파일에 의존성을 찾을 수 있는 정보가 기록된다.

pnp.cjs를 이용하면 디스크 I/O 없이 어떤 패키지가 어떤 라이브러리에 의존하는지,

각 라이브러리는 어디에 위치하는지를 바로 알 수 있다.

/* react 패키지 중에서 */
["react", [
  /* npm:17.0.1 버전은 */
  ["npm:17.0.1", {
    /* 이 위치에 있고 */
    "packageLocation": "./.yarn/cache/react-npm-17.0.1-98658812fc-a76d86ec97.zip/node_modules/react/",
    /* 이 의존성들을 참조한다. */
    "packageDependencies": [
      ["loose-envify", "npm:1.4.0"],
      ["object-assign", "npm:4.1.1"]
    ],
  }]
]],

 

+) 이 글을 작성하는 시점에서 나는 yarn 버전 4.1.0이 설치가 됐었는데 다른 사람들은 다 로컬에 .yarn/cache 폴더가 나타나는데...

(항상 나만 안되는 magic🥹) 나는 자꾸 글로벌 yarn 폴더에 설치가 됐다.

이건 .yarnrc.yml에 글로벌캐시 사용 비활성화 옵션 (enableGlobalCache)을 추가하여 해결했다.

 

 

ZipFS (Zip Filesystem)

 

위의 사진을 다시 가져왔다. 👋

Yarn PnP에서는 zip 아카이브로 관리된다. 내가 설치한 react 18.2.0이 react-npm-18.2.0-1eae08fee2-b562d9b569.zip로 압축 되어 있는 것을 확인 할 수 있다.

이렇게 zip 아카이브를 사용하면 다음과 같은 이점이 있다.

  1. 더 이상 node_modules 디렉토리 구조를 생성할 필요가 없기 때문에 설치가 신속히 완료.
  2. 각 패키지는 버전마다 하나의 Zip 아카이브만을 가지기 때문에 중복해서 설치되지 않음.
  3. 의존성을 구성하는 파일의 수가 많지 않으므로, 변경 사항을 감지하거나 전체 의존성을 삭제하는 작업이 빠름.

+) react만 설치했을 기준으로는 사실 node_modules의 폴더 용량이 더 작았다. 나중에 yarn으로 개인 프로젝트를 해보고 비교를 해봐야 정확히 알 수 있을 것 같다.

 


 

 

아직 제대로 사용해본 적이 없어서 참고글의 후기 같은 부분은 느껴보지 못했지만 git에서 버전관리를 하는 부분은 상당히 궁금하다.

Zero-install 부분인데 한번 체험해봐야겠다..!

 

 

# 참고글

https://toss.tech/article/node-modules-and-yarn-berry

https://0xffffffff.tistory.com/97