pnpm에서 npm으로 돌아간 이유(로컬과 CI 환경에서 직접 테스트해보기)

Description: npm을 쓰다가 pnpm을 써보면서 겪은 문제로 다시 npm을 바꾼 과정입니다. 하지만 은탄환은 없다라는 말처럼 CI환경인 경우에는 pnpm과 같은 패키지매니저의 변경이 필요해보입니다.

현재 노트: KR-020.30 a
상위 분류: KR-020.30

#Packages

Why? 왜 이 글을 쓰게 되었나요?

npm 을 사용하다가 퍼포먼스 측면이 강조되고 채용 공고에서 pnpm 사용경험을 많이 묻길래 pnpm으로 전환을 하였습니다. 하지만 사용하다보니 여러 문제로 다시 npm으로 회귀하였고, pnpm이 필요한 상황이나 조건이 어떤 점인지를 테스트해본 고민을 공유하기 위해 글을 작성하였습니다.

What? 어떤 문제가 있었나요?

npm을 사용하면서 패키지 다운로드 속도와 용량 관리에 불편함을 느끼고 pnpm으로 전환하였습니다. 하지만 pnpm을 사용하면서 dlx 가 동작하지 않거나 마이그레이션과 같은 오버헤드뿐만아니라 결정적으로 Tailwilnd v4pnpm에서 지원이 안되는 문제를 겪었습니다.
다른 사람의 동일한 이슈(2025년 1월)

How 어떻게 해결하셨나요?

기존의 pnpm.lock.yamlnode_modules를 삭제해줍니다. pnpm으로 설치된 패키지를 전부 제거한 후, npm install을 통해 전부 새로 설치해주었습니다.

Testing 어떤 테스트를 수행 하셨나요?

몇 달간 pnpm을 쓰면서 개인 프로젝트 수준에서는 사실 '와! 엄청 빨라졌네?' 하는 드라마틱한 체감을 하지 못했습니다. NPM 첫 다운로드도 그렇게 느리다고 느끼지 못했고, 이후 설치야 워낙 금방 되니 큰 차이를 못 느꼈죠.

Tailwind v4 이슈로 npm으로 돌아온 후, 문득 이런 생각이 들었습니다. "그럼 도대체 어떤 경우에 pnpm의 퍼포먼스 장점을 제대로 누릴 수 있는 걸까?" 궁금증이 커졌고, 이참에 pnpm의 사용 이유와 원리를 좀 더 파보니 그 퍼포먼스 차이는 크게 세 가지 요소의 영향을 받는다고 생각하게 되었습니다.

바로 (패키지 사이즈 + 프로젝트 복잡성) * 설치/변경 횟수 입니다.

여기서 제가 간과했던 부분이 바로 '횟수'였습니다. 로컬 개발 환경에서는 npm install을 처음 한두 번 하고 나면 이후에는 패키지를 추가하거나 업데이트할 때만 부분적으로 설치가 일어나니 전체 설치 속도에 크게 민감하지 않을 수 있습니다.

하지만 CI 파이프라인을 생각해보면 다르죠. 커밋할 때마다 새로운 환경에서 패키지를 '처음부터' 다운로드받고 설치하는 과정이 반복될 수 있습니다. 이럴 경우 설치 시간이 길어지면 전체 빌드 시간이 늘어나고, 클라우드 CI/CD 서비스에서는 곧 비용 증가로 이어집니다.

아, 바로 이런 환경에서 pnpm의 캐싱 및 재사용 전략이 빛을 발하겠구나 싶었습니다. 그래서 가설을 세웠습니다. "사이즈가 크고 복잡한 프로젝트일수록, 그리고 패키지 설치가 '자주' 일어나는 환경일수록 PNPM의 이점이 클 것이다."

이 가설을 검증하기 위해 일부러 프로젝트에 사이즈가 크고 의존성이 많은 패키지를 포함시켜 npmpnpm의 설치 시간을 비교해보는 테스트를 수행했습니다. 테스트는 제 로컬 환경과 CI 환경(GitHub Actions 시뮬레이션) 두 곳에서 진행했습니다.

테스트 환경:
로컬: Windows 11, Intel i5 CPU 12450, 16GB RAM, SSD 노트북
CI 시뮬레이션:** GitHub Actions 환경 (Standard Runner 사양)

테스트 시나리오:

  1. 로컬 환경 - 첫 설치 (콜드 캐시/스토어): node_modules와 락 파일(npm의 경우 package-lock.json, pnpm의 경우 pnpm.lock.yaml)을 모두 삭제한 상태에서 npm install 또는 pnpm install 실행.
  2. 로컬 환경 - 이후 설치 (웜 캐시/스토어): 첫 설치 후, 락 파일이 존재하는 상태에서 npm install 또는 pnpm install을 다시 실행 (의존성 변경 없음).
  3. CI 환경 - 첫 설치 시뮬레이션: GitHub Actions Cache를 사용하지 않거나 캐시가 유효하지 않은 상황을 가정.
  4. CI 환경 - 이후 설치 시뮬레이션: GitHub Actions Cache가 유효한 상황을 가정하고 캐시를 활용하여 설치.
  5. CI 환경 (Cypress 추가) - 첫 설치 시뮬레이션: Cypress와 같이 매우 큰 패키지를 추가했을 때 첫 설치 시간 변화 확인.
  6. CI 환경 (Cypress 추가) - 이후 설치 시뮬레이션: Cypress 추가 후 캐시를 활용했을 때 설치 시간 변화 확인.

