yarn berry 적용기(1)

Donghoon Nam
원티드랩 기술 블로그
10 min readJul 6, 2021

안녕하세요. 원티드 프론트엔드 팀 남동훈 입니다.

현재 진행하고 있는 프로젝트들에 yarn v1을 v2로 업그레이드를 진행해보고 있습니다.

yarn berry관련해서는 시리즈로 진행해보려고 합니다.

1편 기존 npm, yarn v1의 이슈와 yarn v2에 대한 이야기
2편 실질적으로 프로젝트에 적용한 사례
3편 monorepo에 적용한 사례

현재 진행하고 적용한 프로젝트 관련된 사례를 얘기하기 전에 이전의 npm, yarn v1의 문제점에 대해 정리해보고 yarn v2는 이러한 이슈들을 어떻게 해결하고자 하는지 우선 정리해보고자 합니다.

npm과 yarn v1(classic)

https://engineering.fb.com/2016/10/11/web/yarn-a-new-package-manager-for-javascript/

기존 npm의 문제는 다음과 같이 정리해볼 수 있을거 같습니다.

위의 문제들을 해결하고자 2016년, Facebook을 중심으로 Google, Exponent, Tilde 회사들과 함께 기존에 있었던 npm 의 문제를 해결해보고자 새로운 패키지 매니저인 yarn을 발표하였습니다.

yarn v1(classic)의 장점은 다음과 같습니다.

  • yarn.lock파일 자동 생성, checksum을 통해 어느 환경에서든 동일한 패키지를 설치하게 함
  • 의존성 트리 이슈는 lock파일과 설치 알고리즘을 통해 개선
  • 패키지 병렬 설치를 통한 성능 개선
  • cache directory를 통한 오프라인 미러링과 left-pad 이슈 방지

마지막으로 npm과 yarn v1(classic)의 공통적인 문제점을 정리해보려고 합니다.

  • node_modules의 거대함과 depth의 문제

간단한 패키지를 몇 개 설치하지 않아도 node_modules의 depth와 용량은 엄청나게 커지게 됩니다.
더군다나 next.js등과 같이 다양한 기능을 제공하는 프레임워크, 라이브러리를 사용하게 된다면 최소 몇백메가에서 기가 단위의 디스크 용량을 사용하게 됩니다.
거기 더해서 depth는 점점 깊어지는 문제가 발생하게 되고, 이것은 설치, 삭제, 수정, 실행 등의 속도에 영향을 끼치게 되고
패키지의 내용은 확인하지 못하고 기본적인 의존성 트리만 검증하게 됩니다.

  • 유령 의존성(Phantom Dependency)
Package Hoisting

왼쪽 그림을 보면 package-1에서 B (1.0)패키지를 가져다 쓸 수 없는 구조지만
오른쪽 그림을 보면 Hoisted가 되면서 package-1이 B (1.0)을 설치한 적이 없지만 B (1.0)을 가져다 쓸 수 있게 됩니다.
이런 현상을 유령 의존성이라고 보시면 됩니다.

위와 같은 이슈가 발생시에는 package.json을 통해 설치 되지 않은 패키지를 사용할 수도 있고, 삭제할때도 잘 사용하던 패키지를 갑자기 사용할 수 없게 되기도 합니다. (왜 require, import를 하지 못하는지 한참 찾아야 하는 이슈가 발생하기도 했습니다.)

또한 설치했던 패키지들이 어디에 영향을 끼치는지 몰라서 삭제를 못하거나, 버전 업그레이드를 하기에는 너무 무서운 상황이 되곤 했습니다.

이런 다양한 문제점을 가지고 시간이 흘러가던 와중에 2018년 새로운 소식이 하나 나오게 됩니다.

yarn berry(v2)

yarn berry(v2)에서는 pnp라는 개념을 도입하여 아래와 같은 내용들을 해결해 줍니다.

node_moodules의 성능 문제

  • node_modules는 많은 양의 파일들을 포함하고 있습니다. 그렇다 보니 hot cache를 이용하여 yarn install을 사용하여 node_modules을 생성하는 것보다 70%이상의 시간이 걸립니다.
    이러한 복사 방식은 I/O bound를 사용하기 때문에 package manager는 이러한 복사 등에 대해 최적화 할 수 없습니다.

nodejs는 package라는 개념이 없음

  • nodejs는 어떤 파일들에 접근가능한지와 끌어올려지는지를 알 수가 없습니다.
    개발할때는 문제가 없던 코드가 프로덕션 형태일때는 package.json에 누락된 패키지 때문에 실패할 수도 있습니다.
    이는 유령 의존성과도 연관이 될 수 있습니다.

비효율적인 resolution

  • node resolution은 하나의 파일 의존성을 로드하기까지 readdir, stat과 같은 느린 I/O와 같은 system call를 반복적으로 호출합니다.

