테마
프론트엔드 프로젝트 구조와 패키지 매니저의 필요성
브라우저가 이해하는 정적 파일을 만들기 위해 Node.js 기반 프로젝트를 구성하고, 패키지 매니저로 의존성을 관리하는 전체 흐름을 이해한다.
학습 목표
- 프론트엔드 프로젝트의 기본 구조(
package.json,node_modules,src)를 이해한다. - 패키지 매니저가 왜 필요한지, 어떤 문제를 해결하는지 파악한다.
- npm, yarn, pnpm의 등장 배경과 각각의 핵심 차별점을 시간순으로 정리한다.
node_modules구조의 변천과 의존성 해결(resolution) 방식을 이해한다.
1. 프론트엔드 프로젝트의 기본 구조
웹 프론트엔드 프로젝트의 최종 산출물은 HTML, CSS, JavaScript 같은 정적 파일이다. 브라우저가 이해할 수 있는 형태여야 하므로, 개발자가 작성한 소스 코드를 트랜스파일, 번들링, 최적화를 거쳐 변환해야 한다.
이 변환 과정을 위해 Node.js를 런타임 환경으로 사용하며, 프로젝트는 다음과 같은 구조를 갖는다.
my-project/
package.json # 프로젝트 메타 정보 + 의존성 선언
package-lock.json # 의존성 잠금 파일 (npm)
node_modules/ # 설치된 패키지들
src/ # 개발자가 작성하는 소스 코드
index.js
App.jsx
styles/
public/ # 정적 에셋 (HTML 템플릿, 이미지 등)
dist/ # 빌드 결과물 (정적 파일)package.json의 핵심 필드
| 필드 | 역할 |
|---|---|
name | 패키지 이름 (npm 레지스트리에서 고유 식별자) |
version | 시맨틱 버전 (major.minor.patch) |
main | require() 시 진입점 파일 |
scripts | 실행 가능한 명령어 정의 (start, build, test 등) |
dependencies | 프로덕션에 필요한 의존성 |
devDependencies | 개발 시에만 필요한 의존성 |
node_modules 폴더
npm install을 실행하면 package.json에 선언된 의존성이 node_modules 폴더에 다운로드된다. Node.js의 require() 함수는 다음 순서로 모듈을 탐색한다.
- 상대/절대 경로(
./,../,/)로 시작하면 해당 파일 경로에서 탐색 - 그 외에는 현재 디렉터리의
node_modules에서 탐색 - 없으면 **상위 디렉터리의
node_modules**로 이동하며 루트까지 반복
이 탐색 알고리즘이 워크스페이스에서 호이스팅과 팬텀 디펜던시 문제의 근본 원인이 된다.
2. 패키지 매니저의 역할
패키지 매니저는 다음 문제를 해결한다.
- 설치: 외부 라이브러리를 npm 레지스트리에서 다운로드하여 프로젝트에 배치
- 버전 관리: 시맨틱 버전 범위에 따라 호환 가능한 버전 선택
- 의존성 기록:
package.json과 lock 파일에 어떤 패키지가 필요한지 선언적으로 기록 - 재현성: 다른 개발자나 CI 환경에서 동일한 의존성 트리를 재구성
- 스크립트 실행:
npm run build같은 명령으로 개발 도구 실행을 표준화
3. 패키지 매니저의 발전 과정
3.1 npm (2010~)
Node.js와 함께 기본 설치되는 최초의 패키지 매니저이다.
- npm v2: 중첩(nested)
node_modules구조 사용. 의존성의 의존성이 하위 폴더에 반복 설치되어 디렉터리 깊이가 극단적으로 깊어지는 문제 발생 (Windows에서 경로 길이 제한 초과) - npm v3: 평탄(flat)
node_modules구조로 전환. 모든 의존성을 가능한 한 최상위에 배치하여 중복 설치를 줄임 - npm v5:
package-lock.json도입으로 재현 가능한 설치 지원 (yarn의 lock 파일 개념을 수용) - npm v7+: 워크스페이스 기능 추가
3.2 Yarn Classic (2016~)
Facebook, Google 등이 npm의 한계를 해결하기 위해 만든 패키지 매니저이다.
- yarn.lock 파일로 일관된 의존성 재설치 보장
- 병렬 설치로 설치 속도 대폭 향상
- 오프라인 캐시 지원
- 워크스페이스 기능 최초 도입 (모노레포 지원)
- 단, 평탄한
node_modules구조는 npm과 동일하여 Phantom Dependency 문제가 그대로 존재
3.3 pnpm (2017~)
기존 패키지 매니저에 불만을 가진 개발자가 만든 패키지 매니저이다.
- 하드링크 + 심볼릭 링크 전략으로 디스크 공간 절약과 설치 속도 향상
- 비-평탄(non-flat) node_modules 구조로 Phantom Dependency 문제 해결
- 콘텐츠 주소 기반 스토어: 전역 스토어에 패키지를 한 번만 저장하고 하드링크로 연결
- 워크스페이스 지원 (
pnpm-workspace.yaml)
3.4 Yarn Berry (2020~)
Yarn 1.x와 완전히 다른 새로운 아키텍처이다.
- Plug'n'Play (PnP):
node_modules폴더를 아예 사용하지 않고 압축 파일(.zip)로 의존성 관리 - 설치 시간 최소화, Zero-Install 개념 도입
- 엄격한 의존성 해석으로 Phantom Dependency 문제 해결
4. node_modules 구조와 의존성 해결 방식
4.1 npm v2 시절: 중첩(Nested) 구조
node_modules/
A/
node_modules/
B/
node_modules/
C/- 각 패키지가 자신의 의존성을 하위
node_modules에 독립적으로 설치 - 장점: 의존성 격리가 완벽
- 단점: 디렉터리 깊이 폭발, 같은 패키지의 중복 설치, Windows 경로 제한 문제
4.2 npm v3+ / Yarn Classic: 평탄(Flat) 구조
node_modules/
A/
B/
C/- 모든 의존성을 가능한 한 최상위
node_modules에 배치 (호이스팅) - 버전 충돌이 있는 경우에만 하위
node_modules에 중첩 설치 - 장점: 디스크 절약, 깊이 문제 해결
- 단점: Phantom Dependency 발생
Phantom Dependency란?
package.json에 직접 선언하지 않은 패키지를 require()로 불러올 수 있는 현상이다. 예를 들어 A만 설치했는데, A가 내부적으로 사용하는 B가 최상위 node_modules에 호이스팅되어 프로젝트에서 직접 require('B')가 가능해진다.
이는 A가 업데이트되면서 B를 더 이상 사용하지 않게 되면 갑자기 코드가 깨지는 위험이 있다.
4.3 pnpm: 비-평탄(Non-flat) 구조 + 심볼릭 링크
node_modules/
.pnpm/ # 가상 스토어 (Virtual Store)
A@1.0.0/
node_modules/
A/ # 실제 파일 (하드링크)
B -> ../../B@2.0.0/node_modules/B # 심볼릭 링크
B@2.0.0/
node_modules/
B/ # 실제 파일 (하드링크)
A -> .pnpm/A@1.0.0/node_modules/A # 심볼릭 링크- 프로젝트의
node_modules최상위에는 직접 의존성만 심볼릭 링크로 노출 .pnpm/가상 스토어 내부에서 의존성 간의 관계를 심볼릭 링크로 구성- 실제 파일은 글로벌 스토어에서 하드링크로 연결하여 디스크 절약
- Phantom Dependency 원천 차단
4.4 Yarn Berry: PnP (Plug'n'Play)
.pnp.cjs # 의존성 맵 (패키지 이름 -> zip 경로)
.yarn/cache/ # 압축된 패키지 파일들
axios-npm-1.6.0-abc123.zip
lodash-npm-4.17.21-def456.zipnode_modules폴더 자체가 없음.pnp.cjs파일이 의존성 해석 테이블 역할- 패키지는
.yarn/cache/에 zip 파일로 보관 - Zero-Install:
.pnp.cjs와 cache를 git에 커밋하면yarn install없이 바로 실행 가능
5. 패키지 매니저 비교 요약
| 비교 항목 | npm | Yarn Classic | pnpm | Yarn Berry |
|---|---|---|---|---|
| node_modules 구조 | 평탄 | 평탄 | 비-평탄 (심볼릭 링크) | 없음 (PnP) |
| Lock 파일 | package-lock.json | yarn.lock | pnpm-lock.yaml | yarn.lock |
| 디스크 효율 | 보통 | 보통 | 높음 (하드링크) | 높음 (zip) |
| 설치 속도 | 보통 | 빠름 | 매우 빠름 | 매우 빠름 |
| Phantom Dependency | 발생 | 발생 | 방지 | 방지 |
| 워크스페이스 | v7+ | v1.0+ | 지원 | 지원 |
| 특징 | 기본 내장 | 병렬 설치 | 콘텐츠 주소 스토어 | Zero-Install |
핵심 정리
- 프론트엔드 프로젝트는 Node.js를 런타임으로 사용하여 소스 코드를 브라우저가 이해하는 정적 파일로 변환한다.
- 패키지 매니저는 외부 라이브러리의 설치, 버전 관리, 의존성 기록, 재현 가능한 빌드를 담당하는 필수 도구이다.
- npm(2010) -> Yarn Classic(2016) -> pnpm(2017) -> Yarn Berry(2020) 순으로 발전하며, 각각 이전 도구의 한계를 개선했다.
node_modules의 평탄 구조는 Phantom Dependency 문제를 야기하며, pnpm의 비-평탄 구조와 Yarn Berry의 PnP가 이를 해결한다.- 모노레포 환경에서는 워크스페이스 기능이 필수이며, 모든 주요 패키지 매니저가 이를 지원한다.
다음 단계
- 02. npm 워크스페이스: npm의 워크스페이스 기능을 활용하여 모노레포를 구성하고, 호이스팅과 의존성 해석의 실제 동작을 확인한다.