1. 탐구
yarn classic과 yarn berry는 각각 어디서 모듈을 관리할까?
yarn classic은 yarn V1, yarn berry는 yarn V2이후를 말한다.
yarn classic에서는 node_modules에서 패키지를 관리한다. yarn berry에서는 .yarnrc.yml에서 어디서 관리할 지 설정할 수 있는데, nodeLinker를 node-modules로 설정한다면 node_modules에서, pnp로 설정한다면, .yarn/cache에서 zip형태로 관리한다.
yarn에는 yarn.lock이 있다면, package-lock.json은?
package-lock.json은 npm의 버전을 관리하기 위한 파일이다. yarn classic(yarn V1)의 yarn.lock과 같은 개념이다. 이는 버전에 대한 정보를 담고 있기에 저장소에 업로드해야한다.
2. 개발
1. npm
-
npm init -ypackage.json이 생성된다.
// package.json { "name": "npm", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }npm폴더에서 npm init -y를 입력했기에, name은 자동으로 npm이 된다.
-
npm installpackage-lock.json이 생성된다. 패키지에 대한 버전을 고정하기 위함이다.// package-lock.json { "name": "npm", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "npm", "version": "1.0.0", "license": "ISC" } } }설치된 패키지가 없기에, 별 내용이 없다.
-
react를 설치한다.
package.json,package-lock.json에 변화가 생기고, node_modules폴더가 생성된다.-
package.json
// package.json { // .. "dependencies": { "react": "^18.2.0" } }^18.2.0은, major버전만 고정한다는 뜻이다.
-
- package-lock.json
// package-lock.json
{
// ..
"packages":{
"": {
"name": "npm",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"react": "^18.2.0"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
}
}
}
}
react의 의존성에 대한 정보가 추가되었다.
node_modules/react 등에 대한 version을 명확하게 명시하고 있다. 그리고, 각 의존하고 있는 패키지를 명시하고 있다.
-
node_modules
node_modules에는 react 뿐 아니라 다른 패키지도 함께 설치가 되었다.
모두 package-lock.json에서 본 패키지명들이다.
node_modules안에도 .package-lock.json이 있는데, 이는 외부에 있는 package-lock.json과 내용이 정확하게 일치한다. node_modules에서 접근하기 쉽게 만든 복사본인 듯하다.
이 때, react만 설치했음에도 react의 의존성 모듈들이 같은 depth에 설치되었는데 이것이 바로 node_modules의 깊이가 너무 깊어지는 것을 방지하기 위해 hoisting으로 끌어올려 설치하는 것이다. 이 hoisting은 유령 의존성 문제를 일으키는 원인이기도 하다.
-
각 패키지
각 패키지에는 하나의 레포지토리처럼 모든 것이 세팅되어있다.
-
loose-envify? js-tokens?
- js-tokens : Javascript를 토큰화하는 라이브러리이다.
- loose-envify : js-tokens를 사용하여 process.env를 replace해주는 라이브러리이다.
-
결론
- npm은 package.json에서 패키지를 관리하고, package-lock.json에서는 패키지의 정확한 버전을 관리할 수 있도록 돕는다.
- 패키지를 설치하면, package-lock.json이 자동으로 생성되어 버전을 명시한다.
- 패키지를 설치하면, 해당 패키지 뿐 아니라, 패키지에 종속된 패키지들 또한 함께 저장이 되며, 이는 package-lock.json에 모두 명시가 되고, node_modules에 설치가 된다.
2. yarn(v1.22.19, yarn classic)
-
yarn init -y// package.json { "name": "yarn", "version": "1.0.0", "main": "index.js", "license": "MIT" } -
yarn installnpm과 달리 yarn.lock과 node_modules모두 생성이 된다. 하지만, 설치된 패키지가 없기에, 기본 파일들이다.
-
yarn.lock
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1
-
-
node_modules
// node_modules/.yarn-integrity
{
"systemParams": "darwin-arm64-115",
"modulesFolders": [],
"flags": [],
"linkedModules": [],
"topLevelPatterns": [],
"lockfileEntries": {},
"files": [],
"artifacts": {}
}
yarn add react-
package.json
// package.json { // .. "dependencies": { "react": "^18.2.0" } }
-
- yarn.lock
// yarn.lock
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
loose-envify@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies:
loose-envify "^1.1.0"
package-lock.json과 느낌이 많이 다르다는 것을 확인할 수 있다.
버전에 대한 명확한 명시와, 의존성 모듈이 작성되어 있다. react는 loose-envify를, loose-envify는 js-tokens패키지를 필요로 한다.
-
node_modules
yarn classic은 npm과 동일하게 node_modules로 의존성 관리를 한다. 그리고, flat하게 관리한다.
결론
- yarn은 npm과 달리 yarn.lock에서 패키지의 관리를 정확하게 관리한다는 점에서 다르다.
- 그 외에 node_modules에서 패키지를 관리한다는 점은 동일하다.
- yarn classic에서도 node_modules를 사용하기에 큰 용량이라는 한계를 해결하지 못했다.
3. yarn berry(yarn 3.6.4)
-
yarn init -y// package.json { "name": "yarn-berry", "version": "1.0.0", "main": "index.js", "license": "MIT" } -
yarn set version berry
yarn의 최신 버전인 4.1.0으로 바뀐다.
-
.yarnrc.ymlyarn의 설정을 할 수 있는 .yarnrc.yml 파일이다.
// .yarnrc.yml yarnPath: .yarn/releases/yarn-4.1.0.cjs
-
-
.yarn/release/yarn-stable-temp.cjs.yarn/release에도 파일이 하나 생성된다.
-
yarn install
-
.pnp.cjs
pnp를 사용하지 않고, yarn berry(V2이상)를 적용해도 .pnp.cjs로 패키지를 관리하나보다.
-
- yarn.lock
// yarn.lock
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10c0
"yarn-berry@workspace:.":
version: 0.0.0-use.local
resolution: "yarn-berry@workspace:."
languageName: unknown
linkType: soft
❓
.pnp.cjs에서 패키지를 관리하는데,yarn.lock이 필요할까?
.pnp.cjs는 pnp모드에서 모든 패키지 의존성에 대한 정확한 위치 정보를 담고 있다.
yarn.lock은 프로젝트에 설치된 모든 패키지의 정확한 버전 정보를 기록한다.
yarn.lock은 패키지의 정확한 버전을 고정하여 의존성 해결 과정의 일관성을 보장하고,.pnp.cjs는 이러한 패키지들이 실제로 어디에 위치하는 지를 관리한다. 이는 node_modules폴더 대신 효율적인 방식으로 의존성을 로드한다. 둘의 역할은 서로 다르기에, 서로가 대체할 수 있는 존재가 아니다.
-
.yarn/releases/yarn-4.1.0.cjs기존
yarn-stable-temp.cjs의 파일 명이yarn-4.1.0.cjs로 변경되었다. -
.yarn/install-state.gz
yarn init -2: yarn berry를 설정한다- 기존 package.json에 설치된 모듈은 모두 제거가 되니, 주의해야한다.
.gitignore
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Swap the comments on the following lines if you wish to use zero-installs
# In that case, don't forget to run `yarn config set enableGlobalCache false`!
# Documentation here: https://yarnpkg.com/features/caching#zero-installs
#!.yarn/cache
.pnp.*
저장소에 올리는 파일은 .yarn의 patches, plugins, releases, skds, versions이다. 그리고 그 외에는 개인 저장소에서 관리해야 한다.
즉, .pnp.cjs, .yarn/install-state.gz는 올리면 안된다는 말이다.
.editorconfig
root = true
[*]
end_of_line = lf
insert_final_newline = true
[*.{js,json,yml}]
charset = utf-8
indent_style = space
indent_size = 2
.gitattributes
/.yarn/** linguist-vendored
/.yarn/releases/* binary
/.yarn/plugins/**/* binary
/.pnp.* binary linguist-generated
-
yarn add reactyarn.lock에서 버전 관리하는 것은 동일하다.
다른 파일이 생성되지 않는다.
❓ node_modules도 설치가 되지 않고, .yarn/cache도 설치가 되지 않는다. 그렇다면 react 패키지는 어디서 관리하고 있는 걸까?
// .pnp.cjs ["react", [\ ["npm:18.2.0", {\ "packageLocation": "../../../../../.yarn/berry/cache/react-npm-18.2.0-1eae08fee2-10c0.zip/node_modules/react/",\ "packageDependencies": [\ ["react", "npm:18.2.0"],\ ["loose-envify", "npm:1.4.0"]\ ],\ "linkType": "HARD"\ }]\ ]],\cache패키지의 의존성 관계를 관리하는 .pnp.cjs파일이다. react패키지에 대한 정보가 적혀있는 것을 확인할 수 있다. 이 패키지들은 어디에 설치가 된걸까?
경로를 찾아가 보았다.
찾았다. packageLocation의 경로대로 찾아가보니, react 패키지가 저장되어 있다. 이 패키지를 yarn은 찾는 것이다. 이래도 되는걸까? 여기서 패키지를 관리하면, 안되는 거 아닌가..
.pnp.cjs는 저장소에 올리지 않고, 개인 저장소에서 관리한다. 즉, 각자마다 다르다는 내용이다. 즉, 사람마다 패키지에 대한 의존성을 나타내는 .pnp.cjs는 다르기에 상관이 없다는 말이다.
❓ 해당 zip파일을 제거한다면?
동일한 위치에 다시 설치가 되었다. react패키지는 원래, home 폴더에서 관리하나보다.
❗ 아니다, 어떤 패키지를 설치해도 home폴더에 저장이 된다. 이러면 저장소에 패키지를 올릴 수가 없잖아.
이러면 cache된 zip패키지들을 저장소에 업로드할 수 없다. 왜 home에 저장이 되는 것일까?
yarn init -2를 입력했을 때 생성된 .gitignore의 내용 중 일부이다.# Swap the comments on the following lines if you wish to use zero-installs # In that case, don't forget to run `yarn config set enableGlobalCache false`! # Documentation here: https://yarnpkg.com/features/caching#zero-installsyarn config set enableGlobalCache false를 입력하니 해결되었다.위
enableGlobalCache설정을 해주지 않으면, 기본적으로 홈에 저장이 되는 것이다..yarnrc.yml에서enableGlobalCache옵션을 false로 설정하고yarn install을 했을 때 폴더 구조이다.
정상적으로 .yarn/cache에 zip아카이브 파일이 저장되었다.
-
nodeLinker: pnp위에서도 봤듯이,
yarn set version berry를 했을 때 yarn의 버전이 2이상이며 최신 버전인 4.1.0으로 변경이 되었다. 그리고 기본적으로 pnp로 동작한다. -
nodeLinker: node-modules만약, V2.0이상에서 node_modules를 사용하고 싶다면 아래와 같은 코드를 추가하면 된다.
// .yarnrc.yml nodeLinker: node-modules-
yarn install입력 시 .pnp.cjs가 제거되며 node_modules가 설치된다. node_modules에 패키지가 설치 되었기에.yarn폴더는 제거해도 된다.
-
💡
install-state.gz를 이미 저장소에 올렸을 때, 제거하는 방법
install-state.gz는 패키지를 최적화하는 파일로, 저장소에 올리지 않는 것을 공식문서에서 권장한다. 그런데 실수로 저장소에 올렸을 경우에는 다음과 같이 하면 된다.
- gitignore에
install-state.gz가 저장소에 올라가지 않도록 설정되었는지 체크한다.install-state.gz를 제거한다.- commit을 해서 저장소의
install-state.gz를 제거한다.yarn install을 한다.
- 이제, install-state.gz는 변경사항에 반영되지 않는다.
위와 같이 수행했다면, 저장소에는 업로드 되지 않고 개인 저장소에서만 저장될 것이다.
💡 무엇을 저장소에 올리고, 무엇은 올리면 안될까?
yarn berry에서는
install-state.gz는 위에서도 말했듯이 저장소에 올리지 않는다.Zero Install을 사용한다면, .pnp.*와 .yarn/cache를 모두 업로드한다.
// .gitngore !.yarn/cache // !는 git에 올린다는 의미 # .pnp.*Zero Install을 사용하지 않으려면, .pnp.*와 .yarn/cache를 모두 업로드하지 않는다.
// .gitngore # !.yarn/cache .pnp.*Zero Install을 사용하지 않는다면, 추가로 아래 코드를 추가해야 한다.
// .yarnrc.yml pnpMode: loose
결론
- yarn berry는 yarn 1.0(yarn classic)에서 pnp가 추가됨에 따라 많은 변화가 생겨 다르게 명명한, yarn 2.0이상의 버전을 일컫는다.
- yarn berry에서는 기본적으로 pnp모드이고, pnp를 사용하고 싶지 않다면 .yarnrc.yml에서 nodeLinker를 node-modules로 설정하면 된다.
- pnp모드는 .yarn/cache에서 zip아카이브 파일로 패키지를 관리한다. 이러한 zip 파일을 모두 저장소에 업로드하여 zero install로, clone 후 설치하지 않고도 이용할 수 있도록 할 수 있다. 이 때, .pnp.cjs와 .yarn/cache를 저장소에 올려야 한다.(gitignore에서 제외한다)
- zero install를 사용하고 싶지 않다면, .yarn.yml에서 pnpMode : loose를 추가하면 된다. 그리고 .pnp와 .yarn/cache를 저장소에 올리지 않는다.
- pnp모드에서는 install-state.gz를 통해 패키지를 최적화하며, 이는 저장소에 업로드하지 않는다.
3. 개념 정리
1. 노드(Node.js)의 패키지 관리자
- npm, yarn, pnpm
- 전 세계 개발자들이 Javascript로 만든 다양한 패키지를 npm 온라인 데이터 베이스에 올리면 npm, yarn과 같은 패키지 관리자를 통해 설치 및 삭제 가능
2. npm
: Node Package Manager
- 노드를 설치할 때 자동으로 설치되는 기본 패키지 관리자
1. 역할
- 온라인 플랫폼
- 사람들이 노드 패키지를 만들고, 업로드하고, 공유할 수 있는 공간
- 누구나 온라인 플랫폼에 게시된 패키지를 사용할 수 있음
- 명령 줄 인터페이스
- 온라인 플랫폼과 상호작용하기 위해 명령 줄 인터페이스를 사용하여 패키지 설치 및 제거 가능
2. 특징
package.json이라는 파일에 프로젝트가 의존하고 있는 패키지 목록 명시node_modules디렉토리에 저장
- 주로 npm 저장소(registry)로부터 다운로드
package-lock.json: 패키지 버전 잠금
3. 명령어
npm init: npm 기반 프로젝트 생성npm init -y: default값으로 프로제트 생성

