Nodejs

[Nodejs] 더 작고 빠른 패키지 매니징을 위한 yarn berry

Satisfaction 2023. 8. 12. 22:32

yarn classic

yarn classic(v1) 과 npm에서 의존성을 추가하면 node_modules 에 설치된다.

 

새로운 프로젝트를 생성하고, yarn init 으로 초기화한 후 yarn add react 를 이용해 react 의존성을 추가하면

node_modules에 react 패키지가 추가된 모습을 볼 수 있다.

 

그런데 분명 추가한 것은 react 하나뿐이지만, js-tokens 와 loose-envify 패키지도 존재하는 것을 확인할 수 있다.

 

node_modules/react/package.json의 dependencies를 살펴보면 loose-envify를 사용하고 있고,

node_modules/loose-envify/package.json의 dependencies를 살펴보면 js-tokens를 사용하고 있다. 

 

기본적으로 프로젝트에 추가된 의존성들은 node_modules에 설치되는데,

node_modules 내에 있는 패키지의 중복 설치를 막기 위해 의존성 호이스팅이 발생한다.

 

require() 했을 때 탐색 순서가 우선적으로는 상위 디렉터리 방향으로 정해지기 때문에

패키지 위치를 위쪽(node_modules/react/node_modules가 아닌 node_modules)으로 이동시키는 것이다.

 

 

require.resolve.path()를 이용해 require()를 사용해 의존성을 불러올 때 탐색 순서를 살펴볼 수 있다.

 

require('react')를 했을 때

/Users/namgung-geon/cyphers-supporter-user-web/repl/node_modules 에서 react가 있는지 찾고

없다면 /Users/namgung-geon/cyphers-supporter-user-web/node_modules 에서 react가 있는지 찾고

없다면 /Users/namgung-geon/node_modules에서 react가 있는지 찾고

....

마지막으로 없다면 /Users/namgung-geon/.nvm/version/node/v16.19.1/lib/node_modules에서 react가 있는지 찾는다는 것이다.

 

의존성 호이스팅이 node_modules의 크기는 줄여주고 있지만 

반대로 require 시 계속해서 module을 찾기 위해 비효율적인 Disk I/O를 발생시키고 있다.

 

더 작은 사이즈로 더 빨리 모듈을 찾을 수는 없을까?

 

yarn berry

yarn berry에서 등장한 pnp(plug and play) 개념에서는 의존성의 위치를 미리 지정하고

필요한 의존성 패키지들을 압축해서 저장한다.

 

따라서 yarn classic 에서 처럼 상위 디렉터리로 이동하면서 일일이 node_modules 를 뒤질 필요가 없다.

 

예컨데 다시 require('react')하는 상황을 재현해보면

이미 의존성의 위치를 알고 있으므로 한 번만 탐색하면 바로 찾을 수 있는 것이다.

 

이제 yarn berry와 pnp를 적용해 보자.

yarn set version berry

 

위 스크립트를 실행시키면 .yarn 폴더와 .yarnrc.yml 파일이 생성되었을 것이다.

nodeLinker: node_modules

yarnPath: .yarn/releases/yarn-3.6.1.cjs

.yarnrc.yml 파일에서 nodeLinker가 위와 같이 node_modules로 되어 있다면 pnp로 변경해 준다.

(pnp 모드에서는 의존성/버전 오류 등에 대해 좀 더 엄격한 듯 하다)

 

nodeLinker: pnp

yarnPath: .yarn/releases/yarn-3.6.1.cjs

이제 yarn 커맨드를 사용해 모듈을 설치해 준다.

 

이제 의존성 패키지들이 압축 파일로 모두 설치된다.

 

여기서 .pnp.cjs를 살펴보면

 

각 패키지들의 위치가 명시되어 있는 것을 확인할 수 있다.

 

따라서 이제 require가 더 빠르게 동작할 수 있게 되었다.

 

모듈이 압축 저장되기 때문에 사이즈도 크게 줄어들었다.

 

이런 방식을 사용했을 때 또 하나의 장점은 zero install이 가능해진다.

 

일례로 node_modules를 직접 설치해야만 하는 기존 방식에서는

새로운 환경(협업 환경 또는 CI 로 빌드/자동화 할 때)에서 실행할 때 마다 node_modules 를 모두 설치해야만 했다.

 

그러나 pnp방식을 사용할 경우 .yarn/cache에 작은 사이즈로 패키지를 저장하기 때문에 새로 설치할 필요가 없다.

 

vscode에서 비정상 작동할 경우

yarn dlx @yarnpkg/sdks vscode

pnp모드에서 일부 모듈(typescript, eslint, prettier 등)이 비정상 작동할 경우 위 명령어로 sdk를 설치한다.

 

Migration: yarn classic to yarn berry(pnp)

 

현재 프로젝트에서는 github action을 이용해 CI/CD를 구현해 두었다.

 

install modules (yarn install 실행) 에 30s 정도의 시간이 소요되고 있으며

이것은 매 액션 실행마다 낭비되는 시간이다.

 

yarn berry의 zero install 을 사용하면 이 시간을 아낄 수 있다.

 

위에서 설명했던 대로 yarn set version berry 실행과 .yarnrc.yml 파일을 수정하여 마이그레이션을 진행한다.

 

실제 프로젝트에서는 더 극적으로 패키지 사이즈가 차이가 난다.

 

거의 1/10 수준으로 줄어들었으며, 이마저도 CI/CD 환경에서 재설치할 필요가 없어졌다.

 

Lifecycle scripts

yarn classic에서는 스크립트 앞에 'pre'를 붙이면 스크립트 실행 전에, 'post'를 붙이면 실행 후에 실행할 스크립트를 추가할 수 있었다.

 

그러나 yarn berry부터는 일부 스크립트에서만 가능하도록 변경되었다.

 

https://yarnpkg.com/advanced/lifecycle-scripts

 

Lifecycle Scripts

An overview of Yarn's supported lifecycle scripts.

yarnpkg.com

 

pre, post 접두사를 사용하고 있다면 package.json 수정이 필요하다.

 

참고

 

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

 

node_modules로부터 우리를 구원해 줄 Yarn Berry

토스 프론트엔드 레포지토리 대부분에서 사용하고 있는 패키지 매니저 Yarn Berry. 채택하게 된 배경과 사용하면서 좋았던 점을 공유합니다.

toss.tech

https://beomy.github.io/tech/etc/yarn-berry/

 

[ETC] Yarn Berry

Yarn은 NPM과 동일한 Node Package Manager입니다. Yarn의 1 버전을 Yarn Classic이라고 하고 Yarn의 2 버전 이상을 Yarn Berry라고 합니다.

beomy.github.io

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

 

Migration

A step-by-step and in-depth migration guide from Yarn 1 (Classic) to Yarn 2 (Berry).

yarnpkg.com

https://kasterra.github.io/setting-yarn-berry/

 

yarn berry로 React.js 프로젝트 시작하기 | Kasterra's Archive

열정 넘치는 프론트엔드 개발자의 블로그!

kasterra.github.io