[F-Lab 모각코 챌린지 53일차] pnpm

Nami·2023년 7월 23일
0

66일 포스팅 챌린지

목록 보기
53/66

어제 패키지 매니저를 알아봤는데 pnpm이 yarn과 npm이랑 작동 방식이 다르다고 해서 구체적으로 어떻게 다른지 궁금해서 더 알아보려한다!


PNPM

Zoltan Kochan에 의해 2017년에 출시되었다. npm에 대한 드롭 인 대체(Drop-in replacement) 이므로 npm 프로젝트가 있으면 바로 pnpm을 사용할 수 있다!

드롭 인 대체(Drop-in replacement)
컴퓨터 과학 및 기타 분야 에서 사용되는 용어. 다른 코드나 설정 변경 없이 하드웨어(또는 소프트웨어) 구성 요소를 다른 하드웨어의 (또는 소프트웨어) 구성 요소로 대체해도 부정적인 영향을 미치지 않는 기능을 나타낸다.

유령 의존성(phantom dependency)

yarn은 패키지들을 가져와서 설치할 때 병렬적으로 설치를 한다. 패키지가 여러개 있다면 순차적으로 설치하는 npm보다 yarn이 훨씬 빠르게 된다. 또한 오프라인 미러가 있어서 오프라인에서도 패키지를 받아 설치할 수 있다.

yarn은 npm의 좋은 대체재였지만 두 패키지 매니저 모두 몇 가지의 문제를 가지고 있었다. 그중 하나는 flat한 의존성 트리를 만드는 알고리즘의 비용이 많이 든다는 것. 그리고 또 다른 이유는 유령 의존성(phantom dependecy)이다.

예시
my-library/package.json

{
  "name": "my-library",
  "version": "1.0.0",
  "main": "lib/index.js",
  "dependencies": {
    "minimatch": "^3.0.4"
  },
  "devDependencies": {
    "rimraf": "^2.6.2"
  }
}

위 package.json으로 아래my-library/lib/index.js을 실행할 수 있다.

var minimatch = require("minimatch")
var expand = require("brace-expansion");  // ???
var glob = require("glob")  // ???

// (more code here that uses those libraries)

package.json에는 brace-expansion 패키지와 minimatch 패키지가 없지만 에러가 나지 않는다.
brace-expansionminimatch와 의존 관계에 있는 패키지이고 globrimraf의 의존 관계에 있는 패키지다. 내가 사용하기 위해 입력한 패키지는 아니었지만 의존 관계 덕에 사용이 가능하다. 문제는 유령 의존성에 있는 패키지의 관리가 어렵다는 것이다.

  • 사용하지 않는 패키지 정리가 어렵다.
  • 버전 충돌 위험성 (어떤 버전을 사용할 것인지에 대한 내용이 없다.)

saving disk space

npm 및 Yarn Classic은 의존성 접근 방식이 동일 했다. 호이스팅을 사용하여 node_modules를 플랫했다. pnpm은 호이스팅 대신 의존성 해결 전략의 대안책으로 내용 주소화 저장소(Content-addressable storage) 를 도입했다.

단순하게 파일 이름으로 해당 파일을 접근하는 것이 아니라 각각의 의존성 파일에 hash id를 부여하고 관리한다.이 과정에서 중복되는 패키지는 동일한 hash id를 얻게 된다.

이 방법을 사용하면 홈 폴더(~/.pnpm-store/)의 전역 저장소에 패키지를 저장하는 중첩된 node_modules 폴더가 생성된다.

예를 들어 npm을 사용할 때 종속성을 사용하는 프로젝트가 100개인 경우, 해당 종속성의 사본 100개가 디스크에 저장된다. 하지만 pnpm을 사용하면 content-addressable 저장소에만 저장된다.

과정

  1. 다른 버전의 의존성에 의존하는 경우, 다른 파일만이 저장소에 추가된다.

    예를 들어, 100개의 파일이 있고 새 버전이 그 중 하나만 변경되면, pnpm update 는 단일 변경에 대해 전체 파일이 복제되는 대신, 저장소에 1개의 새로운 파일만 추가한다.

  2. 모든 파일은 디스크 상에 한 위치에 저장된다. 패키지가 설치될 때 그 파일들은 단일 위치에서 하드링크되며 추가적인 디스크 공간을 소비하지 않는다.

    따라서 프로젝트 간에 동일한 버전의 의존성을 공유할 수 있다!

결과적으로, 디스크 공간은 프로젝트와 의존성의 수에 비례하여 더 많이 절약되고 더 빠르게 설치할 수 있게 되는 것이다.

