핵심 요약

클라우드 배포 환경에서 패키지 매니저의 차이는 결국 “무엇을 캐시하고, node_modules를 어떻게 구성하느냐”로 갈린다.

npm
→ node_modules를 프로젝트에 직접 생성
→ npm cache를 활용해서 다운로드 비용을 줄임

pnpm
→ 실제 패키지는 pnpm store에 저장
→ node_modules는 링크 구조로 재구성
→ pnpm store 캐시가 핵심

Yarn Berry PnP
→ node_modules를 만들지 않음
→ .yarn/cache와 .pnp.cjs로 패키지 위치를 추적
→ Zero Install이면 install 단계 자체가 매우 가벼워짐

클라우드에서는 로컬처럼 node_modules가 계속 남아있는 환경이 아니기 때문에, 패키지 매니저별 장점은 “다음 배포 때 얼마나 빠르게 의존성을 복원하느냐”에서 드러난다.


1. 클라우드 배포 환경의 기본 흐름

일반적인 클라우드 CI/CD 환경은 보통 매 배포마다 깨끗한 환경에서 시작한다.

예를 들면 GitHub Actions, GitLab CI, AWS CodeBuild, Jenkins, CircleCI, Vercel 같은 환경이 그렇다.

배포 흐름은 대략 이렇다.

1. 새로운 빌드 머신 또는 컨테이너 생성
2. Git 저장소 checkout
3. 이전 빌드 캐시 복원
4. 의존성 설치
5. 프로젝트 빌드
6. 빌드 결과물 업로드 또는 배포
7. 빌드 머신 종료

여기서 중요한 점은 빌드 머신이 매번 새로 만들어질 수 있다는 것이다.

즉, 로컬 개발 환경처럼 예전의 node_modules가 자연스럽게 남아있다고 기대하면 안 된다.

그래서 클라우드 배포에서는 이 질문이 중요해진다.

node_modules를 캐시할 것인가?
패키지 매니저의 전용 캐시를 캐시할 것인가?
패키지를 Git에 포함할 것인가?
빌드 결과물만 캐시할 것인가?

패키지 매니저마다 이 질문에 대한 답이 다르다.


2. npm의 동작 방식

로컬에서의 npm

npm은 가장 기본적인 방식으로 동작한다.

package-lock.json 확인

패키지 다운로드

프로젝트의 node_modules에 패키지 설치

구조는 단순하다.

my-project
├─ package.json
├─ package-lock.json
└─ node_modules
   ├─ react
   ├─ lodash
   └─ next

npm은 프로젝트마다 node_modules 안에 패키지 파일을 설치한다.

그래서 이해하기 쉽고 호환성이 좋다.

대부분의 Node.js 도구, 프레임워크, 라이브러리는 node_modules가 있다고 가정하기 때문에 npm은 거의 모든 환경에서 안정적으로 동작한다.


일반 클라우드에서 npm

일반 CI/CD 환경에서 npm을 쓰면 보통 이렇게 동작한다.

1. Git checkout
2. npm cache 복원
3. npm ci 실행
4. node_modules 새로 생성
5. 빌드 실행

주로 캐시하는 대상은 이것이다.

~/.npm

여기에는 npm이 이미 다운로드한 패키지 tarball이나 메타데이터가 들어간다.

즉, 다음 배포 때는 패키지를 완전히 새로 다운로드하지 않고 npm cache에서 가져올 수 있다.

하지만 중요한 점은 이것이다.

npm cache는 다운로드 비용을 줄여준다.
하지만 node_modules는 보통 매번 새로 만들어진다.

그래서 npm의 클라우드 배포 특징은 이렇다.

장점:
- 호환성이 가장 좋음
- 설정이 단순함
- 대부분의 플랫폼에서 기본 지원이 좋음

단점:
- node_modules 생성 비용이 큼
- 패키지가 많으면 install 시간이 길어질 수 있음
- 디스크 중복이 생기기 쉬움

Vercel에서 npm

Vercel에서 npm을 쓰면 보통 package-lock.json을 보고 npm 프로젝트로 인식한다.

흐름은 대략 이렇다.

1. package-lock.json 감지
2. npm install 또는 npm ci 계열 실행
3. npm cache / Vercel build cache 활용
4. node_modules 생성
5. Next.js라면 .next/cache도 활용
6. 빌드 결과물 배포

npm은 Vercel에서 가장 무난한 선택지다.

