npm, pnpm의 패키지 관리 방법 비교

dahyeon·2023년 4월 6일
2
post-thumbnail
post-custom-banner

1. npm의 패키지 관리 방법

만약 express가 debug라는 모듈에 의존한다면 node_modules는 어떻게 생겼을까?

→ npm@3 이전에는 A였다면, npm@3부터는 B의 방식으로 변경되었다.

A의 문제점

  • deep dependency trees를 형성하게 된다 → directory paths가 너무 길어질 수 있다.
  • 패키지들이 서로 다른 dependencies에 필요할 경우 패키지의 복제본이 여러 개 존재하게 된다.
  • 일부 패키지는 복제본이 있을 경우 오류가 발생할 수 있다.

그래서 npm@3부터는 ‘flattening’을 통해 node_modules의 구조가 B처럼 변경되었다.

B(flattened dependency trees)의 문제점

  • 모듈이 스스로가 의존하지 않는 패키지에 접근할 수 있게 되었다.
  • 의존성 트리를 flattening 하는 과정은 다소 복잡하다.
  • 경우에 따라 패키지가 복제될 수 있다.
    • 아래 그림은 A v1.0이 B v1.0에 의존하며, A가 패키지 중 가장 먼저 설치된 상황이다. 이후 B v2.0에 의존하는 C v1.0과 D v.1.0이 각각 설치된다면, 이미 top-level에 B v1.0이 설치되어 있으므로, nested dependency로 각각 설치된다.
      → 설치 순서에 따라 node_modules 내 폴더 구조가 달라진다.

2. pnpm의 패키지 관리 방법

A가 가진 문제점을 npm에서는 flattening을 통해 해결했다면, pnpm에서는 다른 방법으로 해결했다.

  • node_modules 폴더 아래 모든 패키지는 각자의 dependencies를 갖는다.
  • 하지만 pnpm은 symlinks를 통해 dependences를 flat하게 유지한다.

symlink란?

symlink란?

  • 특정 파일이나 디렉터리에 대한 참조를 포함하는 특별한 파일. Windows 운영체제에서 바로 가기와 같은 기능을 한다.

예시

foo가 bar에 의존할 때, npm에서는 아래와 같이 구성된 구조가

node_modules
├─ foo
|  ├─ index.js
|  └─ package.json
└─ bar
   ├─ index.js
   └─ package.json

pnpm에서는 아래와 같이 구성된다.

-> - a symlink (or junction on Windows)
node_modules
├─ foo -> .registry.npmjs.org/foo/1.0.0/node_modules/foo
└─ .pnpm
	└─ .registry.npmjs.org
	   ├─ foo/1.0.0/node_modules
	   |  ├─ bar -> ../../bar/2.0.0/node_modules/bar
	   |  └─ foo
	   |     ├─ index.js
	   |     └─ package.json
	   └─ bar/2.0.0/node_modules
	      └─ bar
	         ├─ index.js
	         └─ package.json
  • require(’foo’)를 하면 node_modules/foo/index.js 가 아닌 node_modules/.registry.npmjs.org/foo/1.0.0/node_modules/foo/index.js
    의 파일을 실행한다.
  • 만약 bar이 또 패키지 c에 의존한다면? foo가 bar을 의존하는 구조처럼 상위 bar/2.0.0/node_modules 안에 c에 대한 symlink가 형성될 것이고, c는 .registry.npmjs.org 아래 형성될 것이다.
    → deep dependency tree를 형성하지 않는다.
  • bar은 node_modules 디렉터리 바로 아래에 있지 않기 때문에 코드에서 접근할 수 없다.

장점

  • 복제본이 존재하지 않는다.
  • 의존하지 않는 패키지에 대해 접근할 수 없다 → 더 안정적이고, 예측 가능하다.

3. pnpm과 모노레포

모노레포의 개념

  • Monolithic Repository: 두 페이지를 구분 없이 섞어서 구성하고 참조해 독립이라는 느낌이 안 드는 경우
  • MonoRepo: 독립된 각 프로젝트를 하나의 레포지토리에 묶는 방식 ‘독립된’ 이라는 개념(모듈화)이 핵심 서로 패키지처럼 참조

장점

  • 기능을 모듈로 분리함으로써 캡슐화를 이룰 수 있다.
  • 소프트웨어 확장이 쉬워진다.
  • 테스트가 쉬워진다.
  • PolyRepo에 비해 각 패키지를 넘나드는 작업을 하기에 편리하다. 서로 참조하기가 쉽다.
  • Unified Versioning: 버전 관리를 한 번에 할 수 있다.
  • 외부 디펜던시 관리: 외부 디펜던시의 버전을 맞추기 용이해진다.

pnpm의 workspace 프로토콜

workspace란?

  • 하나의 레포지토리 안에 다수의 프로젝트가 존재하여 서로에 대한 의존성을 형성하고 있는 구조.

  • pnpm은 이용 가능한 패키지가 선언된 버전의 범위와 일치하는 경우에 workspace 내의 패키지를 참조한다.

    • e.g.) bar이 "foo": "^1.0.0" 에 의존하고 있고, foo@1.0.0 이 워크스페이스에 있다면 foo@1.0.0 는 bar와 연결(linked)된다. 하지만 "foo": "2.0.0" 에 의존하고 있다면 새로 설치된다.
  • workspace:* workspace에 있는 버전의 패키지를 사용한다.

    • e.g.) b가 패키지 a에 의존한다면?
      b의 package.json에 다음과 같이 명시해주면 된다.
    {
        ...
      "dependencies": {
        "a": "workspace:*",
      }
    }

참고자료

Why should we use pnpm?

The Case for pnpm Over npm or Yarn

Dev: MonoRepo 개념 알아보기

Managing a full-stack, multipackage monorepo using pnpm - LogRocket Blog

Workspace | pnpm

profile
https://github.com/dahyeon405
post-custom-banner

0개의 댓글