카테고리 없음

pnpm의 symlink, hardlink, hoisting 알아보기

jrpark91 2025. 4. 11. 16:14

공식문서 링크: https://pnpm.io/ko/symlinked-node-modules-structure

1. npm과 pnpm 차이 비유

npm은 프로젝트마다 필요한 짐(패키지)을 전부 새로 복사해서 트럭에 실어줌.
→ 똑같은 짐도 프로젝트마다 다 따로 실어야 하니까 공간 많이 차지하고, 시간 오래 걸림.

pnpm은 모든 짐을 공용 창고에 한 번만 넣어두고, 각 프로젝트에는 링크만 걸어줌 (하드 링크라고 함).
→ 똑같은 짐을 여러 번 옮길 필요가 없어서 공간 절약되고 빠름.

예시 상황

project라는 앱이 있고, 여기에 foo라는 라이브러리를 설치했는데
foo는 내부적으로 bar라는 라이브러리를 사용

pnmp install foo@1.0.0 생기는 일 (foo는 bar를 의존성으로 가짐)

1. 실제 라이브러리 코드가 있는 파일이 하드링크를 활용하여 생성됩니다.

node_modules
└── .pnpm
    ├── bar@1.0.0
    │   └── node_modules
    │       └── bar -> <store>/bar
    │           ├── index.js
    │           └── package.json
    └── foo@1.0.0
        └── node_modules
            └── foo -> <store>/foo
                ├── index.js
                └── package.json

2. 노드 modules에서 싱링크로 .pnpm내부를 바라봅니다.

node_modules/
├── foo → .pnpm/foo@1.0.0/node_modules/foo
└── .pnpm/
    ├── foo@1.0.0/
    │   └── node_modules/
    │       ├── foo → ../../../../store/foo
    │       └── bar → ../../bar@1.0.0/node_modules/bar
    └── bar@1.0.0/
        └── node_modules/
            └── bar → ../../../../store/bar
  • foo와 bar는 .pnpm 디렉토리 내에 실제 파일이 저장되며, 이는 하드 링크를 통해 전역 저장소에서 가져옵니다.
  • foo는 자신의 node_modules 내에 bar에 대한 심볼릭 링크를 생성하여 의존성을 관리합니다.
  • 최상위 node_modules에는 foo에 대한 심볼릭 링크만 존재하며, 이는 프로젝트의 직접적인 의존성을 나타냅니다.

 

2. 평탄화란?

npm의 평탄화 (hoisting)

node_modules/
├── foo/
├── bar/        ← 평탄화: 의존성 bar가 루트에 올라옴
 
  • 원래 bar는 foo의 의존성이지만, 루트에 끌어올림
  • => require('bar') 같은 코드가 루트에서도 동작하게 하려고

 pnpm은 평탄화 안 함 (기본값)

node_modules/
├── foo → .pnpm/foo@1.0.0/node_modules/foo

.pnpm/
└── foo@1.0.0/
    └── node_modules/
        ├── foo/
        └── bar/
  • bar는 foo 내부에서만 접근 가능
  • 루트에서 require('bar') 하면 못 찾음

 

3. 심링크와 하드링크

심링크

  • 파일의 "경로"를 가리키는 링크
  • 윈도우의 바로가기(lnk), macOS의 alias와 유사
  • 삭제 대상이 사라지면 깨진 링크가 됨 (dangling symlink)

하드링크

  • 파일의 실제 데이터(inode)를 가리키는 또 하나의 이름
  • 원본 파일과 동등한 파일처럼 동작
  • 원본 파일을 삭제해도 내용은 남아 있음 (다른 링크가 가리키고 있으면)
my-project/
├── node_modules/
│   ├── foo  → ⤴ symlink → .pnpm/foo@1.0.0/node_modules/foo
│   ├── .pnpm/
│   │   ├── foo@1.0.0/
│   │   │   └── node_modules/
│   │   │       ├── foo  → 하드 링크 → ~/.pnpm-store/foo
│   │   │       └── bar  → 하드 링크 → ~/.pnpm-store/bar
│   │   └── bar@1.0.0/
│   │       └── node_modules/
│   │           └── bar → 하드 링크 → ~/.pnpm-store/bar
│   └── .bin/
│       └── foo  → CLI 실행용 링크
├── package.json
├── pnpm-lock.yaml