특히 프로젝트가 작거나, 팀이 패키지 매니저에 크게 신경 쓰고 싶지 않다면 npm이 안정적이다.

하지만 대규모 프로젝트나 모노레포에서는 설치 시간이 길어질 수 있다.


3. pnpm의 동작 방식

pnpm store란?

pnpm store실제 패키지 파일을 모아두는 공용 저장소다.

npm은 프로젝트의 node_modules 안에 패키지를 직접 설치하는 느낌이라면, pnpm은 실제 패키지 파일을 별도의 저장소에 보관하고 프로젝트에서는 링크 구조로 참조한다.

대략 이런 구조다.

pnpm store
├─ react@18.2.0 실제 패키지 파일
├─ lodash@4.17.21 실제 패키지 파일
└─ next@14.x 실제 패키지 파일

프로젝트 쪽은 이렇게 구성된다.

my-project
├─ package.json
├─ pnpm-lock.yaml
└─ node_modules
   ├─ react -> 링크
   ├─ lodash -> 링크
   └─ .pnpm
      ├─ react@18.2.0
      ├─ lodash@4.17.21
      └─ next@14.x

엄밀히 말하면 pnpm은 단순히 모든 패키지를 전역 store에 symlink만 하는 구조는 아니다.

실제 파일은 content-addressable store에 저장되고, 프로젝트의 node_modules/.pnpm에는 store의 파일을 hard link 또는 copy 방식으로 연결한다. 그리고 패키지 간 의존성 구조는 symlink로 구성된다.

그래서 큰 그림으로는 이렇게 이해하면 된다.

실제 패키지 파일의 원본 저장소
= pnpm store

프로젝트에서 Node.js가 읽는 의존성 구조
= node_modules 안의 링크 기반 구조

pnpm의 핵심 장점

pnpm의 장점은 크게 세 가지다.

1. 디스크 중복을 줄임
2. install 속도가 빠름
3. 유령 의존성을 막기 좋음

유령 의존성이란, package.json에 직접 선언하지 않은 패키지를 우연히 사용할 수 있는 상황을 말한다.

npm이나 Yarn classic의 평평한 node_modules 구조에서는 어떤 패키지가 상위에 끌려 올라와 있어서, 내가 직접 설치하지 않았는데도 import가 되는 경우가 생길 수 있다.

pnpm은 의존성 구조를 더 엄격하게 만들기 때문에 이런 문제를 줄여준다.


일반 클라우드에서 pnpm

일반 CI/CD에서 pnpm을 쓰면 핵심은 pnpm store 캐시다.

흐름은 이렇다.

1. Git checkout
2. pnpm store 캐시 복원
3. pnpm install --frozen-lockfile 실행
4. store에 있는 패키지는 재다운로드하지 않음
5. node_modules 링크 구조 재생성
6. 빌드 실행

주로 캐시하는 대상은 이것이다.

pnpm store

또는 명령어로 확인한 store 경로를 캐시한다.

pnpm store path

여기서 핵심은 이것이다.

pnpm은 node_modules 자체를 오래 보존해서 이득을 보는 것이 아니다.
pnpm store를 캐시해두고,
매 빌드마다 node_modules 링크 구조를 빠르게 다시 만드는 것이 핵심이다.

즉, 클라우드에서 pnpm의 symlink 구조가 의미 있냐는 질문에 대한 답은 이렇다.

의미 있다.

하지만 "symlink 된 node_modules를 그대로 오래 유지한다"는 의미가 아니다.

pnpm store를 캐시한다
→ 패키지 재다운로드가 줄어든다
→ node_modules를 빠르게 재구성한다
→ install 시간이 줄어든다

Vercel에서 pnpm

Vercel에서 pnpm을 쓰면 보통 pnpm-lock.yaml을 보고 pnpm 프로젝트로 인식한다.

흐름은 대략 이렇다.

1. pnpm-lock.yaml 감지
2. pnpm install 실행
3. pnpm store 또는 Vercel build cache 활용
4. node_modules 링크 구조 생성
5. Next.js 빌드
6. 빌드 결과물 배포

Vercel에서도 pnpm의 핵심은 동일하다.

node_modules symlink 구조를 영구적으로 보존하는 것이 핵심이 아님
pnpm store/cache를 복원해서 빠르게 node_modules를 재구성하는 것이 핵심

특히 pnpm은 Vercel에서 Next.js 프로젝트나 모노레포 프로젝트와 잘 맞는 경우가 많다.

