npm, yarn, yarn berry의 차이와 발전 과정

2023.08.27
13분
댓글

글을 시작하며

JavaScript로 프로젝트를 진행할 때 대표적으로 쓰이는 패키지 관리 툴로는 npm과 yarn이 있습니다.

230904-114913

패키지들을 사용하기 위해서는 다운로드, 업데이트, 의존성 관리 등 복잡한 상황들이 많이 생기지만 패키지 매니저를 사용하면 이러한 과정들을 편리하고 안전하게 수행할 수 있습니다.

이번 글에서는 패키지 매니저의 발전 과정을 알아보면서 npm, yarn 그리고 yarn berry의 장단점에 대해 배워보겠습니다.


npm

npm(node package manager)은 JavaScript 런타임 환경인 Node.js의 기본 패키지 관리자입니다.

npm이 있는데도 왜 yarn이 개발 되었을까요? 🤔
보통 새로운 기술이 개발된 이유는 이전의 단점을 개선하기 위해서입니다.

먼저 npm의 단점에 대해 알아보겠습니다.

1. 일관적이지 않은 패키지 버전

시멘틱 버저닝

노드 모듈들은 기본적으로 시멘틱 버저닝이라는 기법을 사용하여 모듈의 버전을 나타냅니다.
간단히 말해 1.2.3처럼 버전을 세 가지 숫자로 구분하고 각 자리에 현재 버전이 이전 버전과 어떤 관계가 있는지 암시하는 방법입니다.

캐럿 기호

또다른 특징으로는 캐럿 기호(^)를 이용해서 첫 번째 숫자를 제외한 두 자리(마이너, 패치) 버전까지는 변경을 허용할 수 있습니다. 예를 들어 버전이 ^1.2.3이라면, 메이저 버전인 1은 변경되지 않는 범위에서 1.2.0 1.2.4 등 모듈의 버전이 바뀌는 것을 허용합니다.

버전 불일치

따라서 npm install 명령어로 모듈을 설치하면 메이저 버전 중 최신 버전을 다운받게 됩니다.
그렇다면 최신 버전이 나올 때마다 설치되는 버전이 달라지는 문제가 생길 수도 있지 않을까요?

맞습니다!

버전 변경이 허용되기 때문에 모듈간의 버전이 일치하지 않는 문제가 발생할 수 있습니다.


2. 고정되지 않은 설치 순서

개발자의 환경에 따라 모듈들의 설치 순서가 변경될 수 있습니다.
이미 만들어진 프로젝트에서 모듈을 추가로 설치하게 될 경우 npm install 명령어를 통해 처음부터 설치하는 사람과의 설치 순서가 달라질 수 있습니다.

npm은 개발자들이 어떤 순서로 모듈을 추가했는지 알 수 없기 때문에 모듈 이름을 사전 순서대로 정렬하여 순차적으로 설치합니다.


3. 순차적인 설치로 인한 긴 소요시간

npm은 모듈을 한 번에 하나씩 순차적으로 설치합니다.

설치해야 하는 모듈이 많으면 많을수록 총 설치 시간이 길어지게 됩니다.


yarn

yarn은 npm의 문제들을 해결함과 동시에 여러 기능을 탑재하여 등장하였습니다.

1. 버전 고정을 위한 yarn.lock

yarn은 사용할 모듈의 버전을 지정하기 위해 yarn.lock 파일을 사용합니다.

따라서 개발자들이 항상 같은 버전의 모듈을 사용할 수 있도록 보장합니다.

npm도 이러한 문제를 해결하기 위해서 npm shrinkwrap 명령어로 버전을 .json 파일에 명시하였습니다.

하지만 npm의 경우 고정이 필요할 때마다 명령어를 입력 해야하는 번거로움이 있지만
yarn의 경우 의존성이 달라질 때마다 lock 파일을 자동으로 추가하기 때문에 보다 편리합니다.


2. 설치 확인을 위한 checksum

yarn은 checksum을 이용하여 패키지가 제대로 설치되었는지 확인합니다.
yarn.lock 파일을 확인해보면 resolved 주소 뒤에 해시값이 추가 되어있는데, 이 해시값이 바로 checksum입니다.

checksum은 패키지 파일의 무결성을 확인하는 안전 장치 역할을 합니다.


3. 캐시를 통한 속도 개선

yarn은 한 번 다운로드한 패키지라면 캐시를 해두기 때문에 다음부터는 매우 빠른 속도로 설치할 수 있습니다.
또한 병렬 다운로드를 지원하기 때문에 순차적으로 설치하는 npm과는 다르게 모듈을 한 번에 설치할 수 있습니다. 설치할 패키지가 많을수록 npm과의 속도 차이가 벌어지게 됩니다.


npm과 yarn의 문제

시간이 지나고 npm도 많은 부분이 개선되었습니다. package-lock 파일을 통해서 버전을 고정하고, 속도면에서도 yarn과 큰 차이가 없는 수준까지 따라왔습니다.

