개발

PnP 그리고 Yarn berry가 뭐길래 쓰는지 이제 알아보자

Padd60 2023. 10. 24. 02:14

회사에서 신규 프로젝트를 시작할때 사용할 보일러플레이트를 만들다가 적용하게된 yarn berry를 적용하면서 알게된 내용을 정리해본다.

 

yarn berry 적용하게된 이유

기존에 사용하던 yarn classic에서 monorepo를 적용하고 싶어 리서치하다가 yarn berry에 있는 workspace를 사용해 구현한 블로그를 보게 되어 따라 적용하다가 체택하게되었습니다.

 

나중에 알고 보니 yarn classic에도 workspace가 지원되서 monorepo사용에 문제가 없는것을 알게되었을때는 좀 허탈했지만 그래도 공부했다는 마음으로 지내고 있습니다.

 

그러면 yarn berry 쓰면 뭐가 좋은데?

PnP(Plug'n'Play)

yarn 2버전부터 지원되는 기능으로 해당 기능을 사용하면 의존성 라이브러리를 .yarn폴더내 cache내에 zip파일로 관리하게 된다.

yarn cache

즉 node module이라는 폴더를 만들지 않는다

그러면 이걸 사용하면 무슨 이득이 있는가?

바로 node module을 사용했을때의 단점을 보완해서 사용이 가능하다.
기존 node module의 단점은 라이브러리들이 현재 접근한 최상위 루트의 node module로 호이스팅되어 관리되어진다는점이다.

 

쉬운말로

A
B
﹂A


위처럼 두개의 A, B라는 라이브러리가 있다고 치면
B는 하위에 A라는 라이브러리를 의존해 가지고 있어 
그냥 단순히 하나씩 다운되면 A라는 공통된 라이브러리가 각각 다운받아지겠지만
실제로는 호이스팅되어 

A
B


위처럼 상위로 끌어올려져 라이브러리가 다운 및 관리되어진다.

 

아니 그러면 저렇게 사용하면 중복으로 안 다운받고 훨씬 더 경제적인 방법아니냐 왜 단점이냐??라고 생각할수 있겠다.

 

하지만 이는 큰 단점이 될 수 있는데 바로 ﹂하위에 있던 의존성이 사실은 상위로 올려져 비게된것을 말하는 유령디펜던시 덕분이다.

 

예시로 아래와 같은 상황에서 문제가 발생할 수 있겠다.

1. 최상위로 호이스팅된 A라는 라이브러리가 손상되었을때
2. A와 B 하위 A가 버전이 다르고 B는 구버전인 A만 지원가능했을때
3. 모노레포 환경에서 같은 최상위 루트내 node module폴더가 여러개 있을 경우
4. 프로젝트가 구동되는 환경에서 해당 디바이스내 같은 루트에 프로젝트 외적으로 node module이 있을경우

1번상황부터 보면 A라는 라이브러리가 손상되었을때 B도 해당 A를 바라보고 있어 같이 망가져버리게 된다.

2번상환은 A가 호이스팅되고 최신버전으로 align되어 B가 구동되지 않을것이다.

 

3번상황은 모듈탐색시 루트에 있는 node module을 전부 찾기에 모노레포 하위의 프로젝트간 node module간 영향을 받아 원하지도 않는 버전의 라이브러리를 사용하게 되거나 앱이 구동도 안되는 경험을 맛볼수 있다.

4번상황은 나도 모르게 방치해둔 node module이 프로젝트와 동일 뎁스의 폴더에 존재한다면 원치 않는 라이브러리 버전을 사용하거나 위와 마찬가지로 앱이 망가질 수 있다.

 

이처럼 의존성 관리가 엉망이 될 수 있는 호이스팅을 방지하고 각각의 의존성을 분명히 명시해 라이브러리간 의존성 구조가 꼬이지 않도록 하는것이 PnP이다.

 

yarn의 PnP를 사용하게되면 `.pnp.cjs`라고 파일이 생성되는데 이 파일만 봐도 의존성을 어떻게 관리하는지 볼 수 있다.

 

["@babel/code-frame", [\
  ["npm:7.21.4", {\
    "packageLocation": "./.yarn/cache/@babel-code-frame-npm-7.21.4-5db83e65be-e5390e6ec1.zip/node_modules/@babel/code-frame/",\
    "packageDependencies": [\
      ["@babel/code-frame", "npm:7.21.4"],\
      ["@babel/highlight", "npm:7.18.6"]\
    ],\
    "linkType": "HARD"\
  }]\
]],\

패키지가 의존하는 다른 패키지의 버전이 명확히 명시되어 관리되고 있다.

이처럼 하위의존성을 관리하여 작동하며 yarn의 경우 cache에 압축된 zip파일을 .pnp.loader.mjs로 라이브러리를 읽어와 구동된다.

 

Zero install

앞서 설명한 PnP가 .yarn 내 cache 폴더내 zip파일들로 관리되고 있다는것을 보았다.

zero install은 이제 진짜 별거 없다. 이를 github와 같은 원격저장소에 같이 올려서 관리하면 된다.

일반적으로 node module을 사용할때는 용량이 크기때문에 보통 git ignore에 넣어서 원격저장소에는 올리지 않게 되는데 yarn의 PnP를 사용하게 되면 zip파일로 압축되어 용량이 크지 않아 원격저장소에 올려도 문제가 되지 않고 해당 zip파일들만 pull 또는 clone하게 되면 굳이 프로젝트 구동을 위해 yarn 또는 yarn install을 하지 않고 바로 로컬에서 프로젝트 구동이 가능해진다.

그래서 zero install이다

 

실제로 yarn에서 안내해준대로 따라가면 

### yarn ###
# used Zero-Install
.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

# unused Zero-Install
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*

위처럼 git ignore만 조절하여 zero install의 사용을 정하는것을 볼 수 있다.

 

그래서 이거 적용은 어떻게 하는거지?

적용방법은 간단하다.
해당 프로젝트에서 터미널 열고
`yarn set berry`
`yarn` 하면 끝이다

❗️여기서 주의점 타입스크립트와 vscode를 사용하고 있다면 별도의 yarn에서 제공해주는 플러그인과 익스텐션을 설치해야한다.

이유는 기존 vscode의 타입스크립트가 node module을 바라보아 해당 내용을 .yarn의 cache를 바라보게해야 타입에러가 나지 않는다. 그리고 zip파일을 인식할 수 있도록 익스텐션도 하나 설치해야한다.

정리해보자면 vscode + typescript를 사용해서 개발하는 프로젝트이라면

 

yarn set berry
yarn dlx @yarnpkg/sdks vscode

 

위 플러그인 설치

yarn 이후

누르고

작업영역버전 사용하면 타입 오류가 해결되어 잘 사용할 수 있겠다.

 

참조

https://yarnpkg.com/getting-started/migration

 

Benefits | Yarn

Learn why the Yarn team recommends to migrate from Yarn 1.x to modern releases.

yarnpkg.com