안녕하세요. J4J입니다.
이번 포스팅은 pnpm은 무엇이고, react 프로젝트를 npm에서 pnpm으로 이관하는 방법에 대해 적어보는 시간을 가져보려고 합니다.
pnpm
pnpm은 node 기반의 패키지 매니저 중 하나로 우리에게 익숙한 npm, yarn 등과 동일한 목적으로 사용되는 도구입니다.
그러면 npm, yarn과 비교했을 때 pnpm이 가지고 있는 차이점은 무엇일까요?
package.json에 설정되어 있는 의존성을 이용하여 node_modules 구성을 위해 npm, yarn을 지금까지 많이 사용해 왔습니다.
사용해 오면서 한 번씩 체감할 수 있는 것 중 하나는 node_modules가 정말 무거운 존재라는 것입니다.
어느 정도 규모가 있는 프로젝트를 내려받아 로컬 환경에서 동작을 하기 위해 node_modules를 설치를 시도하면 설치가 완료되기까지 정적의 시간이 꽤나 필요합니다.
pnpm은 이런 문제를 해결하는데 도움을 줄 수 있는 도구가 됩니다.
pnpm이란 존재를 처음 접하게 되었을 때 가장 먼저 들었던 내용 중 하나는 pnpm은 다른 패키지 매니저와 다르게 "node_modules를 프로젝트에 설치하지 않는다" 였습니다.
이 말이 틀린 말은 아니지만, 로컬 환경에 의존성 패키지를 설치하지 않는 건가라는 오해를 만들 수 있었습니다.
pnpm에 대해 정확히 얘기를 해보면, 의존성 패키지를 전역 store에 한 번만 설치하고 하드 링크와 심볼릭 링크를 이용하여 프로젝트들의 node_modules를 구성해 주는 도구로 말할 수 있습니다.
예를 들어, A/B 프로젝트에 모두 react 19.2.3 버전을 사용하고 있다고 가정하겠습니다.
npm, yarn을 이용하여 의존성을 설치하고자 하면 프로젝트의 node_modules에 각각 설치가 되는 것을 경험하게 됩니다.
하지만 pnpm을 이용하면 전역 store에 react 19.2.3 버전의 설치를 한 번만 수행하고, 각 프로젝트에서는 링크를 통해 설치 없이 사용하는 구조가 됩니다.
이런 특징을 가지고 있기 때문에 monorepo, micro frontend 등의 환경에서 pnpm은 강력한 존재가 될 수 있습니다.
작게 나눠져 있는 프로젝트들마다 매번 설치가 필요했던 상황에서, 1번의 설치로 모든 프로젝트에 공유가 되기 때문에 속도 및 디스크 등에 굉장한 이점이 생깁니다.
그러나 반대로 1개의 프로젝트만 관리하고, 심지어 프로젝트의 규모조차 굉장히 작은 곳이라면 pnpm을 사용한다고 하더라도 npm/yarn과 비교했을 때 큰 체감이 발생하지 않을 수 있습니다.
pnpm의 링크 방식
pnpm은 어떻게 이런 이점들을 제공하는지를 알기 위해서는 링크 방식에 대한 이해가 필요합니다.
가장 먼저 pnpm을 이용하여 의존성 패키지를 설치하면 전역 store에 설치가 되어 있는지 확인하고 존재하지 않다면 설치가 이루어집니다.
그래서 react를 설치한다고 가정하면 다음과 같은 구성이 됩니다.
pnpm-store
ㄴ react@19.2.3
다음으로는 하드 링크에 대해서 알아야 합니다.
하드 링크는 같은 파일 데이터를 여러 경로로 공유하는 것을 의미합니다.
이를 이해하기 위해서는 파일 시스템의 구조에 대한 이해가 먼저 필요합니다.
파일 시스템은 다음과 같은 구조들이 존재합니다.
- directory entry → 사람이 보는 경로
- inode → 파일에 대한 메타 정보
- data blocks → 실제 파일 데이터
즉, 파일의 경로는 서로 다르지만 바라보는 inode가 동일하다면 복사 없이 동일한 파일을 사용하고 있는 것입니다.
그래서 하드 링크를 하게 되면 복사를 하는 것이 아니라 다음과 같은 구조가 만들어지는 것입니다.
pnpm-store/react@19.2.3 ─┐
├── inode 111 ── "react 파일 데이터"
custom-path/react@19.2.3 ─┘
pnpm은 하드 링크를 이용하여 store에 설치된 의존성 패키지를 각 프로젝트의 node_modules에 연결합니다.
그래서 한 번 store에 구성된 패키지의 경우 추가적인 설치나 디스크를 차지하는 것이 필요 없고 결과적으로 다음과 같은 구조가 나타나게 됩니다.
project-A
ㄴ node_modules/.pnpm/react@19.2.3/node_modules/react (pnpm-store hard link)
project-B
ㄴ node_modules/.pnpm/react@19.2.3/node_modules/react (pnpm-store hard link)
다음으로는 심볼릭 링크에 대해 알아야 합니다.
심볼릭 링크는 단순히 특정 경로만을 가리키는 파일 링크를 의미합니다.
파일을 복사하지 않고 경로만 바라보는 수준으로 되어 있기 때문에, 심볼릭 링크를 위해 사용 했던 원본 경로에 문제가 생기면 심볼릭 링크도 문제가 생길 수 있습니다.
pnpm에서 심볼릭 링크가 필요한 이유는 하드 링크 된 의존성 패키지들을 node가 읽을 수 있는 node_modules 경로에서 참조 할 수 있도록 구성하기 위함입니다.
하드 링크된 경로를 살펴보면 node_modules 하위 경로 구성이 npm/yarn을 이용하여 구성할 때와 다르다는 것을 확인할 수 있습니다.
그러기 때문에 node가 module 정보를 읽는 규칙과도 맞지 않습니다.
node가 올바르게 module 정보를 읽게 하기 위해서는 node_modules 바로 하위에 의존성 패키지의 정보가 담겨 있어야 합니다.
그러므로 심볼릭 링크를 활용하여 다음과 같은 구조가 나타나며, 이를 통해 최종적으로 pnpm으로 설치된 의존성 패키지들을 각 프로젝트에서 사용할 수 있게 됩니다.
project-A
ㄴ node_modules
ㄴ react → .pnpm/react@19.2.3/node_modules/react (symbolic link)
ㄴ .pnpm/react@19.2.3/node_modules/react (pnpm-store hard link)
pnpm-store
ㄴ react@19.2.3
React 프로젝트 이관하기
pnpm이 무엇인지에 대해 알았다면 react 프로젝트를 npm에서 pnpm으로 이관해 보겠습니다.
제가 시도하는 방법 외에도 더 많은 내용들은 pnpm 공식 문서에서 확인해 주시면 됩니다.
[ 1. corepack & pnpm 설치 ]
corepack은 패키지 매니저의 버전을 프로젝트 단위 별로 자동 관리해 주는 도구입니다.
예를 들어, 동일한 프로젝트여도 각 개발자 별로 서로 다른 pnpm 버전을 사용하는 경우가 있을 수 있는데, corepack을 사용한다면 pnpm 명령 수행을 항상 동일한 버전으로 수행할 수 있게 됩니다.
또한 pnpm을 이용할 때는 corepack을 사용하는 것을 권장하고 있습니다.
$ corepack enable // corepack 활성
$ corepack prepare pnpm@latest --activate // corepack이 사용할 기본 pnpm 버전 설정
명령이 올바르게 수행될 경우 pnpm도 함께 설치된 것을 확인할 수 있습니다.
다음 명령어를 통해 버전이 올바르게 확인되는지 검증합니다.
$ pnpm -v
[ 2. package manager 설정 ]
corepack 설정이 되었다면 작업하는 프로젝트에 사용되는 pnpm 버전이 자동 관리 될 수 있도록 버전을 명시해야 합니다.
// package.json
{
"packageManager": "pnpm@10.31.0"
}
이렇게 설정을 해두면 함께 협업하는 개발자는 다음과 같은 절차를 통해 개발을 진행하면 됩니다.
- corepack enable 수행
- 프로젝트를 수행하는 곳에서 pnpm install 수행 → 자동으로 버전 확인하고 없는 경우 pnpm 버전 설치
[ 3. npm 관련 파일 삭제 ]
pnpm으로 작업을 할 것이기 때문에 npm을 사용하여 생성되는 파일들을 삭제해 줍니다.
삭제해야 되는 곳은 다음과 같습니다.
- node_modules
- package-lock.json
[ 4. pnpm 의존성 설치 ]
다음 명령어를 통해 pnpm 의존성을 설치합니다.
설치가 완료되면 하드 링크, 심볼릭 링크를 이용한 node_modules 하위 구성들과 pnpm-lock.yaml 파일이 만들어지는 것을 확인할 수 있습니다.
$ pnpm install
[ 5. pnpm 명령어 정리 ]
pnpm 설치가 모두 완료되면 npm으로 사용하던 명령어들을 모두 pnpm 기반으로 변경해야 합니다.
목적에 따라 명령어를 작성할 때 참고해 주시면 됩니다.
- (의존성 설치) npm install → pnpm add
- (의존성 삭제) npm uninstall → pnpm remove
- (script 기반 명령) npm run build → pnpm run build
CI 파이프라인
react 기반의 프로젝트를 배포하는 방식은 정말 다양하게 존재합니다.
그중 정말 간단한 파이프라인 예제를 작성해 보려고 합니다.
파이프라인을 통해 배포하는 과정에 중요한 사항은 다음과 같습니다.
- pnpm store 재 사용 (캐싱 이용)
- pnpm-lock 이용
먼저 pnpm store의 경우 pnpm-lock 파일의 변경이 없을 경우 재 사용하여 구성하는 방향을 적용할 수 있습니다.
설정을 하게 되면 다음 파이프라인을 동작할 때는 pnpm store에 있는 것을 하드 링크하여 바로 사용하기에 더 빠른 속도를 제공하게 됩니다.
다음은 pnpm-lock을 이용하는 것입니다.
pnpm-lock은 npm 기준으로는 package-lock과 동일한 역할을 하고, ci 동작을 할 때 lock 파일을 사용해야 하는 이유에 대해서는 npm install vs npm ci: CI 환경에서 package-lock의 진짜 역할을 참고해 주시길 바랍니다.
npm install vs npm ci: CI 환경에서 package-lock의 진짜 역할
안녕하세요. J4J입니다. 이번 포스팅은 npm install과 npm ci의 비교, package-lock의 역할에 대해 적어보는 시간을 가져보려고 합니다. npm install과 package.json node 기반의 프로젝트를 개발하는 곳에서 많이
jforj.tistory.com
그래서 최종적으로 gitlab ci를 이용한다면 다음과 같이 파일을 구성할 수 있습니다.
배포를 어떤 식으로 하고 있는지에 따라 구성이 달라질 수 있다는 점은 참고 바랍니다.
// .gitlab-ci.yml
variables:
PNPM_STORE_DIR: .pnpm-store
stages:
- build
# pnpm-lock 파일을 이용하여 캐싱 처리, 파일 변경이 없다면 다음 pipeline 에서도 재 사용
cache:
key:
files:
- pnpm-lock.yaml
paths:
- .pnpm-store
policy: pull-push
# job 실행 전 마다 corepack 설정 적용
before_script:
- corepack enable
- pnpm config set store-dir $PNPM_STORE_DIR
build:
image: node:24.12.0
stage: build
script:
- pnpm install --frozen-lockfile # pnpm-lock 파일을 이용하여 의존성 설치
- pnpm build
이상으로 pnpm은 무엇이고, react 프로젝트를 npm에서 pnpm으로 이관하는 방법에 대해 간단하게 알아보는 시간이었습니다.
읽어주셔서 감사합니다.
'Language > JavaScript' 카테고리의 다른 글
| npm install vs npm ci: CI 환경에서 package-lock의 진짜 역할 (0) | 2026.02.19 |
|---|---|
| npm 패키지 배포, 처음부터 자동화까지 한 번에 정리 (0) | 2026.01.04 |
| [JavaScript] 비동기 처리와 Callback, Promise, Async/Await (0) | 2021.06.29 |
| [JavaScript] 클로저 (Closure) (0) | 2021.06.22 |
| [JavaScript] This와 Call, Apply, Bind (0) | 2021.06.21 |
댓글