- npm 프로젝트 :
package.json파일을 가진 모든 디렉토리
-
npm i -g <패키지명>: 전역으로 패키지 설치-
npm root -g: 정확한 패키지 전역 설치 디렉토리 경로 출력
-
-
npm ls : 설치된 패키지 확인
-
npm r <패키지명>: 패키지 제거
4. nvm
: Node Version Manager, 여러 개의 Node.js를 설치하고 사용할 Node.js의 버전을 쉽게 변환할 수 있도록 도와주는 shell script
- 명령어
nvm install 14.18.3: 특정 버전 node 설치
nvm ls: 설치된 버전 확인nvm use 14.18.3: 설치한 버전 적용npm ls -g: global로 설치된 항목 보기
5. npx
: 굳이 패키지를 설치하지 않고도 실행할 수 있다.
6. npm init / npm create / npx create
npm init react-app=npm create react-app=npx create-react-app: 모두 동일한 명령어npm에는 create-react-app으로 올라가 있고, 명령어만 다른 것
3. Yarn
1. 특징
- 2016년 페이스북에서 만든 패키지 관리자
- npm 레지스트리와 호환하면서 속도나 안정성 측면에서 npm보다 향상
yarn.lock: 패키지 버전 잠금
2. Yarn berry
- Yarn 2.0에서 등장
node_modules디렉토리에 패키지를 저장하지 않는다는 점에서 혁신적임Yarn 1.0과 호환을 포기
3. Zero Install
- 설치를 하지 않고 이용하는 방식
- CI단이나 배포 파이프라인 등에서 반복되는 install시간을 단축시킬 수 있음 → CI 실행시간 및 배포 시간을 대폭 감소
- PnP(Plug And Play)
-
.yarnrc.yml에서 디펜던시 모듈을 관리하는 방식// .yarnrc.yml nodeLinker: "pnp" // pnp(default), pnpm, node_modules 중 설정 가능.
-
- Zip Archive File
pnp모드 설정으로dependency module설치 시,node_modules로 설치되지 않음- 대신에 필요한 라이브러리 모듈들이
.yarn/.cache디렉토리에zip아카이브 파일로 관리됨 - zip로 된 각 모듈의 의존성 트리 정보들은 프로젝트 루트의
.pnp.cjs파일로 관리
- 장점
- zip로 압축된 파일로 관리함/defa으로써 디펜던시 사이즈를 많이 줄일 수 있음
-
기존 node_modules내에서 각 의존성 모듈 별로 중복되는 모듈을 상위로 호이스팅하는 등 각 의존성 트리가 굉장히 복잡하면서, 직접 의존성 추가를 하지 않은 모듈을 이용할 수 있는 유령 디펜던시 문제가 발생하는 등 의존성 관리에 대한 문제 요소들이 많았음

