해당 글에서는 Yarn Berry를 이해하고 주요 특징에 대해 이해를 돕기 위해 작성하였습니다
1) Yarn Berry(Yarn Modern)
💡 Yarn Berry(Yarn Modern)
- JavaScript 프로젝트의 종속성을 관리하는 데 사용되는 검증된 오픈소스 '패키지 관리자' 중 하나인 'Yarn'은 Yarn 2 버전 이후(3, 4까지 포함되는 새로운 세대 Yarn)를 지칭하는 이름을 의미합니다.
- 기존 Yarn 1(Classic)과 달리 아예 아키텍처를 재 설계한 버전으로 패키지 종속성 설치, 업데이트, 구성 및 제거 과정을 지원하여, 궁극적으로 방해 요소를 줄이고 목표를 더 빠르게 달성할 수 있도록 도와줍니다. - 속도, 정확성, 보안, 개발자 경험에 중점을 두고 있으며, 작업 공간, 오프라인 캐싱, 병렬 설치, 강화 모드, 대화형 명령 등 다양한 혁신적인 기능을 활용하여 모든 측면에서 개선하고 있습니다.
# 프로젝트별로 패키지 관리자 버전을 구성할 수 있는 중개 도구 설치
$ npm install -g corepack
# 중개도구 사용 여부 선택
$ corepack enable
# 최초 설치인 경우
$ yarn init -2
# 기존 프로젝트 설치하는 경우
$ yarn set version stable
$ yarn set version 4.9.4 # 원하는 버전 (예: 4.9.4)
# yarn 설치
$ yarn install
💡 설치 이후 해당 파일들이 변경 및 추가됩니다. - 아래의 경우는 node_modules를 이용하는 경우입니다.
2) Yarn Berry 주요 특징 -1: Yarn Plug’n’Play (PnP)
1. Yarn Plug’n’Play (PnP)
💡 Yarn Plug’n’Play (PnP)
- Yarn Plug’n’Play(PnP)는 Yarn의 새로운 의존성 관리 전략입니다. 기존 node_modules와 비교 됩니다.
1. Yarn 1.x(Classic)
- package.json + yarn.lock을 기준으로 의존성을 해석하고, 결과를 node_modules 폴더에 물리적으로 디스크에 설치합니다.
2. Yarn 2.x+(Berry)
- PnP를 사용하면, 의존성을 .yarn/cache 폴더에 .zip 형태로 저장하며, node_modules를 만들지 않습니다. - 대신 .pnp.cjs, .pnp.loader.mjs 파일이 생성되어, 패키지 이름 → 해당 zip 아카이브 내부 경로로 매핑하는 '루트 맵'이자 런타임 해석기(Plug’n’Play 로더) 역할을 수행합니다.
.yarn/ # yarn 바이너리, 플러그인, 캐시 등
cache/ # 패키지 zip 캐시
releases/
plugins/
.pnp.cjs # 모듈 해석 매핑(필수)
.pnp.data.json # 매핑 데이터(자동)
.yarnrc.yml # Yarn 설정 (PnP/링커/플러그인 등)
2. Yarn 의존성 관리 방식
💡 Yarn 의존성 관리 방식
의존성 관리 방식
설명
node_modules
- package.json + yarn.lock을 기준으로 패키지를 해석한 뒤, 디스크의 node_modules 폴더에 실제 파일/폴더 구조로 설치합니다.
Yarn Plug’n’Play (PnP)
- node_modules 폴더 자체를 만들지 않습니다. 대신 .yarn/cache 폴더에 패키지를 .zip 아카이브 형태로 저장하고, .pnp.cjs / .pnp.loader.mjs 파일에서 매핑 정보(루트맵)를 유지합니다 - .Node.js가 모듈을 require/import할 때 이 매핑을 참조해서 zip 안에서 직접 로딩합니다.
💡 .yarnrc.yml 파일 내에서 설정 방법 - nodeLinker 속성의 값을 'pnp' 혹은 'node-modules'를입력하여서 설정을 할 수 있습니다.
# 기본(PnP) 링커 사용
nodeLinker: pnp
# Node Modules 링커 사용
nodeLinker: node-modules
[ 더 알아보기 ] 💡 Node 로더(Node Loader)란 무엇일까?
- Node.js가 코드를 실행할 때 모듈(= require / import 대상)을 어떻게 읽어오고, 해석하고, 연결할지를 담당하는 메커니즘을 말합니다.
파일
대상 Node
적용방식
역할
.pnp.cjs
CommonJS (require)
node -r ./.pnp.cjs
require 해석 가로채기
.pnp.loader.mjs
ESM (import)
node --loader ./.pnp.loader.mjs
import 해석 가로채기
3. Yarn Plug’n’Play (PnP) 저장 방식
💡 Yarn Plug’n’Play (PnP) 저장 방식
- node_modules 폴더를 만들지 않고 .pnp.cjs 같은 파일에 의존성 트리를 기록하는 방식입니다. - 프로젝트 별로 의존성 패키지를 저장하는 방식이거나 사용자 홈 디렉터리에 저장하는 방식 두 가지가 있습니다.
구분
로컬 캐시(Local Cache)
전역 캐시 (Global Cache)
설명
프로젝트별로 모든 의존성을 .yarn/cache 디렉토리내에 zip 파일 형태로 의존성 패키지를 저장하는 방식입니다.
사용자 홈 디렉토리 내에 패키지를 저장하는 방식으로 여러 프로젝트가 같은 패키지를 공유하여 저장하는 방식입니다.
분류
프로젝트 별 의존성 저장
여러 프로젝트가 같은 패키지 공유 저장
저장 위치
.yarn/cache 폴더 내 저장
/Users/[사용자 이름]/.yarn/berry/cache 폴더 내 저장
공유 범위
프로젝트 단위로 공유
사용자 단위로 공유 혹은 패키지 프록시 레지스트리(Nexus, Verdaccio)를 통한 중앙 패키지 관리
💡 .yarnrc.yml 파일 내에서 설정 방법 - 로컬 캐시, 전역 캐시 지정 및 설정 캐시 폴더 확인 방법
# 글로벌 캐시 사용 여부
enableGlobalCache: true # (기본값 false → 로컬 .yarn/cache 사용)
# 캐시 경로 강제 지정
cacheFolder: ~/.yarn/berry/cache
# yarn의 설정 캐시 폴더 위치를 알수 있는 방법
$ yarn config get cacheFolder
# yarn의 설정 캐시 폴더 패키지 확인
$ /Users/[사용자 명]/.yarn/berry/cache
3) Yarn Berry 주요 특징 -2: Zero-Installs
💡 Zero-Installs
- 프로젝트를 Git 내에서 프로젝트를 Clone 한 이후 yarn install 명령어를 실행하지 않아도, 바로 실행할 수 있는 상태를 의미합니다. 즉, 설치 과정(yarn install) 과정이 0번(Zero)이 된다는 의미입니다. - 기존에는 디스크 내에 저장이 되는 node_modules의 용량으로 인해, .gitignore에 추가하여 매번 프로젝트를 설치해야했고, 신규로 Clone을 받으면 반드시 yarn install을 수행해야 한다는 점이 있었습니다.
- 하지만 Yarn Berry(2+)에서는: 1. .yarn/cache 폴더에 모든 패키지가 .zip 형태로 저장됩니다. 2. .pnp.cjs (PnP 환경) 또는 node_modules 링크 정보가 프로젝트 안에 기록됩니다. 3. 이 두 개를 Git에 같이 커밋해 버리면, 새로운 개발자가 코드를 받아도 이미 모든 패키지가 저장소 안에 들어있는 상태가 됩니다.
- 이러한 경우, .yarn/cache가 Git에 포함되므로 저장소 용량이 커질 수 있다는 점이 있습니다. - 단, 저장소 크기를 줄이고 싶으면 .gitignore에 .yarn/cache를 빼고, 대신 CI에서 yarn install을 실행하는 전략을 선택할 수도 있습니다.
- Monorepo 개발을 위한 핵심 기능으로, 여러 프로젝트(패키지)를 한 번에 관리하고 내부적으로 재 사용할 수 있게 하는 관리 방식을 의미합니다. - 전통적으로는 서비스별로 저장소를 분리(multi-repo)했지만, 요즘은 협업과 코드 재사용성을 위해 서비스를 묶어서 관리하는 Monorepo를 많이 씁니다.
💡 아래와 같이 ‘my-monorepo’라는 루트 디렉터리 내에 워크스페이스의 projectA, projectB, projectC가 있다는 가정하에 있습니다.
- my-monorepo는 관리의 주체가 되어서 모든 패키지에 대한 라이브러리를 일괄 관리합니다. 그렇기에 루트 단위에서 라이브러리를 설치하면, projectA, projectB, projectC라는 workspace에 어디에서든 import가 가능합니다.
- Monorepo 최상위 디렉터리를 의미하며, 워크스페이스(workspace)내에서 공통적으로 관리되는 의존성을 설치하고, 워크스페이스들을 연결합니다.
{
// 루트 자체가 npm에 배포되지 않도록 강제 합니다
"private": true,
// 어떤 디렉토리를 워크스페이스로 인식할지 정의합니다.
"workspaces": [
"apps/*",
"packages/*"
]
}
2.1. 루트(Root) 프로젝트 내 워크스페이스 관리
💡 루트(Root) 프로젝트 내 워크스페이스 관리
- 루프 프로젝트 내에서 워크스페이스들을 관리하는 방법입니다.
# 루트를 기준으로 하위 워크스페이스의 라이브러리 설치
$ yarn workspaces focus [name]
# 사용 예시
$ yarn workspaces focus projectA
# 모든 하위 워크스페이스 내에 라이브러리를 병렬로 설치하는 방법
$ yarn workspaces foreach --all -pt [명령어]
# 예시1: 모든 하위 워크스페이스 내에 eslint . 실행
$ yarn workspaces foreach --all -pt eslint .
# 예시2: 모든 하위 워크스페이스 내에 yarn build 실행
$ yarn workspaces foreach --all -pt yarn build
💡 워크스페이스 관리
- 공통적으로 루트 단위에서 관리되는 라이브러리도 있지만, 특정 프로젝트 내에서만 사용되는 라이브러리도 있습니다. - 이때의 경우는 루트에서 워크스페이스를 동작시켜서, 패키지 내에서 전용 라이브러리를 따로 설치를 수행합니다.
# workspace 중 'projectA' 내에 'axios'라이브러리를 설치합니다.
$ yarn workspace projectA add axios
# 루트를 기준으로 하위 워크스페이스의 스크립트 호출 방법
$ yarn workspaces [name] [명령어]
# 사용 예시
$ yarn workspaces projectA ios:16pro
- 루트 프로젝트 안에 하위 프로젝트 단위를 의미합니다. 각각 독립적인 pacakge.json 파일을 가지고 있고 Monorepo 내부에서는 심볼릭 링크로 연결되어 사용 가능
{
// 워크스페이스 패키지 이름을 의미합니다.
"name": "projectA",
// 루트의 utils의 export 되어 있는 것들을 import하여서 사용할 수 있습니다."workspace:*" 를 통해 심볼릭 링크가 되어 있습니다.
"dependencies": {
"@ecodelab/utils": "workspace:*",
}
}
4. 워크스페이스(Workspace) 별 패키지 관리
💡 워크스페이스(Workspace) 별 패키지 관리
- 공통적으로 루트 단위에서 관리되는 라이브러리도 있지만, 특정 프로젝트 내에서만 사용되는 라이브러리도 있습니다. - 이때의 경우는 루트에서 워크스페이스를 동작시켜서, 패키지 내에서 전용 라이브러리를 따로 설치를 수행합니다.
# workspace 중 'projectA' 내에 'axios'라이브러리를 설치합니다.
$ yarn workspace projectA add axios
4.1. workspace가 사용되는 경우
장점
내용
여러 패키지를 동시에 관리 하는 경우
- Babel처럼 핵심 패키지(core)와 수많은 플러그인/프리셋(add-ons)이 함께 움직여야 하는 프로젝트인 경우 각각을 별도 저장소에서 관리하는 건 매우 비효율적이기에 한 저장소 안에서 모든 패키지를 나란히 관리할 수 있다는 장점이 있습니다.
PR 관리와 협업 효율성
- Jest와 같은 테스팅 프레임워크에서는 여러개의 패키지들의 집합으로 구성으로 되어있습니다. 그렇기에 projectA, projectB, projectC에 대해서 각각 설치하고 설정을 해야하는 번거로움이 있습니다. - 동일한 기능을 테스트를 기능을 수정하는 경우 프로젝트 마다 모두 열고 수정해야할 수 있습니다. 그렇지만 Monorepo에서는 하나의 PR에서 모든 관련 변경을 묶어서 제출 가능, 리뷰와 머지도 간단해짐.
코드 경계 유지 & 모놀리스 회피
대규모 엔터프라이즈 프로젝트나 Yarn 자체 같은 복잡한 코드베이스는 하나의 거대한 모놀리스(monolith)로 묶이면 유지보수가 어려워짐. - Monorepo는 코드베이스를논리적으로 작은 workspace 단위로 나눠 관리할 수 있게 해서“큰 하나”가 아닌작은 여러 개처럼 동작하게 만듦.
의존성 공유와 일관성
모든 패키지가 같은 lockfile을 사용 → 버전 불일치 문제 최소화.공통 라이브러리(예: React, Axios)를 루트에 설치하면 중복 설치 없이 모든 workspace에서 공유 가능.
빌드·테스트 자동화에 유리
루트에서 명령 한 번 (yarn workspaces foreach run build/test)으로 모든 패키지를 순차 혹은 병렬 실행.CI/CD 파이프라인에서 효율적인 멀티 패키지 관리 가능.
[ 더 알아보기 ] 💡 그러면 단일 배포를 수행하는 경우는 어떻게 실행이 되는 걸까?
- 아래와 같이 yarn workspaces focus projectA 과정을 통해서 특정 워크스페이스만 빌드 가능한 상태로 node_modules를 정리합니다. 이 과정을 수행함으로써 projectA가 의존되지 않는 것들에 대해서 사용되지 않는 것들을 정리합니다.
# Monorepo에서 특정 워크스페이스만 빌드 가능한 상태로 node_modules를 정리합니다.
$ yarn workspaces focus projectA
# projectA로 이동합니다
cd apps/projectA
# 빌드 -> 번들 -> aab 파일 생성
yarn android --variant release
5) Yarn Berry 주요 특징-4: Corepack
1. Corepack
💡 Corepack - Node.js 환경에서 Yarn 같은 패키지 매니저를 더 안정적으로 관리하고 실행할 수 있도록 해주는 중간 레이어(wrapper) 역할을 합니다. - Node.js 16.9+ 버전부터 기본 포함된 도구이자 Yarn, pnpm 같은 서드파티 패키지 매니저를 Node.js와 함께 번들처럼 관리할 수 있게 해줍니다. - 예를 들어서, 프로젝트에 어떤 Yarn 버전을 사용하든, Corepack 내에서 프로젝트의 Yarn 버전을 맞춰줍니다. 이로 인해 팀원마다 같은 Yarn 환경을 보장합니다.
장점
설명
일관된 개발 환경 보장
팀원마다 Yarn 버전을 따로 설치할 필요 없이 동일한 버전을 보장
자동 버전 관리
packageManager 필드에 정의된 버전을 Corepack이 자동으로 다운로드 및 실행