예를 들어 Turborepo, workspace 기반 프로젝트에서는 pnpm의 workspace 관리와 빠른 install이 장점이 된다.


4. Yarn Berry의 동작 방식

Yarn Berry란?

Yarn Berry는 보통 Yarn v2 이상을 말한다.

요즘은 Yarn Modern이라고도 부른다.

Yarn Berry는 설치 방식이 두 가지로 나뉜다.

1. nodeLinker: pnp
2. nodeLinker: node-modules

이 둘은 동작 방식이 꽤 다르다.


5. Yarn Berry PnP 방식

PnP란?

PnP는 Plug’n’Play의 약자다.

Yarn Berry의 핵심 기능 중 하나다.

PnP 방식에서는 node_modules를 만들지 않는다.

대신 .pnp.cjs라는 파일이 패키지 위치 정보를 관리한다.

구조는 대략 이렇다.

my-project
├─ package.json
├─ yarn.lock
├─ .pnp.cjs
└─ .yarn
   └─ cache
      ├─ react-npm-18.2.0.zip
      ├─ lodash-npm-4.17.21.zip
      └─ next-npm-14.x.zip

여기서 .pnp.cjs는 이런 역할을 한다.

react는 .yarn/cache 안의 이 zip 파일에 있다
lodash는 .yarn/cache 안의 이 zip 파일에 있다
next는 .yarn/cache 안의 이 zip 파일에 있다

즉, Node.js가 패키지를 찾을 때 node_modules를 뒤지는 대신, Yarn이 만들어둔 .pnp.cjs loader를 통해 패키지 위치를 찾는다.


Yarn Berry PnP의 특징

Yarn Berry PnP의 가장 큰 특징은 이것이다.

node_modules가 없다.

그래서 install 과정에서 거대한 node_modules 폴더를 만들 필요가 없다.

이론적으로는 매우 빠르고, 의존성도 엄격하게 관리된다.

장점은 이렇다.

장점:
- node_modules 생성 비용이 없음
- install이 빠를 수 있음
- 의존성 구조가 엄격함
- 재현성이 좋음
- Zero Install 전략과 잘 맞음

단점도 있다.

단점:
- 일부 도구가 node_modules 존재를 가정하면 깨질 수 있음
- PnP를 모르는 라이브러리나 빌드 도구와 충돌할 수 있음
- 팀이 Yarn Berry 구조를 이해해야 함

Zero Install이란?

Yarn Berry에서 자주 나오는 개념이 Zero Install이다.

Zero Install은 .yarn/cache를 Git에 커밋하는 방식이다.

즉, 패키지 zip 파일을 저장소에 같이 올린다.

Git 저장소
├─ package.json
├─ yarn.lock
├─ .pnp.cjs
└─ .yarn/cache
   ├─ react-npm-18.2.0.zip
   ├─ lodash-npm-4.17.21.zip
   └─ next-npm-14.x.zip

이렇게 하면 클라우드에서 배포할 때 패키지를 새로 다운로드할 필요가 거의 없다.

1. Git checkout
2. .yarn/cache 이미 존재
3. .pnp.cjs로 패키지 위치 확인
4. node_modules 생성 없음
5. 빌드 실행

그래서 이름이 Zero Install이다.

말 그대로 install 단계가 거의 없어지는 것에 가깝다.

다만 단점은 명확하다.

레포지토리 용량이 커진다.
패키지 변경사항이 Git diff에 많이 잡힐 수 있다.
PnP 호환성 문제가 생길 수 있다.

일반 클라우드에서 Yarn Berry PnP

일반 CI/CD에서 Yarn Berry PnP를 쓰면 두 가지 전략이 있다.

1. **.yarn/cache**를 캐시하는 방식

1. Git checkout
2. Yarn cache 복원
3. yarn install --immutable 실행
4. .pnp.cjs 생성 또는 검증
5. node_modules 없이 빌드

이 경우 캐시 대상은 보통 이것이다.

.yarn/cache

또는 Yarn global cache다.

2. Zero Install 방식

1. Git checkout
2. .yarn/cache가 이미 저장소에 있음
3. yarn install --immutable로 상태 검증
4. node_modules 생성 없음
5. 빌드

Zero Install을 제대로 쓰면 CI 캐시에 덜 의존하게 된다.

왜냐하면 패키지 파일 자체가 Git 저장소에 포함되어 있기 때문이다.


Vercel에서 Yarn Berry PnP

