개발👩‍💻/프론트엔드

npm yarn yarn berry

gigibean 2022. 7. 4. 15:34
728x90

패키지 관리 툴이다.

Node Package Manager

패키지 관리자: command line client + npm registry
기본적으로 node.js 내에 npmd은 내장되어 있다.

기본적으로 패키지 관리자가 없다면 따로 코드를 작성하거나 다운로드 받아서 붙여주어서 사용해야하지만 이러한 패키지 관리 툴이 있다면 온라인에서 바로 한줄의 코드로 해당 코드 및 프로젝트를 받아와 바로 사용할 수 있다.

package.json을 사용하여 의존성을 관리하고 어떤 패키지가 다운로드되었는지 어떤 버전을 사용하는지 등을 기록하여 어드 곳에서도 동일한 환경을 구축할 수 있다.

Yarn

페이스북에서만든 패키지 관리자 툴이다. npm 과 같은 기능을 수행한다.
yarn은 npm의 단점을 향상시키기 위해 만들어진 매니저 툴로써 여기서 말하는 npm의 단점은 속도, 안정성, 보안성 등이 있다.

속도

yarn은 다운받은 패키지 데이터를 캐시에 저장하여 중복된 데이터는 다운로드하지 않고 캐시에 저장된 파일을 활용함으로써 npm에 비해 패키지 설치속도가 빠르다. 그리고yarn은 병렬적으로 다운로드 되기 때문에 순차적으로 이뤄지는 npm 보다 빠르기가 증가한다.

안정성과 보안성

npm은 패키지가 설치될 때 자동으로 코드와 의존성을 실행할 수 있도록 허용 하지만 yarn은 yarn.lock이나 package.json으로부터 설치만하며 yarn.lock은 모든 디바이스에 같은 패키지를 설치하는 것을 보장 -> 버전차이로 인해 생기는 버그를 방지해줄 수 있다.

Yarn Barry

Npm의 문제정

NPM은 node 설치시 기본으로 제공되어 범용적으로 사용되고 있으나 비효울적이거나 깨져있는 부분이 많다.

비효율적인 의존성 검색

