패키지 매니저의 역사
npm의 문제점
중복되는 의존성 다운로드: 의존성이 의존하는 의존성, 의존성이 의존하는 의존성도 있을 것이다. 그 중에서 패키지명과 버전이 일치하는 경우도 있을 것인데, npm v1, v2에서는 그 경우에도 중복으로 다운로드를 실행하여 의존성을 설치하는 시간이 길었다.
설치 방식에서 초래하는 request waterfall: 의존성을 순차적으로 해석하고 다운로드하여, 병렬적으로 다운로드를 하는 것이 불가능하였다. 의존성 1을 설치가 끝난 뒤에, 의존성 1의 package.json을 확인하고 의존성1의 의존성인 의존성 2를 설치하였다. 의존성 2의 설치가 끝난 뒤에, 의존성 2의 package.json을 확인하고 의존성 2의 의존성인 의존성 3을 설치하였다. 병렬적으로 다운로드를 할 수 없어 의존성 설치 시간이 길었다.
패키지 버전 고정 불가능: lockfile이 없어서, 각자가 의존성의 다른 버전을 사용하고 있었다. package.json에서 패치버전까지 고정하는 게 아니라 major version 고정(^), minor version 고정(~)만 하는 경우에는 모두가 다른 패키지 버전으로 개발을 하고, 배포를 할 때는 다른 버전으로 배포되기도 하였다. 문제가 발생하여도 모두가 다른 패키지 버전을 사용하고 있었기에 재현이 힘든 경우도 많았다.
read package1/package.json
└─ find package2
└─ download package2
└─ read package2/package.json
└─ find package3
└─ download package3
read package.json, find package, download package이 순차적으로 발생하면서 npm install 시간이 길어진다.
다운로드 병목 해결사 yarn classic
의존성 캐싱: 패키지명과 패키지 버전이 동일한, 즉 중복되는 패키지를 다운로드하는 대신에 캐싱을 하여 다운로드 시간을 줄였다. 중복되는 패키지의 경우에는 후술되는 dependency graph를 그릴 때 상위 디렉토리로 이동시키는 hosting(호이스팅)이 발생한다.
dependency graph를 먼저 그려서 병렬 다운로드: 필요한 dependency를 그래프 형태로 모두 그린 다음에 병렬 다운로드하여 패키지 다운로드에 걸리는 시간을 줄였다. dependency graph를 그리는 단계는 다음과 같이 나타낼 수 있다.
fetch package metadata from registry (JSON)
→ read dependencies from metadata
→ resolve versions (lockfile 우선)
→ 전체 dependency graph 완성
- Download Phase
Download packages in parallel
- lockfile을 통한 패키지 버전 고정: 설치되는 패키지 버전을 patch version까지 명시한 lockfile을 통해 모두가 동일한 의존성 버전에서 개발을 할 수 있게 되었다. CI나 배포 환경에서도 동일한 의존성 버전 사용이 가능해져 에러를 재현하는 난이도가 낮아졌다.
yarn classic의 문제점
yarn classic도 무적은 아니었고, 문제의 해결로 인해 파생되는 또다른 문제가 발생하였다.
암묵적 의존성으로 인한 프로젝트 의존성 붕괴: node.js에서는 import되는 module을 결정할 때(module resolution) import하는 파일의 디렉토리부터 상위로 한 단계씩 올라가며
/node_modules/pakcage디렉토리가 있는지 탐색하는데(module resolution), 이 때 hoisting으로 인해 탑레벨/node_modules에package.json에 명시되지 않은 패키지가/node_modules로 들어가서 import가 가능해진다. package.json에 명시되지 않은 패키지를 불러올 수 있게 되면서 의존성 관리가 망가진다. 이렇게 package.json 명시되진 않았지만 접근 가능한 패키지를 암묵적 의존성(phantom dependencies)라고 한다.디스크 I/O로 인한 module reslolution 속도 저하 Node.js module resolution 알고리즘으로 발생하는 무수한 디렉토리 스캔으로 인한 디스크 I/O 작업이 발생하는데, 메모리 읽기와 쓰기에 비해서 매우 느리다. module resolution은
yarn dev또는yarn build때 실행되기에 개발 서버 시작과 빌드 시간의 저하에 영향을 미친다.
import lodash
→ filesystem walk-up
→ /node_modules/lodash 있네? OK
암묵적 의존성 해결사 yarn berry
매핑 테이블 파일로 위치 밝혀주기:
.pnp.cjs파일에서 각 의존성의 경로를 명시하고, node.js의require()동작을 덮어써서 .pnp.cjs를 참고하혀 의존성 경로를 찾게 만든다. 의존성의 위치가 명확해져 암묵적 의존성이 발생할 가능성이 차단된다.무수한 디스크 I/O 대신 파일 하나에서 로직 전부 처리: 앞서 말했듯이 node.js의
require()동작을 덮어썼으므로 더이상 반복적인 Disk I/O가 발생하지 않는다.
import lodash
→ .pnp.cjs 로드
→ 현재 패키지의 dependency graph 확인
→ package.json에 선언됨?
❌ No → 즉시 에러
Top comments (0)