Yarn Berry 알아보기

date
Jan 23, 2023
thumbnail
slug
YarnBerry
author
status
Published
tags
Yarn Berry
summary
라즈베리…
type
Post
updatedAt
Jan 25, 2023 10:48 AM
디자인 시스템을 만들다가 yarn berry의 Pnp기법에 대해 궁금해져서 찾아보았다.

개요

yarn 2.x 이상의 버전을 요즘에는 yarn berry라고 부르고 있다. 그리고 yarn 1.x버전은 yarn classic이라고 불린다. 이 yarn classic은 오랫동안 node 생태계에서 중요한 역할을 해왔지만, 오래된 만큼 많은 문제를 가지고 있었다.

1.비효율적인 모듈 탐색

기존의 Node Resolution Algorithm은 파일측면에서 의존성을 추론했다.
🔁
“이 파일이 여기에 있습니까? 아니요? 그럼 부모를 살펴보자 여기에 존재합니까? 아직도 아니야? 안타깝지만… 상위 폴더입니다!” https://classic.yarnpkg.com/lang/en/docs/pnp/ 중 일부분
위와 같이 계속 탐색하며 못 찾으면 상위로 올라가는 것이다. 이런 방법은 많은 단점을 야기시켰다.
  • 오래 걸리는 install 시간
    • node_modules는 기본적으로 많은 파일들을 담고있다. 따라서 install 할 때 이 node_modules를 만들어내는 작업이 install 과정에 70%를 차지한다고 한다. 그리고 이미 설치된 패키지도 node_modules가 포함하고 있는지 상위 node_modules로 올라가 비교해야 했다. 이부분에서 기존 설치가 있어도 저장이 되지 않는다는점이 낮은 속도를 야기시켰다.
  • 최적화 한계
    • install 할때는 많은 I/O가 발생하기 때문에, node_moduels 패키지 관리자는 단순한 파일복사 작업 이상으로 무언가를 하기에는 여유가 없었다. 따라서 최적화 할 요소가 별로 없었다.
  • 노드 특성
    • 노드의 특성상 노드에서는 패키지라는 개념이 없다. 따라서 해당 파일에 접근할 수 있는지를 판단할 수 없게 된다. 어느날 어떤 의존성 파일이 패키지 저장소를 통해 접근할 수 없다면 작성한 코드가 개발 단계에서는 작동하지만 프로덕션 단계에서는 중단될 가능성이 생기게된다.
  • 런타임시 많은 호출
    • node의 알고리즘은 모든 단일 파일들을 로드할 위치를 파악하기 위해 stat, readdir과 같은 느린 I/O호출이 많이 발생하게 된다. node 어플리케이션을 실행할때 많이 걸리는 이유가 바로 이것 때문이다.
  • 한계적인 설계
    • node_modules 패키지 관리자가 적절하게 중복제거를 할 수 없도록 폴더가 디자인 돼 있었다. 어떤 트리레이아웃은 호이스팅 기법을 통해 최적화 할 수 있었지만, 설계상 그렇게 할 수 없는 트리도 있었다. 이로인해 디스크 사용량이 필요 이상으로 높아지고, 일부 패키지가 메모리에서 여러번으로 인스턴스화 되기도 했다.

2.유령의존성

위에 한계적인 설계에서 어떤 트리레이아웃은 호이스팅 기법으로 적절하게 패키지 중복제거를 했다고 하는데, 이 호이스팅 방법은 치명적인 문제를 야기 시켰다.
notion image
호이스팅 방법은 의존성이 의존하는 중복되는 패키지들을 상단으로 끌어올리(hoisting)는 방법을 통해 제거한다.
이렇게 중복되는 패키지들이 상단으로 끌어올려지면, 개발자가 이 패키지에 접근할 수 있게 된다. 그 말은 즉, 프로젝트에 존재하지 않는 의존성을 의존하게 된다. 바로 이 의존성을 유령의존성 (Phantom Dependency)라고 한다. 이렇게 존재하지 않는 의존성을 접근할 수 있게 되면
위와 같은 사례를 겪게 될 것이다.

How to Resolve

서론이 많이 길었다. 위와 같은 문제들을 yarn 개발자들은 어떻게 해결했을까?
먼저 기존의 yarn classic은 package.json을 기반으로 의존성 트리를 만들고, 디스크에 node_modules 디렉토리 구조를 만들었다. 이미 패키지 의존성 트리를 다 꿰뚤고 있는 것이다. Node 내장 의존성 관리 시스템으로 디스크에서 패키지를 찾는 작업을 하지 않아도 된다. 이미 의존성트리를 알고 있으니, 단순히 패키지의 위치를 알려주면 낭비가 심한 I/O호출들을 하지 않아도된다. 바로 여기서 시작된게 바로 Plug'n'Play(PnP) 기법이다.
💡
생각해 보면 Yarn은 종속성 트리에 대한 모든 것을 알고 있으며 심지어 설치합니다! 그렇다면 Node가 디스크에서 패키지를 찾는 작업을 수행하는 이유는 무엇입니까? 단순히 Yarn에 쿼리하여 패키지 Y에 필요한 패키지 X를 찾을 위치를 알려주는 것은 어떻습니까? 이것이 바로 Plug'n'Play(약칭 PnP)입니다. https://classic.yarnpkg.com/en/docs/pnp 중 일부분

PnP

PnP로 모듈을 깔았을땐 node-modules가 생기지 않는다. 대신 yarn/cache 폴더와 처음 본 .pnp.cjs가 node_modules 역할을 해주게 된다. yarn/cache에 의존성들의 정보들이 .zip으로 관리되고, .pnp.cjs에 의존성을 찾을 수 있는 트리가 관리된다. 바로 이 .pnp.cjs를 통해 yarn install을 할때 낭비가 심한 디스크 I/O require()문을 호출하지 않고, 어떤 의존성이 어디에 위치해 있는지를 알 수 있는 것이다.
notion image
notion image

Zero-Install

yarn berry에서는 zero install 전략을 내세우고 있다. 개발자마다 작업을 시작하는 환경은 다 다르다(노트북의 차이, 노드 버전의 차이 등). 하지만 yarn을 했을 때는 다 똑같이 돌아가야 한다. yarn에서 이부분을 도와주는 것은 잠금파일 (yarn.lock)을 사용하는 것이다. 하지만 이 yarn.lock 파일은 버그를 도입할 가능성이 있어 불안전하다.
  • 네트워크에 장애가 발생하여 패키지를 더 이상 사용할 수 없게 될 수도 있다.
  • 자격 증명이 교체되어 인증 문제가 발생할 수 있다.
다음과 같이 여러가지 버그 가능성이 있어서 yarn 에서는 Zero-Install 전략을 내세우고 있다.
Zero-Install은 단순히 zip파일로 관리되는 의존성들을 변경사항에 모두 포함시켜 커밋시키면 된다. 그리고 레포지토리에 파일이 올라가 있으니 개별적인 Yarn install 이 필요없고 단순히 클론만 받으면 된다. 이것이 바로 Zero-Install이다.

마치며

이렇게 Yarn Berry 공식문서를 뒤지며 Yarn Berry에 대해 알아봤는데, Yarn Berry는 정말 기존에 있던 불편함들을 싹 날려버린 정확한 솔루션이란 것이 확 느껴진다. PnP 기법부터 Zero-Install 전략까지 대단한 것 같다.

참고