https://toss.tech/article/node-modules-and-yarn-berry - pnp에서는 하나의 의존성 모듈이 하나의 zip 파일로 관리함으로써, 각 의존성 모듈이 서로 얽히지 않고 독립적으로 관리를 할 수 있게 되고 각 디펜던시 트리의 복잡성이 없앨 수 있게 된다.
명령어
yarn install또는yarn:npm installyarn add <package>:npm i <package>yarn add <package> --dev또는yarn add <package> --D: npm i-dev yarn remove <package>: 패키지 삭제yarn upgrade: 패키지 업그레이드yarn upgrade <package>@<Version>: 특정 버전으로 패키지 업그레이드yarn upgrade-interactive: 목록들 중 원하는 패키지만 최신 버전으로 업그레이드하는 interactive terminal ui 실행NODE_ENV=production yarn install또는yarn i —production: production환경에서 필요한 dependencies만 설치yarn global add <package>: 전역 설치yarn cache clean: 캐시 삭제yarn outdated: 설치된 모듈 버전 확인yarn install --immutable --immutable-cache --check-cache: CI할 때, 기존 cache 데이터 제외 설치
3. npm vs yarn
| npm | yarn | ||
|---|---|---|---|
| 속도 | 직렬 설치 | 병렬 설치 | |
| 보안 | 취약 : 자동으로 패키지에 포함된 다른 패키지 코드 실행 | ||
| but 최근에 npm에서도 보안 업데이트를 하며 향상 | yarn.lock 혹은 package.json파일에 있는 파일만을 설치 | ||
| npm보다 터미널에서 실행 결과가 예쁘게 나옴 |
4. pnpm
- 2017년 처음 소개
- npm을 대체하기 위한 더 빠르고 효율적인 패키지 매니저를 목표
- 패키지를 직접 설치하는 방법 대신 전역 저장소(Virtual Store, .pnpm-store)에서 패키지를 공유
-
node_modules에 Symbolic Link(symlinks)를 생성하여 전역 저장소의 해당 패키지를 참조
우측에 화살표 모양으로 무언가 가르키고 있는 것을 확인할 수 있다. 이는 심볼릭 링크로 생성되었음을 의미한다.
-
- 평탄하지 않은 node_modules 유지
- yarn보다 빠른 패키지 매니징
- node_modules를 플랫하게 만들지 않음
- 호환되지 않는 모듈 존재
pnpm으로 마이그레이션 하는 방법
- 루트 + 각 패키지의 node_modules 삭제
$ pnpm import: yarn.lock → pnpm-lock.yaml으로 마이그레이션- 루트 + 각 패키지의 yarn.lock 혹은 package-lock.json 제거
5. Package.json
- npm, yarn 모두 package.json파일을 통해 해당 프로젝트의 메타 정보 관리
dependencies: 일반적으로 설치되어야 하는 패키지devDependencies: 개발할 때만 필요한 패키지- package.json파일만 있으면, 개발자마다 설치한 시점에 따라 버전이 다르게 됨
- ^나 ~등을 이용해서 범위로 지정한 경우가 많기 때문
^(carrot) : minor 버전까지만 설치 및 업데이트v.major.minor.patchminor버전까지는 하위 호환되기 때문에^를 많이 사용
~(tilt) : patch버전까지만 설치 및 업데이트
package-lock.json
-
package-lock.json을 통해 버전 고정 -
예시
"@emotion/memoize@npm:0.7.4": version: 0.7.4 resolution: "@emotion/memoize@npm:0.7.4" checksum: 4e3920d4ec95995657a37beb43d3f4b7d89fed6caa2b173a4c04d10482d089d5c3ea50bbc96618d918b020f26ed6e9c4026bbd45433566576c1f7b056c3271dc languageName: node linkType: hard
Script
: 테스트나 구동, 빌드와 같이 반복적으로 실행해야하는 스크립트를 등록하여 npm run 혹은 yarn 명령어를 이용해서 간편하게 실행 가능
"scripts": {
"test": "mocha --compilers js:babel-register",
"start": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
}
npm run test로 실행
Reference
4. Articles
npm vs yarn vs yarn berry
npm
- 일관적이지 않은 패키지 버전
^: 가장 앞 숫자인 메이저 버전을 제외한 두 자리(마니어, 패치)버전까지는 변경을 허용한다.
- 고정되지 않은 설치 순서
- 모듈을 추가로 설치할 경우 그 뒤에 npm i로 설치하는 사람과 모듈 설치 순서가 달라진다.
- 순차적인 설치로 인한 긴 소요시간
yarn
- 버전 고정 파일 yarn.lock 포함
- 정확한 버전을 지정
- npm에서도 버전을 고정하는
npm-shrinkwrap.json파일을 만듬 but 매 번 명령어를 입력해야함
-
설치 확인을 위해 checksum 사용
-
yarn.lock파일의 resolved주소 뒤에 해시값 추가
-
-
속도
- 캐시를 사용하여 한 번 다운로드한 패키지는 빠른 속도로 설치
- 병렬 다운로드 지원
npm과 yarn의 현재
- 현재, npm에서는 버전 고정을 위한 package-lock파일 등장
- 속도면에서도 yarn과 큰 차이가 없다
여전한 문제점
- node_modules의 큰 용량
- 이를 해결하기 위해 npm에서는 호이스팅 도입
- 호이스팅 : node_modules내부의 중복된 패키지를 최상단으로 끌어올림
- 이를 해결하기 위해 npm에서는 호이스팅 도입
- → 유령 의존성이라는 문제 새로 낳음
- 유령의존성 : 직접 설치하지 않고, 간접 설치한 종속성에 개발자가 접근할 수 있는 상황 → 존재하지 않는 종속성에 의존하는 코드가 발생할 수 있다.
- yarn berry는 이러한 호이스팅 동작이 일어나지 않도록 nohoist옵션이 기본적으로 활성화 됨
- node_modules의 모듈 검색 방식은 디스크 I/O작업 : 계속해서 폴더와 파일을 실제로 열고 닫으며 탐색
yarn berry
: yarn V2이상의 yarn을 이르는 명칭
- yarn V1은 yarn classic
- PNP(Plug’n’ Play) : 패키지에 대한 정보는 node_modules대신 .zip파일로 압축하여 .yarn/cache폴더에 저장
- 이를 찾기 위한 정보를 .pnp.cjs파일에 생성후 의존성 트리 정보를 단일 파일에 저장
- 이를 Interface Linker라 한다.
- 직접 파일시스템에 접근하여 I/O를 실행하던 requre문의 비효율을 자료구조를 메모리에 올리는 방식으로 탐색을 최적화
- Zero Install : 의존성까지 github에 올릴 수 있다
- yarn berry를 통해 만든 의존성 폴더는 200mb를 거의 넘지 않는다
- git clone이후 별도의 설치 없이 바로 사용할 수 있다
package-lock.json은 왜 필요할까?
package-lock.json파일은 의존성 트리에 대한 정보를 모두 가지고 있습니다.package-lock.json파일은 저장소에 꼭 같이 커밋해야 합니다.package-lock.json파일은node_modules없이 배포하는 경우 반드시 필요합니다.
Package.json과 Package-lock.json의 차이를 아시나요?
- package.json : 버전 정보를 저장할 때 version range를 사용
- ^ : major만 고정, minor, patch는 최신꺼로 사용
- package-lock.josn : 버전이 명확히 명시되어 있다.
- package.josn과 package-lock.json 모두 존재할 때, package-lock.json을 사용하여 node_modules를 생성한다. 더 정확한 버전의 패키지를 다운로드하기 위함이다.
node_modules로부터 우리를 구원해 줄 Yarn Berry
비효율적인 의존성 검색
- node_modules폴더의 의존성 검색은 매우 비효율적이다. 파일 I/O방식으로 직접 파일과 폴더를 열고 닫아 찾기 때문이다.
-
require()문을 사용하여 react패키지를 불러올 때, Node.js에서 제공하는 require.resolve.paths()함수를 사용하여 패키지를 검색한다.
위 사진 처럼 npm은 패키지를 찾기 위해 계속 상위 디렉토리의 node_modules폴더를 탐색한다.
만약 바로 찾지 못하면 readdir,stat같은 느린 I/O호출이 반복된다.
-
환경에 따라 달라지는 동작
비효율적인 설치
- 매우 큰 공간을 차지한다.
유령 의존성
-
nom과 yarn V1은 중복해서 설치되는 node_modules를 아끼기 위해 호이스팅 기법을 사용한다.
A(1.0)과 B(1.0) 패키지는 동일한 버전의 패키지이기에 디스크 공간을 아끼기 위해 공통된 패키지는 최상단으로 끌어올리는 hoisting을 수행한다.
이렇게, 의존성 트리가 바뀌면서 원래 package-1에서 require할 수 없었던 B(1.0라이브러리를 불러올 수 있게 된다. 이렇게 끌어올리기에 따라 직접 의존하고 있지 않은 라이브러리를 require()할 수 있는 현상을 유령 의존성이라고 부른다.
이 때, package.josn에 명시하지 않은 라이브러리를 조용히 사용할 수 있게 된다.
PNP
- pnp 켜기
- npm install -g yarn
- yarn set version berry
yarn install- .yarn > cache에 .zip파일로 패키지가 저장된다.
- .pnp.cjs파일에 의존성을 찾을 수 있는 정보를 기록한다.
/* 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"]
],
}]
]],
특정 패키지와 의존성에 대한 정보가 필요할 때 바로 알 수 있다.
- ZIP아카이브 의존성 관리의 장점
- node_modules디렉토리 구조를 생성할 필요 없어 설치가 빠르다
- 각 패키지는 버전마다 하나의 ZIP아카이브를 가지이게 중복해서 설치하지 않는다.
- 의존성을 구성하는 파일의 수가 많지 않으므로 , 변경사항을 감지하거나 전체 의존성을 삭제하는 작업이 빠르다
리멤버 웹 서비스 좌충우돌 Yarn Berry 도입기
- pnpm + Turborepo 적용 → yarn berry 미사용
yarn berry로 마이그레이션
-
yarn 버전 변경
yarn set version berry -
.gitignore 설정
.yarn/* !.yarn/cache !.yarn/patches !.yarn/plugins !.yarn/releases !.yarn/sdks !.yarn/versions!: 제외할 경로에서 빼달라는 뜻 -
.yarnrc.yarml수정nodeLinker: pnp yarnPath: .yarn/releases/yarn-3.3.0.cjs -
yarn install
- 깨진 의존성 발견 시, packageExtensions에 추가하여 의존성 추가 설치
yarn berry와 IDE통합
- VS Code Extension > ZipFS 설치 : zip파일로 설치된 종속성 읽을 수 있음
yarn install -D typescript eslint prettieryarn dlx @yarnpkg/sdks vscode: 관련 세팅을 포함한 .vscode 폴더 생성- cmd + shift + p > typescript version을 Workspace version으로 수정
트러블 슈팅
install-state.gz은 최적화 관련 파일이기에 커밋할 필요가 없다.yarn/unplugged: zip로 묶이지 않고 압축해제된 종속성이 설치되는 경로
Yarn berry로 프로젝트 세팅하기
- Zero-installs : 설치 하지 않고 이용하는 방식
- PnP설정을 통해 사용할 수 있다.
- PnP모드로 설정하고 모듈을 설치하면 node_modules가 아니라 .yarn/.cache폴더에 zip아카이브 파아ㅣㄹ로 관리된다. 이 모듈의 의존성 트리 정보는 .pnp.cjs파일로 관리하게 된다.,
- 장점 : 용량이 준다
- 단점 : 의존성 에러가 발생할 수 있다. 각 모듈이 PnP 방식에 맞게 의존성 관리가 세팅이 되어있어야 한다.
- 이로인해 pnp loose모드를 통해 명시적으로 요구하는 의존성을 요구하지 않을 수 있다.
// .yarn.yml
pnpMode : loose
-
Zero install 적용
# .gitignore .yarn/* !.yarn/cache !.yarn/patches !.yarn/plugins !.yarn/releases !.yarn/sdks !.yarn/versions -
Zero Install 미적용
# .gitignore .pnp.* .yarn/* !.yarn/cache !.yarn/patches !.yarn/plugins !.yarn/releases !.yarn/sdks !.yarn/versions