로컬 에서 테스트 시 56초, 41초로 점점 빨라지는 걸 볼 수 있습니다. npm도 cache를 상요하기 때문

$ time npm install
...
npm 1번째
real    0m56.212s
user    0m0.135s
sys     0m0.091s

npm 2번째
found 0 vulnerabilities

real    0m41.497s
user    0m0.106s
sys     0m0.108s

로컬 pnpm 첫 시도시 2분36초, 두 번째 시도시 33초

약간 놀랍게도 pnpm의 경우 첫 설치 56초의 npm보다 후러씬 느렸습니다. 하드링크생성, 의존성 처리가 더 복잡하기에 디스크의 스펙이 큰영향을 받다보니 노트북 환경에서는 그 차이가 더욱 두드러졌습니다. 하지만 2번 째 시도시 33초로 reused584라는 수치를 통해 pnpm의 장점을 확인 할 수 있었습니다.

KMC@DESKTOP-GDCG9FE MINGW64 ~/Desktop/Projects/npmtest (main)
$ time pnpm install
...
Progress: resolved 585, reused 0, downloaded 584, added 584, done


real    2m35.561s
user    0m0.092s
sys     0m0.155s
KMC@DESKTOP-GDCG9FE MINGW64 ~/Desktop/Projects/npmtest (main)
$ time pnpm install
Lockfile is up to date, resolution step is skipped

Progress: resolved 584, reused 584, downloaded 0, added 584, done
real    0m33.569s
user    0m0.077s
sys     0m0.031s

CI 환경에서 드러나는 두드러지는 변화

적당히 성능이 좋은 CI 환경에서는 pnpm이 압도적으로 빠른 속도가 나타남을 테스트할 수 있었습니다.
첫 설치시 npm이 38초로 훨씬 느린 시간이 걸렸으며 pnpm 은 8초밖에 걸리지 않았습니다.
이후 캐시 활용한 설치 시뮬레이션에서도 크게 차이가 났엇습니다. 11초 (npm) vs 3초 (pnpm)

용량이 큰 CyPress추가하여 한 번 더 테스트 해보았을떄 초기 설치 1분 (npm) vs 8초 (pnpm) 그 차이가 훨씬 두드러졌습니다.

표로 정리하면 아래와 같습니다.

환경 시나리오 PNPM 설치 시간 NPM 설치 시간 핵심 인사이트
로컬 환경 첫 설치 2분 35초 56초 PNPM 콜드 스토어 초기 구성 시간 소요 vs NPM (부분적) 웜 캐시 활용 가능. 로컬 환경 요인 영향 큼.
이후 설치 33초 41초 PNPM 웜 스토어 재사용 (링크 생성)이 NPM 웜 캐시 (복사/압축 해제)보다 빠름.
CI (기본) 첫 설치 시뮬레이션 8초 38초 PNPM CI 캐시(웜 스토어) 활용 vs NPM 콜드 캐시 시뮬레이션. CI에서 PNPM 캐시 효과 압도적.
이후 설치 시뮬레이션 3-4초 9-11초 PNPM 웜 스토어 재사용이 NPM 웜 캐시보다 훨씬 빠름. 반복적인 CI 빌드에 큰 이점.
CI (Cypress 추가) 첫 설치 시뮬레이션 8초 1분 0초 PNPM CI 캐시(웜 스토어)가 대용량 의존성(Cypress) 효율적으로 처리 vs NPM 콜드 캐시 시뮬레이션 시 시간 대폭 증가.
이후 설치 시뮬레이션 4초 10-11초 PNPM 웜 스토어 재사용이 NPM 웜 캐시보다 훨씬 빠름. 대용량 의존성 환경에서 PNPM의 웜 캐시 이점 부각.

Retrospective 무엇을 배웠고, 어떻게 활용할까요?

pnpm을 써봤다가 다시 npm으로 돌아오고, 이번 테스트를 통해 저는 pnpmnpm 각각이 빛을 발하는 환경이 다르다는 것을 확실히 깨달았습니다.

우선 제가 개인적으로 pnpm을 쓰면서 불편함을 느꼈던 이유는 다음과 같습니다.

하지만 테스트를 통해 pnpm의 진가가 발휘되는 지점도 명확히 확인했습니다.

결론적으로, 저는 다음과 같은 기준으로 pnpmnpm을 선택할 것 같습니다.

이번 경험을 통해 단순한 벤치마크 숫자나 듣기만 한 경우가 아닌 **'자신의 프로젝트 환경과 사용 패턴, 그리고 중요하게 생각하는 지점(로컬 첫 설치 속도 vs CI 속도 vs 디스크 용량 vs 호환성 등)'**에 맞는 도구를 선택하는 것이 가장 중요하다는 것을 다시 한번 느꼈습니다.

앞으로는 새로운 프로젝트를 시작하거나 기존 프로젝트의 패키지 매니저 변경을 고려할 때, 단순히 '요즘 뜨는 도구'가 아니라 프로젝트의 규모, 팀의 숙련도, CI/CD 환경, 로컬 개발 환경의 특성 등 다양한 요소를 종합적으로 판단하여 가장 적합한 도구를 선택하여야함을 느끼고, 이번 테스트 경험은 그런 판단을 내리는 데 큰 도움이 될 것입니다.