공부/기술 개념 정리

패키지 매니저의 정의와 차이 (yarn, pnpm, npm)

dev_jiwonpark 2025. 6. 27. 14:23

Flab 과정을 들으면서 멘토님이 나에게 패키지 매니저에 대해 질문했는데 제대로 답을 하지 못했었다..;;

써보기만 했지 그것의 개념들은 잘 알지 못한 상황이어서 멘토님께서 공부하기 좋은 아티클들을 주셨다. 해당 아티클을 보고 정리한 내용들을 블로그에 작성해보려고 한다! 밑에 참고한 아티클의 링크를 작성해놓았습니다. 

1. 패키지 매니저란?

import axios from 'axios'

위의 코드를 작성했을 때 패키지 매니저는 axios라는 패키지를 올바르게 참조할 수 있도록 포장해주는 프로그램.

비유하자면 내가 만약 샌드위치(= 개발)를 만들고 싶은데 다양한 재료(=react, axios 같은 패키지 & 라이브러리) 가 필요함.

근데 어떤 재료가 필요한지 대신 사다주는 도우미(= 매니저)같은 것.

 

JavaScript 표준인 ECMAScript ⇒ 정확한 절대 경로 or 상대 경로로 import 할 수 있지만

간단하게 사용하고 싶다면??

import React from 'react';
import { sum } from '@toss/utils';

 

하지만 어떤 버전을 사용할건지 명시하지 않아 모호함.

때문에 package.json파일에 사용할 버전을 명시하는 것이다!

=> 그리고 npm install, yarn install을 할때 명시된 버전을 설치하게 되는것.

비유하자면 package.json은 장바구니 메모지 같은 것.

 

1.1 패키지 매니저가 동작하는 세가지 단계

(1) Resolution

(2) Fetch

(3) Link

⇒ 위 세 단계로 패키지 매니저는 동작함.

(1) Resolution

  • 라이브러리 버전 고정
  • 라이브러리의 다른 의존성 확인
  • 라이브러리의 다른 의존성 버전 고정

⇒ 어떤 버전의 라이브러리를 사용할지 명확하게 정하는 과정.

 

1. package.json에서 라이브러리 버전 확인

"dependencies": {
  "react": "^18.2.0"
}
// React는 18.2.0 이상, 19.0.0 미만 이면 가능! 
// 패키지매니저는 범위 안에서 가능한 최신버전 사용하려고함.

 

2. 의존성의 의존성 문제

패키지끼리 의존성을 갖는 상황 ⇒ 패키지 간 의존성, 그리고 또다른 의존성을 갖는지 추적

 

3. 의존성의 버전 고정

ex )

  • A 개발자의 PC에는 React 18.1.0
  • B 개발자의 PC에는 React 18.2.0

→ "내 PC에선 잘 돼요" 현상 발생

따라서 모든 버전 결정 결과를 기록하는것이 필요

= yarn.lock, package-lock.json에 저장

(2) Fetch

  • 결정된 버전의 파일을 다운로드 하는 과정

Resolution 결과로 결정된 버전을 다운로드하는 과정

→ yarn.lock에 명시된 패키지들

네트워크를 통해 필요한 파일들을 가져옴. (= Fetch)

(3) Link 단계

  • Resolution/Fetch 된 라이브러리를 소스 코드에서 사용할 수 있는 환경을 제공하는 과
    • 실제로 import/ require 구문을 사용하는 환경

 

아래의 사례를 통해 Link 단계 이해해보자!

  1. npm Linker (npm의 의존성 설치 방식)
    1. node_modules에 의존성(패키지들)을 직접 하나하나 넣는 방식
    2. If 패키지들이 또다른 패키지를 갖고 있음 → 그 안에 또 node_modules 만들어서 넣음.
    3. 계속 폴더 속에 폴더 , 반복 구조임.
my-service/
└─ node_modules/
|  ├─ react/ <-- 1단계 의존성
|  |  
|  └─ @tossteam/tds-mobile/ <-- 1단계 의존성
|     └─ node_modules/
|         └─ @radix-ui/react-dialog <-- 2단계 의존성
|
└─ src
    └─ index.ts

 

위 방식의 문제점

  • 찾기 느림.
    • 폴더의 계층이 많아질수록 import/require 속도가 느려짐.
    • 호이스팅을 사용한다해도 완전한 최적화를 기대하기 어려

 

2. pnpm Linker (pnpm의 의존성 설치 방식)

위 단점들을 개선하기 위해 만들어짐. (fast, disk space efficient 한 패키지 매니저)

 

Q. 어떻게 위 문제점을 개선했는지?

A. Hard Link , 파일 복사 대신 별칭을 만들어 연결만 해주는 방식으로 개선

⇒ But 기존 node_module 디렉토리 그대로 사용

그래서 대신 빠르고 용량최적화를 위해 Hard Link 방식(=별칭 alias 사용) 사용함.

 

비유하자면 아래와 같음.

npm 방식 (개인 창고)

각 집마다 같은 우유 따로따로 사서 냉장고 보관

pnpm 방식 (공유창고)

우유를 공유 창고에 1개만 두고 각 집에 “여기서 가져다 써” 라고 이름표 붙임

store/
  └─ react@18.2.0/