Vercel에서 Yarn Berry PnP를 쓸 수도 있다.

흐름은 대략 이렇다.

1. yarn.lock 감지
2. Yarn Berry 설정 확인
3. .yarn/cache 활용
4. .pnp.cjs 기반으로 패키지 접근
5. node_modules 없이 빌드

다만 주의할 점이 있다.

Vercel과 Next.js 자체는 Yarn Berry를 사용할 수 있지만,
프로젝트 안의 일부 라이브러리나 빌드 스크립트가 node_modules를 직접 가정하면 문제가 생길 수 있다.

예를 들어 어떤 스크립트가 이런 식으로 경로를 직접 참조하면 문제가 될 수 있다.

node_modules/some-package/dist/index.js

PnP 환경에서는 node_modules가 없기 때문이다.

그래서 Yarn Berry PnP는 빠르고 엄격하지만, 팀과 프로젝트가 그 구조를 감당할 수 있어야 한다.


6. Yarn Berry node-modules 방식

Yarn Berry를 쓴다고 항상 PnP를 쓰는 것은 아니다.

설정으로 node_modules 방식을 사용할 수 있다.

nodeLinker: node-modules

이 경우 Yarn Berry는 npm/pnpm처럼 node_modules를 생성한다.

구조는 대략 이렇다.

my-project
├─ package.json
├─ yarn.lock
├─ .yarn/cache
└─ node_modules
   ├─ react
   ├─ lodash
   └─ next

이 방식은 PnP보다 덜 극단적이다.

대신 호환성이 좋다.


일반 클라우드에서 Yarn Berry node-modules

흐름은 이렇다.

1. Git checkout
2. Yarn cache 복원
3. yarn install --immutable 실행
4. node_modules 생성
5. 빌드 실행

특징은 이렇다.

Yarn Berry의 lockfile, workspace, cache 장점을 가져가면서
node_modules 기반 호환성도 챙기는 방식

즉, PnP가 부담스러운 팀은 Yarn Berry를 쓰되 nodeLinker: node-modules로 가는 선택도 가능하다.


Vercel에서 Yarn Berry node-modules

Vercel에서도 이 방식은 비교적 무난하다.

1. yarn.lock 감지
2. Yarn install 실행
3. Yarn cache 활용
4. node_modules 생성
5. 빌드 실행

PnP보다 특별함은 줄어들지만, 실무 호환성은 좋아진다.


7. 일반 클라우드 기준 비교

패키지 매니저보통 캐시하는 것node_modules 생성 여부매 빌드 때 하는 일특징
npm~/.npm생성함npm cache 복원 후 node_modules 재생성가장 무난하고 호환성 좋음
pnpmpnpm store생성함store 복원 후 링크 기반 node_modules 재구성빠르고 디스크 효율 좋음
Yarn Berry PnP.yarn/cache 또는 Git 커밋생성 안 함.pnp.cjs로 패키지 위치 확인빠르고 엄격하지만 호환성 체크 필요
Yarn Berry node-modulesYarn cache생성함cache 복원 후 node_modules 생성Berry 기능과 호환성의 절충안

8. Vercel 기준 비교

패키지 매니저Vercel에서의 동작캐시 포인트장점주의점
npmlockfile 감지 후 npm install 계열 실행npm cache + build cache안정적이고 단순함큰 프로젝트에서는 install이 느릴 수 있음
pnpmpnpm-lock.yaml 감지 후 pnpm install 실행pnpm store + build cache빠름, 모노레포와 잘 맞음symlink 구조를 이해해야 함
Yarn Berry PnPYarn Berry 설정 기반으로 PnP 사용.yarn/cache + dependency cachenode_modules 없이 빠른 설치 가능일부 도구 호환성 이슈 가능
Yarn Berry node-modulesYarn Berry를 쓰되 node_modules 생성Yarn cache + build cachePnP보다 호환성 좋음PnP만큼 install 최적화가 극단적이지 않음

9. 패키지 매니저별 핵심 차이

npm

npm은 가장 단순하다.
패키지를 다운로드하고 node_modules에 설치한다.
클라우드에서는 npm cache를 복원해서 다운로드 비용을 줄인다.
하지만 node_modules는 보통 매번 새로 생성된다.

npm은 이런 경우에 좋다.

- 프로젝트가 크지 않음
- 호환성이 가장 중요함
- 팀원이 모두 익숙한 도구를 원함
- 배포 환경에서 예측 가능한 동작을 원함

