[FE] 패키지 매니저 - npm / yarn / pnpm

선영·2024년 11월 25일
0

Front-End

목록 보기
5/5
post-thumbnail

문제점


npm을 사용하니까 의존성을 설치하는 과정에서 CI/CD 빌드 파이프라인 시간이 늘어나 배포 효율이 감소하는 문제가 발생했다. 그래서 npm, yarn, pnpm 패키지 매니저를 각각 비교해보고 적절한 것을 도입하기로 했다.

nodeJS


먼저, nodeJS는 Chrome V8 자바스크립트 엔진으로 빌드된 자바스크립트 런타임 환경이다.

런타임: 프로그래밍 언어가 구동되는 환경, 자바스크립트의 런타임은 웹 브라우저와 nodeJS로 구성되어있다.

프론트엔드 개발환경에서 nodeJS를 쓰는 이유는 최신 스펙으로 개발할 수 있고, 빌드 자동화, 개발 환경의 커스터마이징이 가능하다.

npm


nodeJS를 사용한 node package manager로 패키지 매니저의 시초이다. 즉, 프로젝트의 의존성을 수동으로 다운로드해야 했던 불편함을 개선한 패키지 매니저이다. 패키지 매니저의 시초이기 때문에 오랜 시간 아주 많은 사용자들이 사용해 와서 생태계가 풍부하다.

nodeJS에서 사용할 수 있는 모듈들을 패키지화하여 모아놓은 저장소 역할과 패키지 설치 및 관리를 위한 cli(command line interface)를 제공한다.

npm으로 프로젝트를 구성할때의 디렉터리 구조는 아래와 같다.

my-app/
├─ node_modules/
├─ package.json
└─ package-lock.json
  • 설치
    • node.js에 내장이 되어 node.js를 설치하면 기본적으로 포함된다. 그리고 추가 설정이 필요 없다. npm -v로 버전확인 가능
  • 호환성
    • node.js와 함께 기본 제공되며, 모든 패키지와 완벽히 호환된다.
  • 속도
    • v7 이후 병렬 설치와 더 나은 캐싱을 도입했지만, 여전히 다른 패키지 매니저들보다 느릴 수 있다.
  • 패키지 저장 방식
    • 기본적으로 패키지를 프로젝트 디렉토리 안의 node_modules 폴더에 복사하기 때문에 동일한 패키지를 여러 프로젝트에서 사용하면, 각각의 프로젝트마다 패키지가 중복 저장될 수 있다.
  • 프로젝트/의존성 관리
    • v7 이후, 워크스페이스(모노레포 관리)를 지원한다.
    • 의존성 트리의 복잡성 때문에 대규모 프로젝트나 여러 라이브러리를 사용할 때 몇 가지 문제점이 발생할 수 있다.
      • npm은 기본적으로 모듈화를 매우 세분화해서, 하나의 라이브러리가 여러개의 하위 라이브러리에 의존할 수 있다. 이런 구조는 중첩된 의존성 트리를 만들며, 프로젝트에 설치되는 모듈이 매우 많아질 수 있다. 아래는 npm i lodash mongoose express를 했을때의 예시이다.
        my-app
        ├─ express@4.18.2
        │  ├─ accepts@1.3.8
        │  │  ├─ mime-types@2.1.35
        │  │  │  └─ mime-db@1.52.0
        │  │  └─ negotiator@0.6.3
        │  └─ body-parser@1.20.2
        │     ├─ bytes@3.1.2
        │     └─ debug@2.6.9
        ├─ lodash@4.17.21
        ├─ mongoose@6.8.0
        │  ├─ mongodb@5.4.0
        │  │  ├─ bson@5.7.0
        │  │  └─ other-dependencies...
        │  └─ kareem@2.3.4
        
      • 그래서 의존성 트리가 커질수록 보안 취약점이 있는 라이브러리를 포함할 가능성이 높아진다.
      • 불필요하거나 너무 많은 의존성을 포함하면 애플리케이션의 번들 크기가 커지고, 이는 성능 문제로 이어질 수 있다.
  • commend : 위에서 살펴본 단점을 해결하기 위해서 모노레포를 도입하는 것이다. 즉, 여러 프로젝트가 동일한 의존성을 공유하도록 구조화하여 중복 설치를 방지한다. 그러기 위해서 yarn이나 pnpm과 같은 대체 패키지 관리자를 사용한다.

yarn


기존의 npm의 단점을 보완하면서 2016년에 페이스북 개발자들과 구글 개발자들이 함께 내놓은 패키지 매니저이다.

yarn으로 프로젝트를 구성할때 디렉터리의 구조는 아래와 같다.