my-project/node_modules/
  └─ react → 📦 store/react@18.2.0  (하드 링크)

npm과 비교했을때 node_module이 같은 구조이지만 파일을 하나씩 복붙하지 않기때문에 속도는 빠르게 느껴짐.

 

3. PnP(Plug'n'Play)

node_modules 디렉토리 없이 의존성 처리!

PnP에서 패키지 import 할때 중요한 점

  1. “어떤 파일”에서 import하는지
  2. “무엇”을 import하는지

⇒ 명확한 타케팅, 더이상 node_modules 순회하는건 중요치 않음.

때문에 Javascript 객체로 처리

(1) yarn install 하고 난 후 .pnp.cjs 파일 생성

⇒ Javascript Map으로 의존성을 찾음.

["my-service", /* ... */ [{
  // ./my-service에서...
  "packageLocation": "./my-service/",
  "packageDependencies": [
    // React를 import 하면 18.2.0 버전을 제공하라.
    ["react", "npm:18.2.0"]
  ]
]

/* react 패키지 중에서 */
["react", [
  /* npm:18.2.0 버전은 */
  ["npm:18.2.0", {
    /* 이 위치에 있고 */
    "packageLocation": "./.yarn/cache/react-npm-18.2.0-98658812fc-a76d86ec97.zip/node_modules/react/",
    /* 이 의존성들을 참조한다. */
    "packageDependencies": [
      ["loose-envify", "npm:1.4.0"]
    ],
  }]
]],

 

(2) Node.js 프로세스, PnP Map 메모리에 로드

→ import, require 에서 Map 참조

 

yarn.lock 기반으로 파일만 만들면 되니 설치속도가 빨라짐.

메모리에 파일 로드 → Map 연산만 하면 끝 _ 디렉토리 순회할 필요 X

But 단점 존재..

  • Node.js 프로세스 속도가 느림.
  • node_modules 디렉토리와 호환성 낮음.

잠깐..! PnP 와 Zero-install의 차이

PnP

  • 의존성 위지 정보를 Map 처럼 관리
  • node_modules 없이 코드에서 import 하면 매핑 테이블에서 경로 찾아서 연결
  • 해당 정보는 .pnp.cjs 파일에 저장.

⇒ 책방에 직접 가서 책 빌리는게 아니라 그냥 책 대여 어떤거 해야하는지만 보고 집에서 빌리는 느낌.

(1) 하나의 의존성만 설치되어 효율적, 속도향상, 용량적음.

하지만 .. 일부 툴들 (webpack, eslint) 과의 호환성 문제가 생길수 있음.

 

Zero-install

  • yarn install 없이 바로 코드 실행할수있게 !
  • 의존성 설치하고 그리고 그 결과물도 모두 저장 그리고 Git에 커밋
  • 즉 협업자는 git clone만 해도 바로 실행 가능

npm, yarn도 사용 가능하지만 용량 부담이 큼..

but 레포 사이즈가 커지고 git 관리가 어려워 진다는 단점이 있음.

 

2. npm 정리 

  • Node.js의 기본 패키지 매니저, 최초의 패키지 매니저
  • 폴더 구조
    • node_modules에 의존성(패키지) 들이 폴더 트리 구조로 저장되어 있음.
    • 같은 패키지가 중복 설치될수 있음.?
  • 속도
    • 구조상 속도가 많이 느림.
  • 장점
    • node.js 공식, 모든 환경에서 기본 지원
    • 별다른 설정 없이 사용 가능
  • 단점
    • node_modules 폴더가 무거워지고 중복 저장이 많음.
    • 대규모 프로젝트에서 설치/ 삭제/ 업데이트 속도가 느려질 수 있음.

3. yarn 정리

  • Facebook이 개발, npm의 속도 & 안정성 문제를 해결하기 위해 만들어짐.
  • 폴더 구조
    • npm과 동일하게 node_modules 사용
    • yarn berry(2.x)부터 PnP(Plug'n'Play) 방식 지원
  • 속도
    • npm 보다 빠른 설치 (병렬설치, 캐싱)
  • 장점
    • yarn.lock 파일로 정확한 버전 관리
    • workspace, PnP 등 대형 프로젝트 지원.
  • 단점
    • 기본적으로 node_modules 구조는 npm과 비슷해서, 구조상의 한계가 있음.
    • Yarn berry(PnP)는 도입하려면 세팅을 많이 해야하고, 호환성 이슈가 많음 (특히 구 라이브러리).

4. pnpm 정리

  • node_modules의 비효율(용량, 속도, 중복) 해결을 위해 탄생.
  • 폴더 구조
    • Hard link 방식(→ 같은 패키지 여러 프로젝트에서 써도 디스크 하나만 차지)
    • 패키지는 한번만 저장, 각 프로젝트는 "링크(바로가기)"만 만듦.
  • 속도
    • 빠르다는게 장점
    • 디스크 적게 차지

 

Reference

https://toss.tech/article/lightning-talks-package-manager

 

패키지 매니저의 과거, 토스의 선택, 그리고 미래

토스는 왜 패키지 매니저로 Yarn을 선택했을까요? 이번 라이트닝 토크에서는 JavaScript의 패키지 매니저, 동작 방식, 그리고 토스의 선택과 앞으로의 방향성에 대해 이야기해 보려고 해요.

toss.tech