pnpm

pnpm은 실제 패키지를 pnpm store에 저장한다.
프로젝트의 node_modules는 store를 기반으로 링크 구조로 만들어진다.
클라우드에서는 pnpm store를 캐시하고,
매 배포마다 node_modules를 빠르게 재구성한다.

pnpm은 이런 경우에 좋다.

- install 속도가 중요함
- 모노레포를 사용함
- workspace를 적극적으로 사용함
- 디스크 효율이 중요함
- 의존성 구조를 엄격하게 관리하고 싶음

Yarn Berry PnP

Yarn Berry PnP는 node_modules를 만들지 않는다.
.yarn/cache에 패키지를 zip 형태로 보관하고,
.pnp.cjs를 통해 패키지 위치를 해석한다.

Yarn Berry PnP는 이런 경우에 좋다.

- install 단계를 최대한 줄이고 싶음
- Zero Install 전략을 쓰고 싶음
- 의존성 재현성을 강하게 가져가고 싶음
- 팀이 Yarn Berry/PnP 구조를 이해하고 있음

하지만 이런 경우에는 조심해야 한다.

- 일부 라이브러리가 node_modules 경로를 직접 참조함
- 빌드 도구가 PnP를 제대로 지원하지 않음
- 팀이 PnP 문제를 디버깅할 준비가 안 되어 있음

Yarn Berry node-modules

Yarn Berry의 기능은 쓰되,
패키지 해석 방식은 기존 node_modules 방식에 가깝게 가져간다.

이 방식은 이런 경우에 좋다.

- Yarn Berry를 쓰고 싶음
- 하지만 PnP 호환성 문제가 부담됨
- 기존 Node.js 생태계 호환성을 유지하고 싶음

10. 빌드 단계와 런타임 단계에서의 차이

패키지 매니저의 영향은 주로 빌드 단계에서 크다.

의존성 설치
타입 체크
번들링
테스트
Next.js build

이 단계에서는 npm, pnpm, Yarn Berry의 차이가 크게 드러난다.

하지만 빌드가 끝난 뒤 런타임에서는 패키지 매니저의 영향이 줄어든다.

예를 들어 Next.js를 Vercel에 배포하면 중요한 것은 보통 이것이다.

.next 빌드 결과물
serverless function 번들
static assets
edge/server runtime output

즉, 런타임에서는 “어떤 패키지 매니저로 설치했는가”보다 빌드 결과물이 어떻게 만들어졌는가가 더 중요하다.

다만 자체 서버를 운영하거나 Docker 이미지 안에서 node_modules를 포함해 실행하는 경우에는 런타임에서도 패키지 매니저의 설치 구조가 영향을 줄 수 있다.


11. 최종 정리

클라우드 배포 환경에서 패키지 매니저의 차이는 이렇게 정리할 수 있다.

npm
→ node_modules를 직접 생성한다.
→ 클라우드에서는 npm cache로 다운로드를 줄인다.
→ 가장 무난하지만 install이 느릴 수 있다.

pnpm
→ 실제 패키지는 pnpm store에 저장한다.
→ node_modules는 링크 구조로 재생성한다.
→ 클라우드에서는 pnpm store 캐시가 핵심이다.
→ 빠르고 모노레포에 강하다.

Yarn Berry PnP
→ node_modules를 만들지 않는다.
→ .yarn/cache와 .pnp.cjs로 패키지를 찾는다.
→ Zero Install과 조합하면 install 단계가 매우 가벼워진다.
→ 다만 호환성 검증이 필요하다.

Yarn Berry node-modules
→ Yarn Berry를 쓰지만 node_modules를 생성한다.
→ PnP보다 호환성이 좋다.
→ npm/pnpm과 비슷한 배포 흐름을 가진다.

가장 중요한 결론은 이거다.

클라우드 배포에서는 node_modules를 오래 보존하는 것이 핵심이 아니다.

핵심은 패키지 매니저별 캐시를 얼마나 잘 복원해서
다음 빌드에서 의존성 설치 비용을 줄이느냐이다.

그래서 실무적으로는 이렇게 선택할 수 있다.

안정성과 호환성이 최우선
→ npm

빠른 install, 모노레포, 효율적인 캐시가 중요
→ pnpm

node_modules 없는 강한 재현성과 Zero Install을 원함
→ Yarn Berry PnP

Yarn Berry는 쓰고 싶지만 PnP 호환성이 부담됨
→ Yarn Berry node-modules