pnpm으로 모노레포 관리하기
pnpm으로 모노레포 관리하기
여러 패키지를 한 저장소에서 함께 관리하는 모노레포(monorepo) 는 프론트엔드와 백엔드, 공용 라이브러리를 한 흐름으로 다룰 수 있어 팀 생산성을 크게 높여준다. 이 글에서는 pnpm 의 워크스페이스 기능을 활용해 모노레포를 깔끔하게 구성하는 방법을, 실제 프로젝트에 적용하며 정리한 순서대로 소개한다.
모노레포란 무엇인가
모노레포는 여러 프로젝트(패키지)를 하나의 Git 저장소에 두고 관리하는 방식이다. 멀티레포와 달리 코드 공유·일괄 리팩터링·원자적 커밋이 쉽고, 버전 불일치로 인한 "내 로컬에선 되는데" 문제를 줄여준다.
- 코드 공유: 공용 타입·유틸을 내부 패키지로 즉시 재사용
- 원자적 변경: API와 클라이언트를 한 커밋에서 함께 수정
- 일관된 도구: 린트·포맷·테스트 설정을 루트에서 통일
왜 pnpm인가
npm·yarn 도 워크스페이스를 지원하지만, pnpm 은 디스크 효율 과 엄격한 의존성 격리 에서 확실한 강점을 가진다.
디스크 효율
pnpm 은 전역 콘텐츠 주소 저장소(content-addressable store)에 패키지를 한 번만 저장하고, 각 프로젝트의 node_modules 에는 하드링크만 만든다. 그래서 같은 의존성을 수십 개 패키지가 써도 디스크에는 한 벌만 존재한다.
엄격한 의존성 격리
pnpm 의 node_modules 는 평탄화(flatten)되지 않는다. 즉, package.json 에 선언하지 않은 패키지는 import 할 수 없다. 이 "유령 의존성(phantom dependency)" 차단 덕분에 배포 후에야 터지는 누락 의존성 버그를 개발 단계에서 잡을 수 있다.
워크스페이스 설정
모노레포의 뼈대는 워크스페이스 정의 파일과 폴더 구조다.
pnpm-workspace.yaml
저장소 루트에 아래 파일을 두면 pnpm 이 각 폴더를 하나의 패키지로 인식한다.
packages:
- "apps/*"
- "packages/*"
패키지 구조
애플리케이션은 apps/, 공용 라이브러리는 packages/ 로 나누는 관례가 널리 쓰인다.
.
├─ apps/
│ ├─ web/ # 프론트엔드 (Vite + React)
│ └─ api/ # 백엔드
├─ packages/
│ ├─ ui/ # 공용 컴포넌트
│ └─ config/ # 공용 ESLint·TS 설정
├─ package.json
└─ pnpm-workspace.yaml
자주 쓰는 명령어
특정 패키지에만 명령을 실행하려면 --filter 를 쓴다.
# web 앱에만 의존성 추가
pnpm --filter web add axios
# 모든 패키지의 test 스크립트 실행
pnpm -r test
# web 이 의존하는 패키지까지 함께 빌드
pnpm --filter web... build
의존성 관리 전략
모노레포가 커질수록 의존성 버전을 어떻게 맞추느냐가 유지보수 난이도를 좌우한다.
공통 의존성
TypeScript·ESLint 같은 개발 도구는 루트 package.json 에 한 번만 두고, -w(workspace root) 플래그로 설치해 버전을 통일한다.
pnpm add -Dw typescript eslint prettier
내부 패키지 참조
내부 패키지는 workspace:* 프로토콜로 참조하면, 배포 시 실제 버전으로 자동 치환된다.
{
"dependencies": {
"@myorg/ui": "workspace:*"
}
}
CI 파이프라인 최적화
모노레포 CI 의 핵심은 바뀐 패키지만 빌드·테스트 하는 것이다. pnpm 의 --filter "...[origin/main]" 구문은 특정 커밋 이후 변경된 패키지와 그 의존 관계만 골라낸다.
pnpm --filter "...[origin/main]" test
여기에 pnpm store 캐시를 CI 에 얹으면 설치 시간이 극적으로 줄어든다.
자주 겪는 문제와 해결
- 유령 의존성 오류: import 하는 패키지를 해당 package.json 에 명시적으로 추가한다(그게 원래 의도된 동작이다).
- peer dependency 경고:
.npmrc의auto-install-peers설정을 확인한다. - hoist 관련 도구 호환성: 일부 레거시 도구는
node-linker=hoisted로 우회할 수 있다.
마치며
pnpm 워크스페이스는 "빠르고, 디스크를 아끼며, 실수를 미리 막아주는" 모노레포 도구다. 작게 시작해 apps/ 와 packages/ 두 폴더만 나눠도 효과를 바로 체감할 수 있다. 이 글의 목차를 따라 위에서부터 하나씩 적용해 보자.