my-app/
├─ node_modules/
├─ package.json
└─ yarn.lock
  • 설치
    • npm과 달리 별도로 설치해야 한다.
      • 이미 nodeJS, npm이 설치된 환경이라면, npm i --global yarn명령어로 npm을 통한 설치가 가능하다.
      • 혹은 homebrew와 같은 패키지 매니저를 사용하여 brew i yarn명령어로 설치할 수 있다.
  • 호환성
    • npm레지스트리를 사용하므로 대부분의 npm패키지와 호환된다. 일부 기능은 yarn.lock파일에만 최적화 되어 있다.
  • 속도
    • yarn v1 더 빠른 설치를 위해 부터 글로벌 캐싱과 병렬 다운로드를 지원한다.
    • yarn v2(berry) 부터는 Plug’n’Play(PnP)를 사용할 수 있다. 더욱 속도가 빠르다. (하지만 설정이 복잡할 수 있다. ) 참고로, PnP를 사용한 프로젝트의 디렉터리 구조는 아래와 같다.
      my-app/
      ├─ .pnp.cjs          # PnP 설정 파일
      ├─ .yarn/            # 의존성 저장소
      ├─ package.json
      └─ yarn.lock
  • 패키지 저장 방식
    • 기본적으로 패키지를 프로젝트 디렉토리 안의 node_modules 폴더에 복사하기 때문에 동일한 패키지를 여러 프로젝트에서 사용하면, 각각의 프로젝트마다 패키지가 중복 저장될 수 있다.
  • 프로젝트/의존성 관리
    • 워크스페이스(모노레포 관리)를 지원한다. yarn2 이상에는 PnP모드로 더 나은 의존성 관리 기능을 제공한다.
  • comment : 결론적으로는 최근 버전의 npm과는 기능면에서 큰 차이는 없어보인다. 그래도 대규모 프로젝트, 모노레포, 빠른 설치 속도가 중요한 경우에 적합하다. (프로젝트에 따라 혼합해서 사용할 수 있다.)

pnpm(performant npm)


2017년에 Zoltan Kochan이란 개발자가 내놓은 패키지 매니저이다.

pnpm으로 프로젝트를 구성할때 디렉터리의 구조는 아래와 같다.

my-app/
├─ node_modules/
├─ package.json
└─ yarn.lock
  • 설치
    • 별도로 설치해야 한다.
  • 호환성
    • npm 레지스트리를 사용하므로 대부분의 npm패키지와 호환된다. 그러나 node_modules 구조가 기본 방식과 다르기 때문에, 일부 패키지에서 문제가 발생할 수 있다.
    • 특정 패키지를 한 번만 설치하기 때문에 프로젝트별로 연결해놓으면 호환 문제가 발생할 수 있다. 그래서 버전관리를 해줘야 한다.
  • 속도
    • 하드 링크를 활용한 저장 방식때문에 매우 빠르다. 패키지를 중복 다운로드 하지 않고, 이미 다운로드된 패키지를 재활용한다.
    • yarn v2와 동일하게 global 저장소에 패키지를 한 번만 저장하고, 동일한 패키지를 다른 프로젝트에서 다시 설치할 때 캐싱할 수 있다. 즉, 똑같은 라이브러리를 중복해서 설치할 필요가 없다.
  • 패키지 저장 방식
    • 패키지를 글로벌 저장소(시스템의 한 곳)에 하드 링크 또는 심볼릭 링크를 사용하여 관리한다. 그래서 동일한 패키지가 여러 프로젝트에 중복 저장되지 않으므로 디스크 공간이 절약된다.
  • 프로젝트/의존성 관리
    • pnpm도 강력한 워크스페이스를 지원하고, 설정이 간단하고 성능이 뛰어나다. 의존성을 공유하고 저장소 크기를 줄이는데 매우 효과적이다.
    • node_modules안에 심볼릭 링크를 사용하므로 의존성 중복이 발생하지 않는다.

위 내용을 차트로 비교하면 다음과 같다. (출처: pnpm 공식문서, v9 기준)

결과

  1. node_modules 폴더 크기 350mb(0.35gb)가 305mb로 줄었다.

    • 중복 파일을 줄여 디스크 공간을 절약했다.
  2. time 명령어를 사용하여 출력된 시간을 비교했다. (최소 3~5회 테스트 후 평균 값을 계산하면 더 정확하다.)

    # Clean the environment
    rm -rf node_modules package-lock.json pnpm-lock.yaml
    
    # Test npm
    time npm install
    
    # Clean again
    rm -rf node_modules package-lock.json pnpm-lock.yaml
    
    # Test pnpm
    time pnpm install

    참고로, npm과 pnpm 모두 이전 설치 기록을 기반으로 캐시된 데이터를 사용하기 때문에 node_modules, package-lock.json, pnpm-lock.yaml 이 존재하면 새로 설치하는 대신 기존 데이터를 활용하므로 실제 설치 속도를 측정하지 못한다. 그래서 rm -rf 명령어로 해당 파일들을 제거한 상태에서 테스트해야 정확한 비교가 가능하다.

    npm install 16.26s user 17.42s system 47% cpu 1:11.66 total

    • total 값(총 경과 시간)
      • 실제 사용자 입장에서 체감하는 작업의 소요 시간이다.
      • 총 1분 11.66초 동안 작업이 실행됐다.
    • 사용자 vs 시스템 시간
      • 시스템 시간이 사용자 시간보다 길다는 것은 파일 시스템 작업이나 네트워크 통신 등 I/O 작업이 많았다는 것을 나타낸다.
    • CPU 사용
      • 사용자 모드에서 16.26초, 시스템 모드에서 17.42초
    • CPU 활용도 (병렬 작업과 CPU 활용 효율성이 반영된 결과)
      • CPU 리소스의 47%를 사용했다.

    pnpm install 7.80s user 15.12s system 86% cpu 26.589 total

    • total 값(총 경과 시간)
      • 실제 사용자 입장에서 체감하는 작업의 소요 시간이다.
      • 총 26.589초 동안 작업이 실행됐다.
    • 사용자 vs 시스템 시간
      • 시스템 시간이 사용자 시간보다 길다는 것은 파일 시스템 작업이나 네트워크 통신 등 I/O 작업이 많았다는 것을 나타낸다.
    • CPU 사용
      • 사용자 모드에서 7.8초, 시스템 모드에서 15.12초
    • CPU 활용도 (병렬 작업과 CPU 활용 효율성이 반영된 결과)
      • CPU 리소스의 86%를 사용했다.

참고자료

profile
Superduper-India

0개의 댓글