하드 링크
파일 시스템에서 사용되는 개념으로, 하나의 파일에 대해 여러 개의 이름(경로)을 가질 수 있도록 하는 방식. 하드링크를 통해 같은 파일의 여러 이름이 서로 다른 디렉토리에 존재하는 것처럼 보이게 된다. 이러한 링크들은 원본 파일과 동일한 데이터를 공유하며, 하드링크들 중 하나를 수정하면 나머지 하드링크들도 동일한 내용으로 수정되는 특징이 있다.

Boosting installation speed

pnpm은 3단계로 설치를 수행한다.

  1. Dependency resolution. 의존성 해결
    필요한 모든 의존성을 식별하여 저장소로 가져온다.
  2. Directory structure calculation. 디렉토리 구조 계산
    의존성을 기반으로 node_modules 디렉토리 구조가 계산된다.
  3. Linking dependencies. 종속성 해결
    의존성 연결. 나머지 모든 의존성을 저장소에서 node_modules로 하드링크 한다.

Creating a non-flat node_modules directory

node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
    ├── bar@1.0.0
    │   └── node_modules
    │       ├── bar -> <store>/bar
    │       └── qar -> ../../qar@2.0.0/node_modules/qar
    ├── foo@1.0.0
    │   └── node_modules
    │       ├── foo -> <store>/foo
    │       ├── bar -> ../../bar@1.0.0/node_modules/bar
    │       └── qar -> ../../qar@2.0.0/node_modules/qar
    └── qar@2.0.0
        └── node_modules
            └── qar -> <store>/qar

npm 또는 Yarn Classic을 사용하여 의존 항목을 설치할 때 프로젝트에 포함되지 않은 의존성(패키지)들에 대해서도 프로젝트의 소스 코드가 접근할 수 있는 상황이 발생할 수 있다. 왜냐하면 모든 패키지들이 공통된 모듈 디렉토리에 설치되기 때문에, 모듈 디렉토리 내의 모든 패키지들은 프로젝트의 어디서든 접근 가능하게 되는 것..!

pnpm은 symbolic link를 사용하여 프로젝트의 직접 의존성만 모듈 디렉토리의 루트에 추가한다. 이 레이아웃의 큰 장점은 실제로 의존성에 있는 패키지에만 액세스할 수 있다는 것.

peer dependency

pnpm의 가장 좋은 기능 중 하나는 한 프로젝트에서 패키지의 특정 버전이 항상 하나의 종속성 세트를 갖는다는 것이다. 하지만 이 규칙에는 한 가지 예외가 있다. 피어 의존성이 있는 패키지이다.
모노레포 사용시 package.jsonpeer dependency를 명시하지 않으면 사용할 수 없는 pnpm의 엄격함이 있다.
➡️ 모노레포에 익숙하지 않은 사람이 만들어내는 실수를 방지하는 것.

모노레포 monorepo

간단하게만 알아보겠다!

Google, Facebook, Microsoft, Uber, Airbnb, 그리고 Twitter 등 글로벌 테크 회사들은 이미 각자 자신들의 운영 전략 아래 대규모 모노레포를 운영하고 있다.

네이버와 같은 거대 서비스를 개발할 때, 소스 코드가 모듈화 없이 하나의 프로젝트로 구성된다면 어떻게 될까? 코드가 서로 직접적으로 의존하며 단 하나의 버전으로 관리되면서 관심 분리(separation of concerns)가 어려워지고, 설계, 리팩터링, 배포 등의 작업을 매번 거대한 단위로 처리해야 하므로 개발상 많은 제약과 비효율이 있을 것이다.

모노레포(monorepo) 구조는 두 개 이상의 프로젝트가 동일한 저장소에 저장되는 소프트웨어 개발 전략이다. 앞선 예시의 분리된 모듈들은 모노레포에서 여전히 독자 프로젝트로 존재하지만 저장소는 같은 곳을 사용한다.

모노레포의 또 다른 중요한 특징 중 하나는 프로젝트 간의 관계다. 단순히 여러 프로젝트가 하나의 저장소를 사용한다고 해서 모노레포 구조라고 부르기에는 부족하다. 흔히 모노레포에서는 프로젝트 사이에 의존성이 존재하거나 같은 제품군이거나 하는 정의된 관계가 존재한다. 모노레포 관리 도구는 모두 이러한 관계를 효율적으로 관리해 주는 도구라고 할 수 있다.

찾아볼수록 pnpm을 시도해보고 싶은 생각이 든다!
모노레포라는 프로젝트 구성 기법도 알게 되어 좋았다. 추후에 멀티 레포와 함께 깊게 파보아야겠다.


2개의 댓글

comment-user-thumbnail
2023년 7월 23일

이런 유용한 정보를 나눠주셔서 감사합니다.

1개의 답글