하지만 여러 발전을 거친 두 패키지 매니저에도 여전히 남아있는 문제점들이 있었습니다 🥲

무거운 node_modules

230904-010059

npm에서 구성하는 node_modules 폴더는 매우 큰 용량을 차지합니다.

npm은 이 무거운 폴더를 경량화시키기 위해 호이스팅을 도입하였습니다.
용량을 줄이는 가장 간단한 방법은 바로 중복을 제거하는 것입니다.

node_modules의 중복된 패키지를 최소화하기 위해 각 패키지가 의존하는 패키지들을 최상단으로 끌어올립니다.
이렇게 되면 중복된 패키지가 최상단에 하나만 존재하게 되어 불필요한 중복을 제거할 수 있게 됩니다.


유령 의존성

230904-115613

하지만 이렇게 되면, 나는 설치한 적이 없지만 사용하는 패키지가 의존하고 있다는 이유만으로 사용하지 않는 패키지도 설치하게 됩니다. 이러한 현상을 유령 의존성이라고 부릅니다.

따라서 비효율적인 설치와 유령 의존성 등은 여전히 npm과 yarn에게 숙제로 남아있었습니다.
그리고 yarn은 이러한 숙제를 해결한 v2를 2020년 1월에 공개했습니다!


yarn berry

yarn berry는 yarn v2 이상을 부르는 명칭입니다. 이에 따라 기존의 yarn v1은 yarn classic이라고 부르게 되었습니다.

yarn berry는 yarn classic의 무거운 node_modules와 유령 의존성 문제를 해결하기 위해 새로운 방식을 도입하였습니다.

사용 방법

npm에서 최신 버전의 yarn을 내려받고, 버전을 berry로 설정하면 yarn berry를 사용할 수 있습니다.

$ npm install -g yarn
$ yarn set version berry

1. Plug'n'Play

기존의 무거웠던 node_modules를 생성하지 않고 패키지들에 대한 정보를 .zip 파일로 관리합니다.
패키지 의존성 정보를 .zip 파일로 압축하여 .yarn/cache 폴더에 저장하고 의존성을 찾을 수 있는 정보를 .pnp.cjs 파일에 기록합니다.

230904-122752

/* react 패키지 중에서 */
["react", [
  /* npm:17.0.1 버전은 */
  ["npm:17.0.1", {
    /* 이 위치에 있고 */
    "packageLocation": "./.yarn/cache/react-npm-17.0.1-98658812fc-a76d86ec97.zip/node_modules/react/",
    /* 이 의존성들을 참조한다. */
    "packageDependencies": [
      ["loose-envify", "npm:1.4.0"],
      ["object-assign", "npm:4.1.1"]
    ],
  }]
]],

.pnp.cjs는 의존성 트리를 중첩된 맵으로 표현하기 때문에 디스크 I/O 없이도 패키지가 어떤 라이브러리에 의존하는지, 어디에 위치하는지를 바로 알 수 있습니다.

따라서 패키지의 위치를 정확히 알 수 있기 때문에 시간도 단축되고 중복 설치도 방지할 수 있습니다.


2. Zero Install

yarn PnP는 의존성을 압축 파일로 관리하기 때문에 의존성의 용량이 작습니다.
예를 들어, 일반적인 node_modules는 1.2GB 크기이며 13만 5천개의 파일로 구성되어 있는 반면, yarn PnP의 의존성은 139MB 크기의 2천개의 압축 파일로 구성됩니다.

용량과 파일의 수가 적기 때문에 의존성을 Git으로 관리할 수 있습니다.
이처럼 yarn berry에서 의존성을 버전 관리에 포함하는 것을 zero-install이라고 합니다.
zero-install을 사용하면 git clone 이후 별도의 설치 필요없이 바로 사용할 수 있습니다.

zero-install을 사용했을 때의 장점은 무엇이 있을까요?

  1. 새로 저장소를 복제하거나 브랜치를 바꾸었을 때 yarn install을 실행하지 않아도 됩니다.
  2. CI에서 의존성을 설치하는 시간을 크게 절약할 수 있습니다.
  3. 오프라인 캐시 기능으로 인터넷이 연결되지 않아도 패키지를 사용할 수 있습니다.

글을 마치며

이번 글에서는 npm, yarn, yarn berry에 이르기까지 패키지 매니저가 가졌던 문제점들과 그 발전 과정에 대해 알아보았습니다.
글을 정리하면서 기술의 장단점을 정확히 알아야 그 기술을 최대로 활용할 수 있겠다는 생각이 들었습니다.

이전까지는 유령 의존성과 node_modules의 문제에 대해서는 깊게 생각해보지 못했던 것 같습니다. 이젠 npm과 yarn의 단점에 대해서도 인지하고 있기 때문에 적절한 상황에 yarn berry를 적용할 수 있도록 고민해볼 수 있을 것 같습니다.

참고 문서