Yarn workspace 필드를 사용해 간단히 모노레포를 구성할 수 있다.
yarn link 혹은 npm link 기능을 선언적으로 사용하는 것으로 node_modules 디렉터리에 workspace 에 대한 심볼릭 링크가 생성된다. 이를 통해 하나의 저장소에 있는 여러 프로젝트가 서로 쉽게 상호 참조 할 수 있다.
worktree 선언
package.json
{
"private": true,
"workspaces": ["packages/*"]
}
Yarn 1.x 에서는 private: true 필드 값이 필수 이지만, 2.x 이상에서는 필수가 아니다.
그림과 같이 client, server, common 세 개의 workspace 를 추가하고 루트 경로에서 yarn 명령을 실행하면 루트 경로에 node_modules 디렉터리에 workspace들에 대한 심볼릭 링크가 생성된다.
yarn workspace <WORKSPACE_NAME> <COMMAND_NAME>
workspace 를 의존성으로 추가
client 패키지가 common 패키지를 의존하게 하려면 package.json에 의존성을 명시하거나 바로 위에서 설명한 yarn workspace 명령을 이용하면 된다.
yarn workspace client add common@1.0.0
npm 레지스트리에 publish 된 common 이란 이름의 패키지가 있다 해도, 의존성 버전을 충복한다면 로컬에 존재하는 common workspace 를 우선하여 설치한다.
client에서 common 의존성을 잘 부르는지 확인하기 위해 다음 명령을 실행해 보면 common의 hello() 함수가 client 에서 호출되는 것을 확인할 수 있다.
yarn workspace client run start
yarn workspace info 명령을 실행해 workspace들의 의존 관계를 알아볼 수 있다.
Yarn 2.x 이후에는 yarn workpsaces list 를 사용한다.
yarn workspaces info
yarn workspaces run \를 사용하면 모든 workspace에 대해 명령을 실행할 수 있다.
다음 명령은 모든 workspace들을 순회하며 test 스크립트를 실행한다.
yarn workspaces run test
Yarn 2.x 이후에는 yarn workspaces foreach \ 를 사용한다.
workspace가 아닌 루트 프로젝트에 의존성을 추가하려면 다음 명령을 실행한다.
yarn add <package_name> -w
npm, yarn 등은 중복 의존성 설치를 방지하기 위해 호이스팅(hoisting)기법을 사용한다.
모노레포에서의 구조는 다음과 같다.
일부 모듈 로더는 심볼릭 링크를 지원하지 않기 때문에 B(2.0)을 탐색할 수 없다.
이 때는 nohoist 필드를 사용하면 된다.
{
"workspaces": {
"packages": ["packages/*"],
"nohoist": [ "**/react-native" ]
}
}
그 외의 Yarn 1.x에 대한 명령어는 CLI Introduction 에서 확인할 수 있다.
Yarn workspace를 도입하려고 할 때 성능면에서 Yarn Berry를 함께 검토해 볼 수 있다. Yarn Berry는 yarn의 두 번째 버전으로, 2018년 9월 yarn의 RFC 저장소에서 시작되었다. Yarn 1.x의 주요 개발자인 Mael Nison에 의해 TypeScript로 개발되었고 2020년 1월 25일 정식 버전이 출시되었다. Yarn 1.x는 v1.22.17에서 코드 프리징되었고 https://github.com/yarnpkg/berry 에서 2022.03.09 현재 v3.2.0이 출시되었다.
require()
함수를 실행하면 모듈을 찾을 때까지 상위 node_modules 디렉터리를 순회한다. 이때 느린 디스크 I/O 동작이 경로의 깊이만큼 발생한다.!https://d2.naver.com/content/images/2022/04/0a710dbd-7f96-14ab-817f-aa4f08550681.png
어떤 프로젝트를 구성하는 의존성은 결정적(deterministic)이다. Berry는 node_modules에 패키지 파일을 저장하는 대신 패키지의 압축 파일을 .yarn/cache
폴더에 수평적으로 저장하는 방식으로 위 문제를 해결했다. 이 방식을 Yarn은 Plug’n’Play(PnP)라고 부른다. 압축 파일은 ZipFS를 이용하며 해당 모듈 로드가 필요할 때 메모리에서 압축을 해제하여 접근한다.
.yarn/cache
에 수평적으로 존재하므로 모든 패키지에 대한 접근 시간이 O(1)이 된다. 따라서 require()
에 소요되는 시간이 크게 단축된다.다른 브랜치에서 새로운 의존성이 설치되었을 때 설치 과정 없이 바로 사용할 수 있다.
CI에서 의존성 설치에 드는 시간을 크게 줄일 수 있다.
네트워크가 다운되었을 때에도 Yarn이 제대로 작동하도록 하는 기능이다. 자세한 기능은 Offline Cache를 참고한다.
간단히 사용 방법을 살펴보자.
$ npm install -g yarn
$ cd ../path/to/your-package
$ yarn init -y
$ yarnset version berry
아래와 같은 초기 파일이 생성된다.
!https://d2.naver.com/content/images/2022/04/0a710dbd-7f96-14ab-817f-aa4ff99a07dd.png
해당 디렉터리에서 Yarn 버전을 살펴보면 1.x가 아닌 것을 확인할 수 있다.
!https://d2.naver.com/content/images/2022/04/0a710dbd-7f96-14ab-817f-aa503aca09ad.png
의존성을 추가하면 .yarn/cache
경로에 추가된다.
!https://d2.naver.com/content/images/2022/04/0a710dbd-7f96-14ab-817f-aa5088b009b5.png
.pnp.cjs
파일에는 모든 의존성에 대한 메타 정보(zip 경로, 의존성)와 함께 ZipFS에 대한 처리 코드가 들어있다.
!https://d2.naver.com/content/images/2022/04/0a710dbd-7f96-14ab-817f-aa56ebc6393d.png
따라서 Berry 기반의 프로젝트는 node src/main.js
와 같은 명령으로는 실행할 수 없고 yarn node src/main.js
와 같이 Yarn을 통해서 실행해야 한다.
{
"scripts": {
"start": "node src/main.js"
}
}
위와 같이 package.json에 스크립트 명령을 작성한 경우에는 yarn start
명령을 사용할 수 있다.