NPM은 **파일 시스템**을 이용하여 의존성을 관리한다. 익숙한 node_modules 폴더를 이용하는 것이 특징이다 이렇게 관리했을 때 의존석 검색은 비효율적으로 동작한다.
예를 들어 `/Users/toss/dev/frontend 폴더에서 require() 문을 이용하여 react 패키지를 불러오는 상황을 가정해보자

라이브러리를 찾기 위해 순회하는 디렉트리의 목록을 확인하려고 할 때, Node.js에서 제공하는 require.resolve.paths() 함수를 사용할 수 있다. 함수는 NPM이 검색하는 디렉토리 목록을 반환한다.

$ node  
Welcome to Node.js v12.16.3.  
Type ".help" for more information.  
\> require.resolve.paths('react')  
\[  
  '/Users/toss/dev/toss-frontend-libraries/repl/node\_modules',  
  '/Users/toss/dev/toss-frontend-libraries/node\_modules',  
  '/Users/toss/node\_modules',  
  '/Users/node\_modules',  
  '/node\_modules',  
  '/Users/toss/.node\_modules',  
  '/Users/toss/.node\_libraries',  
  '/Users/toss/.nvm/versions/node/v12.16.3/lib/node',  
  '/Users/toss/.node\_modules',  
  '/Users/toss/.node\_libraries',  
  '/Users/toss/.nvm/versions/node/v12.16.3/lib/node'  
\]  

목록에서 확인할 수 있는 것처럼 NPM은 패키지를 찾기위해서 계속 상위 디렉토리의 node_modules 폴더를 탐색한다 따라서 패키지를 바로 찾지 못할 수록 readdir, stat과 같은 느린 I/O 호출이 반복된다. 경우에 따라서는 I/O 호출이 중간에 실패하기도 한다.

typescript 4.0까지는 node_modules 이용한 패키지 탐색이 너무 비효율적인 나머지 패키지를 처음으로 Import하기 전까지는 node_modules 내부의 타입정보를 찾아보지 않기도 했다.

환경에 따라 달라지는 동작

NPM 패키지를 찾지 못하면 상위 디렉토리의 node_modules 폴더를 계속 검색한다. 이 특성때문에 어떤 의존성을 찾을 수 있는지는 해당 패키지의 상위 디렉토리 환경에 따라 달라진다.

예를 들어 상위 디랙토리가 어떤 node_modules를 포함하고 있는지에 따라서 의존성을 불러올 수 있기도 하고, 없기도 한다. 다른 버전으 의존성을 잘못 불러올 수 있는 여지도 존재한다.

이렇게 환경에 따라 동작이 변하는 것은 좋지 않다 해당 상황을 재현하기 까다로워지기 때문이다.

비효율적인 설치

NPM 에서 구성하는 node_modules디렉토리구조는 매우 큰 공간을 차지한다.
일반적으로 간단한 CLI 프로젝트도 수백 메가 바이트의 node-modules 폴더가 필요하다.

용량만 많이 차지할 뿐아니라 큰 노드 모듈 디렉토리 구조를 만들기 위해서는 많은 I/O 작업이 필요

노드 모듈 폴더는 복잡하기 때문에 설치가 유효한지 검증이 어렵다.
예를 들어 수백개의 패키지가 서로를 의존하는 복잡한 의존성 트리에서 노므 모듈의 이렉토리 깊이 구조는 깊어진다.

이렇게 깊은 트리 구조에서 의존성이 잘 설치되어 있는지 검증하려면 수많은 I/O호출이 필요하다.
일반적으로 디스크 I/O호출은 메모리의 자료구조를 다루는 것보다 훨씬 느리다. 이런 문제로인해 yarn v1이나 npm은 기본적인 의존성 트리의 유효성까지만 검증하고 각 페키지의 내용이 올바른지는 확인하지 않는다.

유령 의존성 (Phantom Dependency)

NPM 및 yarn v1에서는 중복해서 설치되는 node-modules를 아끼기 위해 끌어올리기(hoisting) 기법을 사용한다.

이런식의 모습을 하고 있고 package-1은 원래 require()할 수 없었던 B(1.0)라이브러리를 불러올 수 있게 되었다.

이렇게 끌어올리기에 따라 직접의 존하고 있지 않은 라이브러리를 require()할 수 있는 현상을 유령 의존성이라고 부른다.

유령 의존성 현상이 발생할 때 package json에 명시하지 않은 라이브러리를 조용히 사용할 수 있고, 다른 의존성을 제거했을 대 소리없이 같이 사라지기도 한다. 이러한 특성은 의존성 관리 시스템을 혼란스럽게한다.

Plug n Play (PnP)

Yarn berry는 새로운 plug n play 전략을 이용해 해결한다.
PnP를 사용하면 더이상 설치한 패키지를 node_module에 저장하지 않는다.
.yarn/cache 폴더에 따로 의존성 정보가 저장되고, pnp.cjs 파일에서 의존성을 찾을 수 있는 정보가 기록된다.

["react", [\
        ["npm:17.0.2", {\
          "packageLocation": "./.yarn/cache/react-npm-17.0.2-99ba37d931-b254cc17ce.zip/node_modules/react/",\
          "packageDependencies": [\
            ["react", "npm:17.0.2"],\
            ["loose-envify", "npm:1.4.0"],\
            ["object-assign", "npm:4.1.1"]\
          ],\
          "linkType": "HARD"\
        }]\
      ]],\

각 의존성은 Zip 아카이브로 관리된다.
.yarn/cache 폴더에 들어가보면 여러 압축파일들이 있다.

즉, .pnp.cjs 에 명시된 정보에 따라 각 패키지들은 동적으로 참조된다.
압축파일로 동적으로 관리함으로써 yarn berry PnP를 사용하여 node_modules 방식보다 훨씬더 프로젝트 용량을 줄일 수 있다.
더하여 기존의 트리 형태의 node_modules 폴더를 순회할 필요가 없기 대문에 검색 속도 또한 증가되었다.

그리고 기존에 node_modules를 사용할 때는 git에서 협업 시
해당 패키지들을 담고있는 node_modules.gitignore에 넣고
pull 할 때 등 협업시 npm i를 하여 로컬에 node_modules를 생성해주어야 했다.
하지만 pnp 방식은 zip 아카이브로 의존성을 관리하기 때문에 용량 자체가 작아져,
.yarn 폴더 자체를 원격 저장소에 업로드 하여 zero-install 방식을 사용할 수 있다.
그렇게 되면, git clone 후 추가 설치 필요 없이 바로 실행할 수고, CI 속도도 빨라질 수 있다.

참고)
https://toss.tech/article/node-modules-and-yarn-berry
https://velog.io/@altmshfkgudtjr/yarn2%EC%99%80-%ED%95%A8%EA%BB%98-Plug-n-Play%EB%A5%BC-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90
https://lovemewithoutall.github.io/it/node-module-phantom-dependency/

Yarn Berry 사용하기

yarn && yarn set version berry

만약

.yarnrc.yml 파일에

nodeLinker: node-modules

노드 링커가 연결되어 있다면 해당 코드를 삭제해준다.

rm -rf node_modules && yarn install

패키지들이 zip 아카이브로 관리되기 때문에 기존의 방식으로는 정상적으로 타입이 불러와지지 않는다.
Editor SDK 설정을 하기 전에 먼저 VSCode에서 zipfs 을 설치해줍니다. 이 extension는 zip 아카이브에서 직접 파일을 읽을 수 있도록 VSCode에 지원을 추가한다.
Editor SDK 설정은

yarn dlx @yarnpkg/sdks vscode && yarn plugin import typescript

설치 후에는 .yarn/plugins/@yarnpkg 폴더 아래에 plugin-typescript.cjs 파일이 생성된다. 기타 플러그인
마지막으로 command + shift + p키 (맥북 기준)를 눌러 TypeScript 버전 선택..을 검색해 Use Workspace Version을 선택해 workspace의 typescript sdk로 변경해주면 된다.

.gitignore zero-install 사용시

.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.vscode

참고)
Yarn Berry를 사용해보자
Yarn berry에서 React + Jest + Cypress + TypeScript + Github Actions CI/CD를 세팅해보자 :: haranglog
VScode 에서 Typescript 환경 설정하기. typescript설치에서 TSlint, prettier 까지 | by geonmo-nine | Medium

반응형