읽기 전에
본 글은 Yarn Berry를 처음 사용해보는 개발자를 위한 글입니다! npm과 Yarn Classic을 사용해본 경험이 있는 초보 개발자들이 정말 필요한 개념들과 Yarn Berry 개념과 Yarn Berry를 사용해야 하는 이유에 초점을 맞춰 작성하였으니 참고 바랍니다.
"yarn": "4.4.1"
PM(Package Managers)이란?
패키지 매니저(Package Manager)는 소프트웨어 개발에서 필요한 외부 라이브러리와 도구들을 쉽게 관리하고 설치할 수 있게 도와주는 도구입니다. 개발자들이 프로젝트를 진행하면서 자주 사용하는 오픈소스 패키지나 모듈들을 검색, 설치, 업데이트, 삭제할 수 있도록 해줍니다.
개발자들이 직접 필요한 라이브러리 코드를 찾아서 다운로드하고, 버전을 맞춰 관리하는 일은 굉장히 번거롭고 비효율적일 수 있습니다. 패키지 매니저는 이런 과정을 자동화하고, 의존성을 관리해 주며, 패키지를 설치할 때 필요한 설정들을 간단하게 처리해 줍니다.
패키지 매니저의 주요 기능은 다음과 같습니다:
npm (Node Package Manager): Node.js와 함께 가장 널리 사용되는 패키지 매니저로, 방대한 양의 JavaScript 패키지를 관리할 수 있습니다.
Yarn: npm의 단점을 보완하고 더 빠른 속도를 제공하기 위해 Facebook에서 만든 패키지 매니저입니다. 설치 속도와 안정성을 높이고, 다양한 기능을 추가하여 개발자들에게 많은 인기를 끌었습니다. Yarn Classic이라고도 부릅니다.
npm과 Yarn의 문제점
npm과 Yarn Classic은 많은 개발자들에게 사랑받는 패키지 매니저들이지만, 몇 가지 공통된 문제점과 각각의 단점을 가지고 있습니다.
npm과 Yarn Classic은 node_modules 폴더에 의존성을 설치하며, 이 구조는 중첩된 폴더와 복잡한 디렉터리 구조를 만들어냅니다. 이로 인해 디스크 사용량이 증가하고, 파일 시스템이 복잡해져서 성능이 저하될 수 있습니다.
$ node
Welcome to Node.js v22.4.0.
Type ".help" for more information.
> require.resolve.paths('svelte')
[
'/Users/jinoo/Desktop/jinoo/Programming/Good-Night-3rd-Hackathon/Good-Night-3rd-Hackathon-Frontend/repl/node_modules',
'/Users/jinoo/Desktop/jinoo/Programming/Good-Night-3rd-Hackathon/Good-Night-3rd-Hackathon-Frontend/node_modules',
'/Users/jinoo/Desktop/jinoo/Programming/Good-Night-3rd-Hackathon/node_modules',
'/Users/jinoo/Desktop/jinoo/Programming/node_modules',
'/Users/jinoo/Desktop/jinoo/node_modules',
'/Users/jinoo/Desktop/node_modules',
'/Users/jinoo/node_modules',
'/Users/node_modules',
'/node_modules',
'/Users/jinoo/.node_modules',
'/Users/jinoo/.node_libraries',
'/opt/homebrew/Cellar/node/22.4.0/lib/node',
'/Users/jinoo/.node_modules',
'/Users/jinoo/.node_libraries',
'/opt/homebrew/Cellar/node/22.4.0/lib/node'
]
위의 코드는 Node.js에서 require.resolve.paths('svelte')를 사용하여 Svelte 패키지를 찾으려는 상황을 보여줍니다:
npm과 Yarn Classic은 패키지를 찾기 위해 여러 node_modules 폴더를 상위 디렉토리까지 계속 탐색합니다. 예를 들어, Svelte 패키지를 찾으려고 할 때 Node.js는 프로젝트 폴더부터 시작해 점점 상위 폴더까지 올라가면서 모든 node_modules를 순차적으로 확인합니다. 이렇게 패키지를 못 찾을 때마다 느린 I/O 작업이 반복되어 성능이 떨어질 수 있습니다.
결론적으로, 이러한 구조는 프로젝트의 성능 저하뿐만 아니라 유지보수의 어려움도 초래할 수 있습니다. 이는 특히 대규모 프로젝트나 복잡한 의존성 트리를 가진 경우에 문제가 될 수 있습니다.
위의 node_modules의 비효율적인 구조로 인해 유령 의존성 문제가 발생할 수 있습니다. 유령 의존성은 프로젝트에서 명시적으로 선언하지 않은 의존성이 node_modules 폴더에 존재하는 상황을 말합니다. 이러한 문제는 중첩된 node_modules 디렉토리 구조에서 특정 패키지가 상위 디렉토리의 의존성으로 인해 예상치 못하게 접근 가능해지는 경우에 발생합니다.
예를 들어, 프로젝트 A가 패키지 B에 의존하고 있고, 패키지 B는 패키지 C에 의존한다고 가정해봅시다. 프로젝트 A에서는 직접적으로 패키지 C를 사용한다고 선언하지 않았지만, node_modules 구조 덕분에 패키지 C가 프로젝트 A에서도 사용 가능하게 됩니다.
# 프로젝트 A의 package.json
{
"dependencies": {
"package-B": "^1.0.0"
}
}
# 패키지 B의 package.json (패키지 B는 package-C에 의존함)
{
"dependencies": {
"package-C": "^1.0.0"
}
}
이 상황에서 프로젝트 A는 명시적으로 패키지 C를 의존성에 추가하지 않았지만, node_modules 구조 덕분에 다음과 같이 사용할 수 있습니다:
// A 프로젝트의 코드
const c = require('package-C'); // 정상 작동
console.log(c.someFunction());
이처럼 유령 의존성은 코드 작성 시에는 아무 문제가 없어 보이지만, 나중에 패키지 B가 패키지 C와의 의존성을 제거하거나 버전을 변경할 경우, 프로젝트 A의 코드가 갑자기 동작하지 않거나 예기치 않은 오류를 발생시킬 수 있습니다.
npm과 Yarn Classic에서는 패키지 설치 시 많은 네트워크 요청과 디스크 I/O 작업이 발생하여 설치 속도가 느려질 수 있습니다. 특히 대규모 프로젝트에서는 이로 인해 개발 생산성이 크게 저하될 수 있습니다.
또한, npm과 Yarn Classic은 node_modules 폴더에 모든 의존성을 중첩된 형태로 저장하기 때문에, 의존성이 많은 프로젝트에서는 디스크 사용량이 급격히 증가하게 됩니다.
위의 밈처럼, 의존성 패키지는 한 프로젝트에서 적게는 수 GB, 많게는 수십 GB에 이르기까지 큰 용량을 차지하는 무시무시한 녀석입니다. 이 무게를 경량화 하기 위해 호이스팅 기법을 사용해서 중복을 최소화 하고 있긴 하지만 여전히 패키지의 무게는 무겁도 그로 인해 위에서 말한대로 유령 의존성 문제가 생기고 있습니다.
Yarn Berry의 탄생
Yarn Berry는 Yarn Classic의 다음 세대 버전으로, 사용자 경험 개선보다는 개발자 경험 개선을 목적을 가지고 있으며 기존 패키지 매니저들의 문제점을 해결하기 위해 개발되었습니다. 특히 성능 개선과 안정성, 일관성을 목표로 다양한 새로운 기능들이 추가되었습니다. Yarn Berry는 npm과 Yarn Classic에서 자주 겪었던 문제들을 어떻게 해결하는지 아래에서 알아보겠습니다.
Yarn Berry는 전통적인 node_modules 폴더를 사용하지 않고, 의존성을 PnP 방식으로 관리합니다. PnP는 의존성을 별도의 .zip 파일로 보관하고, 이를 필요한 때에만 메모리에 로드하여 사용합니다.
의존성의 저장: 프로젝트에 필요한 모든 의존성은 압축된 .zip 파일 형태로 Yarn Berry의 캐시 디렉토리에 저장됩니다. 이 파일들은 각각의 패키지를 독립적으로 관리하며, 패키지 간의 상호 의존성을 명확히 구분해 줍니다.
로드 시점의 의존성 연결: PnP는 실제로 패키지를 사용할 때 해당 패키지를 메모리에 로드합니다. 패키지 로드는 .pnp.js 파일을 통해 관리되며, 이 파일은 각 패키지와 그 의존성 간의 매핑을 담당합니다. 이 방식은 필요할 때만 의존성을 로드하므로, 불필요한 I/O 작업을 줄이고 속도를 높입니다.
실행 시 패키지 접근: 프로젝트가 실행될 때 PnP는 해당 프로젝트에 필요한 패키지 경로를 동적으로 설정하여, 의존성을 직접적으로 참조할 수 있게 만듭니다. 이렇게 동적으로 연결된 경로는 프로젝트에서 선언된 패키지 외에는 접근할 수 없게 되어, 유령 의존성 문제를 방지합니다.
PnP 방식을 쉽게 이해하기 위해, 이를 마우스를 컴퓨터 본체에 연결하여 드라이버가 설치되는 과정에 비유해 보겠습니다!
• 전통적인 node_modules 방식: 이 방식은 마치 모든 종류의 마우스 드라이버를 미리 컴퓨터에 설치해 놓는 것과 같습니다. 컴퓨터 본체가 필요로 하는 드라이버가 어떤 것인지와 관계없이, 사용되지 않는 드라이버도 모두 설치되어 있는 상황입니다. 이로 인해 디스크 사용량이 많아지고, 시스템이 불필요한 파일로 복잡해집니다.
• PnP 방식: PnP는 마우스를 컴퓨터에 연결했을 때, 컴퓨터가 자동으로 필요한 드라이버만 설치하는 것과 같습니다. 예를 들어, USB 마우스를 연결하면 컴퓨터가 그 마우스에 맞는 드라이버만 즉시 설치하여 작동시키는 것처럼, PnP는 실제 필요한 의존성만 로드하여 사용합니다. 다른 드라이버(패키지)는 설치되지 않고, 필요할 때만 연결되어 메모리나 디스크를 효율적으로 사용할 수 있습니다.
Yarn Berry를 설명하며 Yarn Berry는 개발자의 경험을 개선시킨다고 했었습니다. 그럼 Yarn Berry가 개발자 경험을 개선시키는것도 알겠고, 작동 원리도 알았는데 그럼 왜 이 원리가 성능을 향상시키는걸까요?
빠른 패키지 탐색: .pnp.js 파일을 통해 의존성을 직접 매핑하여, Node.js가 패키지를 빠르게 찾을 수 있습니다. 기존에는 모든 패키지를 검색하여 비효율적인 패키지 탐색을 했지만 .pnp.js 파일에는 모든 의존성을 직접 매핑하여 빠르게 필요한 패키지를 탐색합니다. 이는 디렉토리 탐색을 줄여 성능을 높입니다.
디스크 I/O 감소: 의존성을 .zip 파일로 관리하고 필요할 때만 로드하여, 불필요한 디스크 접근을 줄입니다.
기존 npm과 Yarn을 사용할 땐 .gitignore 파일에 항상 node_modules를 입력해 주어야 했습니다. 왜냐하면 node_modules 폴더는 수많은 패키지 파일과 중복된 의존성으로 인해 용량이 매우 크고, 프로젝트 버전 관리에 불필요한 데이터를 포함하기 때문입니다. 그래서 모든 팀원이 프로젝트를 클론할 때마다 yarn install이나 npm install 명령어를 실행하여 의존성을 다시 설치해야 하는 번거로움이 있었습니다.
하지만 Zero-Install을 사용하면 이러한 문제를 해결할 수 있습니다.
Zero-Install 방식에서는 패키지를 .yarn/cache 폴더에 압축된 .zip 파일 형태로 저장해 둡니다. 이 방식은 기존의 node_modules에 패키지를 풀어서 보관하던 것과 달리, 패키지를 압축하여 저장하고 이 압축된 형태로 버전 관리 시스템에 커밋합니다.
결론
이 긴 내용을 정리하자면, Yarn Berry는 기존의 npm과 Yarn Classic에서 발생하는 다양한 문제를 해결하여 개발자 경험을 크게 개선한 Package Manager입니다.
Yarn Berry의 핵심 장점은 다음과 같습니다:
하지만 세상엔 완벽한건 없습니다. Yarn Berry는 많은 장점이 있지만 몇 가지 단점도 존재합니다.
첫 번째로 호환성 이슈가 있습니다. PnP 방식은 일부 패키지나 도구와 호환되지 않을 수 있어, 설정을 추가하거나 예외 처리를 해야 할 때가 있습니다. 아직 경험해 보지 못했지만 일부 패키지가 호환되지 않는 경우가 있다고 하네요...?
두 번째로 복잡한 설정과 러닝 커브입니다. 기존의 node_modules 방식에 익숙한 개발자에게는 새로운 설정 파일과 방식(PnP, Zero-Install)이 다소 복잡하게 느껴질 수 있습니다. 또한 Yarn Berry의 새로운 기능과 개념을 이해하고 활용하기 위해서는 어느 정도 학습이 필요합니다. 러닝 커브가 올 수도 있죠.
그렇기에 본인의 프로젝트를 이해하고 상황에 맞게 이런 도구들을 적절하게 이용하는게 가장 중요한것 같습니다. 언제나 완벽한건 없고 도구는 프로젝트의 효율성과 생산성을 높이기 위한 수단일 뿐, 절대적인 정답은 없다고 생각합니다.
틀린 내용 또는 읽기 불편하신 점이 있다면 언제든지 지적해 주시면 감사하겠습니다. 긴 글 읽어주셔서 감사합니다!
Reference
https://toss.tech/article/node-modules-and-yarn-berry
https://html-jc.tistory.com/676