node_modules의 비효율적인 구조

  • node_modules의 다양한 구조는 실용적이지 못하며, 원하는 만큼의 중복제거를 할 수 없습니다.
    같은 이름의 다양한 버전을 가지고 있는 패키지들 때문에 완벽한 hoist를 보장할 수 없습니다.

Plug’n’Play (PnP)

yarn2에서는 비효율적인 node_modules대신에 .pnp.js라는 파일을 생성하게 됩니다.

.pnp.js는 다음과 같은 포맷을 가지게 됩니다.

패키지 이름 + 패키지 버전 + 패키지의 실제 위치를 담은 맵과, 패키지 이름 + 패키지 버전 + 패키지의 종속성을 담은 맵을 저장합니다.

코드는 다음과 같습니다.

위와 같은 방식은 아래와 같은 이점을 가지고 있습니다.

  1. .pnp.js파일 하나만 생성하게됨으로서 설치가 빨라집니다.
  2. disk I/O사용이 줄어들기 때문에 안정적으로 설치가 가능합니다. (특히 윈도우에서)
  3. 종속성 트리의 최적화 및 예측가능한 패키지 인스턴스화
  4. Zero-Installs의 부분으로서 repository에 .pnp.js를 commit할 수 있습니다. (다른 곳에서도 yarn install을 할 필요가 없어집니다.)
  5. 프로그램을 빠르게 시작할 수 있습니다. 위에서 얘기한 node resolution의 file system의 순회가 이전보다 줄어듭니다.
  6. 의존성 끌어올리기(hoisting)을 하지 않기 때문에 node_modules에 설치되었던 패키지를 사용할 수 있었던 부분이 제거 되었고(package.json에만 의존), 유령 의존성을 현상을 막을 수 있습니다.
  7. Yarn PnP에서는 Zip 파일을 이용하여 패키지를 관리하기 때문에 빠진 의존성을 찾거나 의존성 파일이 변경되었음을 찾기 쉽습니다. 이로써 의존성이 잘못되었을 때 쉽게 바로잡을 수 있습니다.

Zero-Installs

yarn pnp는 의존성 관리를 .yarn/cache 디렉토리내에 zip파일로 관리를 합니다.
또한 다른 환경에서도 별도의 yarn install을 통한 설치가 필요없도록 repository에 commit을 하도록 요구합니다.

(offline환경에서도 설치가 문제 없도록 함은 덤입니다.)
(Zero-Installs을 사용할지 아닐지에 따라 .gitignore내용도 제공해주고 있습니다.)

Zero-Installs를 사용하였을 때 기존 yarn v1을 사용하였을 때와의 차이점은 다음과 같습니다.

node_modules 는 1.2GB 크기이고 13만 5천개의 파일로 구성되어 있는 반면, Yarn PnP의 필요한 패키지들은 139MB 크기의 2천개의 압축 파일로 구성됩니다.

위의 용량 차이로 인하여 repository에 올리도록 하는 것에 대한 비효율에 대한 의문은 해결이 되었습니다.

Zero-Installs을 사용하여 원티드내 서비스의 CI의 배포 시간을 기존 7~8분에서 5~6분으로 줄일 수 있었습니다.

또한 기존 유령 의존성을 사용하던 패키지들을 엄격하게 발견하고 해당 패키지들을 재설치를 하였습니다.

npm, pnpm, yarn (nm), yarn등의 여러사례를 통한 benchmark

https://p.datadoghq.eu/sb/d2wdprp9uki7gfks-c562c42f4dfd0ade4885690fa719c818?from_ts=1622953350431&to_ts=1625545350431&live=true

yarn berry(v2)를 적용해보고자 하신다면 아래의 호환성 표를 참고해 주시기 바랍니다.

https://yarnpkg.com/features/pnp#compatibility-table

node_modules을 포기하실 수 없는 분들은 아래의 migration을 참고해주시면 될거 같습니다.

https://yarnpkg.com/getting-started/migration

editor 관련 sdk는 아래를 참고해 주시면 됩니다.

https://yarnpkg.com/getting-started/editor-sdks

그 외에 아직 사용해보지는 않았지만 충분히 활용할만한 다양한 기능들이 아래와 같이 있습니다.

  1. https://yarnpkg.com/features/plugins
  2. https://yarnpkg.com/features/protocols
  3. https://yarnpkg.com/features/release-workflow
  4. https://yarnpkg.com/features/workspaces
  5. https://yarnpkg.com/api/

yarn berry(v2)에는 매우 많은 기능들이 있다보니 짧은 기간동안 다 사용해보기는 어려웠습니다.

추후 monorepo로 관리되고 있는 원티드 패키지를 개선하면서 위의 내용들을 사용해봐야 할거 같습니다.

관련된 내용은 하나씩 정리해보고자 합니다.

다음 글에서는 yarn pnp를 적용해본 사례를 정리해보고자 합니다.

Referencce

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

https://www.bloter.net/newsView/blt201604040002

--

--