diff --git a/contents/development/moving-on-to-pnpm/images/actions-npm.png b/contents/development/moving-on-to-pnpm/images/actions-npm.png new file mode 100644 index 0000000..019177a Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/actions-npm.png differ diff --git a/contents/development/moving-on-to-pnpm/images/actions-pnpm.png b/contents/development/moving-on-to-pnpm/images/actions-pnpm.png new file mode 100644 index 0000000..f6046a5 Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/actions-pnpm.png differ diff --git a/contents/development/moving-on-to-pnpm/images/benchmark-table.png b/contents/development/moving-on-to-pnpm/images/benchmark-table.png new file mode 100644 index 0000000..05ffb2b Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/benchmark-table.png differ diff --git a/contents/development/moving-on-to-pnpm/images/moving-on-to-pnpm-thumbnail.png b/contents/development/moving-on-to-pnpm/images/moving-on-to-pnpm-thumbnail.png new file mode 100644 index 0000000..0f128ea Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/moving-on-to-pnpm-thumbnail.png differ diff --git a/contents/development/moving-on-to-pnpm/images/npm-clean-install(61s).png b/contents/development/moving-on-to-pnpm/images/npm-clean-install(61s).png new file mode 100644 index 0000000..82b28f2 Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/npm-clean-install(61s).png differ diff --git a/contents/development/moving-on-to-pnpm/images/npm-clone(39s).png b/contents/development/moving-on-to-pnpm/images/npm-clone(39s).png new file mode 100644 index 0000000..eecc1b1 Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/npm-clone(39s).png differ diff --git a/contents/development/moving-on-to-pnpm/images/npm3.png b/contents/development/moving-on-to-pnpm/images/npm3.png new file mode 100644 index 0000000..6892fe7 Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/npm3.png differ diff --git a/contents/development/moving-on-to-pnpm/images/pnpm-clean-install-1.png b/contents/development/moving-on-to-pnpm/images/pnpm-clean-install-1.png new file mode 100644 index 0000000..81d5036 Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/pnpm-clean-install-1.png differ diff --git a/contents/development/moving-on-to-pnpm/images/pnpm-clean-install-2(35s).png b/contents/development/moving-on-to-pnpm/images/pnpm-clean-install-2(35s).png new file mode 100644 index 0000000..c2bebfa Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/pnpm-clean-install-2(35s).png differ diff --git a/contents/development/moving-on-to-pnpm/images/pnpm-clone(30s).png b/contents/development/moving-on-to-pnpm/images/pnpm-clone(30s).png new file mode 100644 index 0000000..f34449b Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/pnpm-clone(30s).png differ diff --git a/contents/development/moving-on-to-pnpm/images/pnpm-store.png b/contents/development/moving-on-to-pnpm/images/pnpm-store.png new file mode 100644 index 0000000..e34a88d Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/pnpm-store.png differ diff --git a/contents/development/moving-on-to-pnpm/images/pnpm-structure.png b/contents/development/moving-on-to-pnpm/images/pnpm-structure.png new file mode 100644 index 0000000..c8df2e8 Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/pnpm-structure.png differ diff --git a/contents/development/moving-on-to-pnpm/images/symlink-example.png b/contents/development/moving-on-to-pnpm/images/symlink-example.png new file mode 100644 index 0000000..a3e190e Binary files /dev/null and b/contents/development/moving-on-to-pnpm/images/symlink-example.png differ diff --git a/contents/development/moving-on-to-pnpm/index.mdx b/contents/development/moving-on-to-pnpm/index.mdx new file mode 100644 index 0000000..2c47e04 --- /dev/null +++ b/contents/development/moving-on-to-pnpm/index.mdx @@ -0,0 +1,383 @@ +--- +title: 'pnpm으로 나아가기' +description: 'npm에서 pnpm으로 마이그레이션 하는 과정을 기록합니다.' +date: '2024-09-20' +slug: 'moving-on-to-pnpm' +thumbnail: './images/moving-on-to-pnpm-thumbnail.png' +thumbnail_alt: 'moving-on-to-pnpm-thumbnail' +category: 'development' +type: 'post' +--- + +# pnpm으로의 마이그레이션 과정 + +💡 npm에서 pnpm으로 마이그레이션 하는 과정을 기록합니다. + +## npm은 무엇이 문제일까? + +npm3 + +### npm 3 이전 + +초기 npm은 모든 종속성을 중첩된 방식으로 설치한다. +이러한 방식은 다음과 같은 문제점을 유발한다. + +- 의존성이 점점 중첩되면서 트리가 깊어지고 불필요한 중복이 발생한다. +- 윈도우의 경우 너무 깊은 의존성 트리가 생성되어 디렉터리 경로가 길어지면 문제가 발생한다. +- 다른 종속성에 필요할 때마다 여러 번 복사 붙여넣기를 해야 해서 용량 문제를 일으킨다. + +
+ +### npm 3 이후 + +npm 3에서는 `flat node_modules` 라는 구조 개선을 통해 기존 의존성 관련 문제들을 해결하고자 한다. + +
+ +방식은 다음과 같다. + +- `/node_modules` 경로에 평평하게 모듈들이 설치된다. +- 중복된 라이브러리(top-level에 존재하는 경우)에서 다른 버전이 필요한 경우 이를 필요로 하는 모듈 아래에 다른 버전의 의존성을 중첩한다. + +
+ +### 남아있는 이슈들 + +npm의 개선된 구조에서도 여전히 문제점들이 존재한다. + +- `package.json`에 설치하지 않는 모듈에 접근할 수 있어 잘못된 코드를 작성할 위험이 존재한다. (`ghost dependency`) +- 의존성 트리를 평평하게 만드는 알고리즘이 복잡하다. +- 몇몇 패키지(같은 패키지에서 버전이 다른 경우)는 다른 패키지의 `node_modules` 안에 복사되어 일관성을 해친다. + +
+ +npm이 가지고 있는 문제점들을 pnpm은 어떻게 해결했을까? + +## pnpm 알아보기 + +> pnpm은 Performant NPM을 의미합니다. + +### pnpm은 왜 빠른가? + +pnpm은 개선된 npm처럼 의존성 트리를 평평하게 만들려고 하지 않는다. (`flat node_modules` 방식을 따르지 않는다.) + +그렇다고 초기 npm처럼 트리가 중첩된 형태로 깊지도 않다. + +pnpm은 모든 의존성을 평평하게 유지하면서 **심볼릭 링크**(symlink)를 활용하여 그룹화시킨다. + +### global store + +> Yarn copies files from cache whereas pnpm just links them from the global store. +> +> pnpm is capable of quickly linking to the already existing files rather than downloading and extracting them again. + +
+ +pnpm에서는 `global store`라는 개념을 사용한다. +여기서 `linking` 이라는 키워드를 등장하는데 **하나의 복사본만 배치하고 하드 링크를 통해 참조하는 방식**이다. + +`copy`하는 방식이 아닌 `linking`하는 방식으로 pnpm은 이미 존재하는 파일을 빠르게 접근하고자 한 것이다. + +
+ +```bash +pnpm store path // 스토어 경로 +``` + +
+ +pnpm store + +여기에 설치된 패키지들의 원본이 저장되는 것이다. + +
+ +### virtual store + +pnpm의 `node_modules`의 대략적인 구조는 다음과 같다. + +하나씩 살펴보자. + +pnpm structure + +최상단 `node_modules`에 `.pnpm` 폴더와 `package.json`에 설치한 모듈들이 나타난다. + +여기서 모듈들은 심볼릭 링크를 가르킨다. + +symlink example + +pnpm에서는 `.pnpm` 폴더를 가상 저장소(`virtual store`)라고 지칭한다. + +
+ +**virtual store** + +```bash +node_modules/.pnpm // 해당 경로에 패키지 하드 링크가 생성 +``` + +
+ +가상 저장소에는 설치한 패키지와 버전를 기록한 폴더가 생성된다. + +
+ +**virtual store directory** + +```bash +.pnpm/@/node_modules/ +``` + +
+ +이미 동일한 패키지가 존재해도 버전에 따라 구분지어 구성된다. + +`bar`가 2개의 버전이 존재하지만 심볼릭 링크를 통해 깊게 중첩되지 않으면서 평평한 구조를 유지시켜 준다. + +```bash +node_modules +├─ foo -> .pnpm/foo/1.0.0/node_modules/foo // symlink +├─ bar -> .pnpm/bar/3.0.0/node_modules/bar // symlink +└─ .pnpm + ├─ foo/1.0.0/node_modules + | ├─ bar -> ../../bar/2.0.0/node_modules/bar // symlink + | └─ foo (actual package) // hard link + ├─ bar/2.0.0/node_modules + | └─ bar (actual package) // hard link + └─ bar/3.0.0/node_modules + | └─ bar (actual package) // hard link +``` + +
+ +> That’s why it is possible that only 1 developer could keep pace with the dozens of contributors of Yarn + +
+ +이러한 구조를 통해 훨씬 간단한 알고리즘을 활용하면서 pnpm은 한 명의 개발자가 다수의 yarn 기여자의 속도를 따라잡을 수 있었다. + +## hard link, symlink + +> a hard link is a different reference to the same file. In soft link, you create a new file, and the contents of the file point to another path. + +
+ +link라는 키워드와 관련해서 `hard link` , `symlink` 를 좀 더 정리하자. + +### hard link + +- 디스크의 실제 데이터를 직접 참조한다. +- 하나의 파일에 대해 여러 하드 링크가 동일한 데이터를 가리키며 동일한 `inode`이다. +- 하드 링크를 통해 파일이 변경되면 모든 하드 링크에 반영된다. + +
+ +### symlink + +- 다른 파일 또는 경로에 대한 포인터이다. +- 바로 가기와 유사하다. +- 원본 파일이 삭제되면 심볼릭 링크가 끊어지고 원본 파일의 데이터가 포함되지 않는다. + +
+ +## 디스크 공간의 장점 + +적절하게 링크 방식을 적용하면서 pnpm은 디스크 공간을 최적화한다. + +
+ +`global store`에서 콘텐츠 주소에 따라 패키지와 종속성을 디스크에 저장한다. +하드 링크를 통해 특정 모듈이 여러 프로젝트에 사용되어도 디스크에서 하나의 공간만 사용하게 한다. +그 다음 `node_modules` 경로에서 해당 하드 링크에 대한 심볼릭 링크를 만든다. +이를 통해 동일한 패키지가 중복해서 설치되는 현상을 막는 것이다. + +
+ +예를 들어 `temp@v1`에 500개 파일이 있고 `temp@v2`에 1개의 파일이 있다. +이때 pnpm은 기존 500개 파일에 대한 하드 링크를 생성하고 새 파일만 작성한다. +반면 npm은 `temp@v2`도 기존 500개의 파일을 복사하고 로드해서 디스크 공간의 효율성이 차이난다. + +
+ +## 엄격한 의존성 관리 + +> [left pad](https://www.theregister.com/2016/03/23/npm_left_pad_chaos/) 와 같은 이슈를 막아줍니다. + +
+ +pnpm은 최상단 `node_moduels`에 모든 것을 이동시키지 않는다. +오로지 `package.json`에 설치한 모듈(심볼릭 링크)만을 추가한다. +이를 통해 사용자는 `package.json`를 신뢰하면서 작업을 진행할 수 있게 된다. + +
+ +엄격한 의존성 관리와 겪은 개인적인 사례는 `antd`를 버전을 4 → 5로 마이그레이션할 때 경험했다. +이때 날짜 라이브러리 `moment`가 제거되었는데 빌드 에러가 발생했다. +원인은 `package.json`에 설치하지 않고 `antd` 4버전에 `moment` 의존성을 참조하여 사용하고 있었던 것이다. + +## 마이그레이션 진행하기 + +npm에서 pnpm으로 마이그레이션은 굉장히 간단한다. + +
+ +- [pnpm](https://pnpm.io/installation)을 설치한다. +- `node_modules`를 제거한다. +- `lockfile`이 존재하면 `pnpm import`를 실행해서 `pnpm-lock.yaml`을 생성한다. +- `pnpm install`을 실행해서 `node_modules`를 새로 구성한다. + +
+ +## Benchmark 테스트해보기 + +pnpm에서의 [benchmarks](https://pnpm.io/benchmarks) 방식처럼 프로젝트에 테스트를 진행해봤다. + +
+ +비교 기준은 `time` 명령어에서 체크된 시간으로 진행했다. + +```bash +time pnpm install +``` + +
+ +npm에서는 다음 명령어를 통해 캐시를 제거했다. + +```bash +npm cache clean -f +``` + +
+ +pnpm에서는 다음 명령어를 통해 캐시를 제거할 수 있다. + +```bash +pnpm store prune +``` + +
+ +혹은 스토어의 경로를 찾아가 저장된 파일들을 제거하면 된다. + +```bash +pnpm store path +``` + +
+ +대표적으로 2개의 경우만 이미지를 첨부하고자 한다. + +나머지도 동일한 방식으로 진행되어 테이블에서 결과를 확인할 수 있다. + +### clean install (42% 감소) + +
+
+ npm clean install + npm clean install(61s) + 61초 소요 +
+
+ pnpm clean install + pnpm clean install 1 + pnpm clean install 2 + 35초 소요 +
+
+ +
+ +### with lockfile (23% 감소) + +
+
+ npm with lockfile + npm clone(39s) + 39초 소요 +
+
+ pnpm witch lockfile + pnpm clone(30s) + 30초 소요 +
+
+ +
+ +### 결과 테이블 + +benchmark table + +
+ +### actions 비교 (54% 감소) + +
+
+ npm install + actions-npm + 31초 소요 +
+
+ pnpm install + actions pnpm + 14초 소요 +
+
+ +
+ +## 마무리 + +확실히 npm과 비교했을 때 굉장히 빠르다는 것을 체감할 수 있다. + +더욱이 해당 마이그레이션은 큰 비용없이 작업을 진행할 수 있어 간편하게 pnpm의 효과를 누릴 수 있다. + +
+ +## 참고 문서 + +- [Flat node_modules is not the only way](https://pnpm.io/blog/2020/05/27/flat-node-modules-is-not-the-only-way) +- [Why should we use pnpm?](https://www.kochan.io/nodejs/why-should-we-use-pnpm.html) +- [pnpm’s strictness helps to avoid silly bugs](https://medium.com/pnpm/pnpms-strictness-helps-to-avoid-silly-bugs-9a15fb306308) +- [How npm3 Works](https://npm.github.io/how-npm-works-docs/npm3/how-npm3-works.html) +- [Why I Switched From NPM/Yarn to PNPM And Why You Should Too!](https://www.youtube.com/watch?v=d1E31WPR70g) +- [pnpm](https://jeonghwan-kim.github.io/2023/10/20/pnpm) +- [Why you should prefer using pnpm over npm and yarn?](https://refine.dev/blog/pnpm-vs-npm-and-yarn/)/