# [ 살펴보기 ] Pnpm - Basics

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](https://pnpm.io/installation)을 참고하자.

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

```plaintext
corepack enable pnpm
```

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

```plaintext
corepack use pnpm@latest
```

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

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

* npm install → pnpm install
    
* npm install &lt;package&gt; → pnpm add &lt;package&gt;
    
* npm run &lt;command&gt; → pnpm &lt;command&gt;
    
* npm uninstall &lt;package&gt; → pnpm remove &lt;package&gt;
    

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

## Node\_modules link 구조

PNPM [documentation](https://pnpm.io/symlinked-node-modules-structure)에서 사용하는 예제를 통해 node\_modules에 link structure가 어떻게 형성되는지 살펴보자.

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

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

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

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

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

```plaintext
 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를 가리킨다.

```plaintext
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`이다.

```plaintext
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가 다음과 같다고 가정해보자.

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

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

```plaintext
{
  ...
  "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이 나와 있다면 아래와 같은 상황이 가능하다.

```plaintext
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를 구성한다.

```plaintext
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](https://pnpm.io/how-peers-are-resolved) )
