Skip to main content

Command Palette

Search for a command to run...

[ 살펴보기 ] Pnpm - Basics

Updated
4 min readView as Markdown
[ 살펴보기 ] Pnpm - Basics
C

A developer living in Busan, Korea

Pnpm을 통해 package를 설치하면 Pnpm은 global store에 해당 package를 설치하고 project 폴더의 node_modules에는 package대신 .pnpm 폴더를 생성하여 그곳에 global store에 설치된 package에 대한 hard link를 생성한다. 그리고 project의 node_modules에 각 package에 대한 symlink를 생성한다. ( symlink는 .pnpm에 생성된 hard link를 가리킨다 )

다른 project에서 특정 package를 설치할 때 해당 package가 global store에 존재하면 해당 package를 새로 설치하는 대신 project의 node_modules/.pnpm 파일에 global store에 존재하는 package에 대한 hard link를 생성하여 이미 설치된 package를 그대로 사용한다.

Pnpm은 이러한 방법을 통해 서로 다른 프로젝트에서 같은 package를 할 때 해당 package를 두 곳에서 다 설치하는 대신 이미 설치된 package를 다시 사용할 수 있게 함으로서 사용자의 disk space를 아껴준다.

Installation & Basic Cli

Corepack을 사용하면 pnpm을 바로 사용할 수 있게 해주므로. Corepack을 통해 pnpm을 사용하는 법을 살펴보자. Node 16.13 version 이상을 사용하고 있다면 corepack을 사용할 수 있다.

만일 다른 방법을 통해 pnpm 설치를 원한다면 documentation을 참고하자.

다음 명령어를 실행시켜 pnpm을 사용할 수 있는 상태로 만들어 준다.

corepack enable pnpm

그리고 project 폴더에서 다음 명령어를 실행하면 해당 project가 특정 pnpm version을 사용하도록 설정할 수 있다.

corepack use pnpm@latest

위의 명령어를 실행하면 project package.json에 packageManager property가 추가되고 packageManager property에 위의 명령어를 통해 설정한 pnpm version 정보가 추가된다.

Command Line Interface는 npm과 일부 다른 부분도 있지만 크게 다르진 않다.

  • npm install → pnpm install

  • npm install <package> → pnpm add <package>

  • npm run <command> → pnpm <command>

  • npm uninstall <package> → pnpm remove <package>

Pnpm Command Line Interface는 다른 포스트에서 보다 자세히 살펴보기로 하자.

PNPM documentation에서 사용하는 예제를 통해 node_modules에 link structure가 어떻게 형성되는지 살펴보자.

현재 project의 dependency가 다음과 같다고 가정해보자.

{
  ...
  "dependencies": {
    "foo": "1.0.0"
  }
}

그리고 foo@1.0.0 package는 bar@1.0.0 package를 dependency로 가지고 있다고 가정한다. 즉, foo@1.0.0 package의 package.json은 아래와 같다.

{
  ...
  "dependencies": {
    "bar": "1.0.0"
  }
}

pnpm가 위 dependency를 설치할 때 project node_modules/.pnpm에 아래와 같이 각 packages에 대한 hard link를 생성한다.

 node_modules
└── .pnpm
    ├── bar@1.0.0
    │   └── node_modules
    │       └── bar -> <store>/bar
    │           ├── index.js
    │           └── package.json
    └── foo@1.0.0
        └── node_modules
            └── foo -> <store>/foo
                ├── index.js
                └── package.json

그런 다음 bar는 foo package의 dependency이므로 .pnpm/foo@1.0.0/node_modules에 bar package의 symlink가 추가된다. 이 때 추가된 bar symlink는 node_modules/.pnpm에 추가된 bar package hard link를 가리킨다.

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

그리고 project의 node_modules에 project direct dependency의 symlink가 추가된다. 위의 예제에서 project의 direct dependency는 foo@1.0.0이다.

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

만약 같은 package지만 다른 version의 pacakge가 peerDependency로 있으면 link 구조는 어떻게 될까? project의 direct dependency가 다음과 같다고 가정해보자.

{
  ...
  "dependencies": {
    "foo-address":"1.0.0",
    "foo-name":"1.0.0",
  }
}

그리고 foo-address, foo-name 모두 foo@1.0.0을 dependency로 가지고 있고 foo package의 package.json은 다음과 같다고 가정해보자.

{
  ...
  "peerDependencies": {
    "baz":"^1",
    "bar":"1.0.0",
  },
}

위에서 foo package의 peerDependency에 정의된 baz package는 carrot version range를 허용한다. 만약 project 초기에는 foo-name package 없이 진행하다 시간이 지나 필요에 의해 foo-name package를 설치하는데 baz package의 새로운 minor version이 나와 있다면 아래와 같은 상황이 가능하다.

foo-address@1.0.0
    - foo@1.0.0
        - baz@1.0.0
        - bar@1.0.0

foo-name@1.0.0
    - foo@1.0.0
        - baz@1.1.0
        - bar@1.0.0

만약에 dependency graph가 위와 같은 상황일 때 pnpm은 대략 다음과 같이 project dependency에 대한 link를 구성한다.

node_modules
└── .pnpm
    ├── foo@1.0.0_bar@1.0.0+baz@1.0.0
    │   └── node_modules
    │       ├── foo
    │       ├── bar   -> ../../bar@1.0.0/node_modules/bar
    │       ├── baz   -> ../../baz@1.0.0/node_modules/baz
    ├── foo@1.0.0_bar@1.0.0+baz@1.1.0
    │   └── node_modules
    │       ├── foo
    │       ├── bar   -> ../../bar@1.0.0/node_modules/bar
    │       ├── baz   -> ../../baz@1.1.0/node_modules/baz
    ├── bar@1.0.0
    ├── baz@1.0.0
    ├── baz@1.1.0

즉, package는 같지만 다른 version의 peerDepency가 존재하는 상황일 때는 위의 예제와 같이 같은 package의 link가 실제 의존하는 peerDepency의 version에 맞게 여러개 생길 수 있다. 위의 예제에서는 foo에 대한 link가 두 개 생긴 것을 확인할 수 있다.

PeerDependency reposution에 대한 보다 자세한 정보는 documentation을 통해 확인할 수 있다. ( How peers are resolved )

More from this blog

[ 살펴보기 ] TypeORM - Transactions, Migration

Transation Database 종류에 따라 detail한 부분은 차이점이 조금씩 있겠지만 각 sql statement는 개별적인 transaction block을 통해 실행되며 Database 설정에 따라 sql statement의 실행 결과가 자동으로 commit되어 영구히 적용되거나 commit을 직접 실행하기 전까지는 영구히 적용되지 않을 수 있다. 대부분의 경우 default로 sql statement 실행 결과가 자동으로 comm...

Feb 9, 20256 min read
[ 살펴보기 ] TypeORM - Transactions, Migration

Dev Diary

184 posts