pnpm + Git Worktrees를 활용한 멀티 에이전트 개발

여러 AI 에이전트가 같은 모노레포에서 동시에 작업해야 하는 경우, 각 에이전트는 완전히 작동하는 node_modules가 있는 격리된 작업 사본이 필요합니다. Git worktree를 pnpm의 글로벌 가상 스토어와 함께 사용하면 이를 실용적으로 구현할 수 있습니다: 각 worktree는 자체 체크아웃과 자체 node_modules를 가지지만, 종속성은 디스크의 단일 콘텐츠 주소 지정 스토어를 통해 모든 worktree에서 공유됩니다.

Git worktree란 무엇인가요?#

일반적으로 git 저장소에는 한 번에 하나의 브랜치에 연결된 단일 작업 디렉터리가 있습니다. 다른 브랜치를 보려면 변경 사항을 stash하거나 커밋한 후 전환해야 합니다. git worktree를 사용하면 여러 브랜치를 각각 자체 디렉터리에 동시에 체크아웃할 수 있습니다. 모든 worktree는 같은 저장소 기록과 객체를 공유합니다 — 같은 저장소를 다르게 보는 뷰일 뿐입니다.

git worktree add ../feature-branch feat/my-feature

이렇게 하면 feat/my-feature가 체크아웃된 새 디렉터리 ../feature-branch가 생성되며, 원래 작업 디렉터리는 현재 브랜치에 그대로 유지됩니다. 두 디렉터리에서 독립적으로 작업할 수 있습니다.

일반적인 패턴은 bare 저장소(자체 작업 디렉터리가 없는 저장소)를 허브로 사용하고, 모든 작업 디렉터리를 worktree로 생성하는 것입니다:

git clone --bare https://github.com/your-org/your-repo.git your-repo
cd your-repo
git worktree add ./main main
git worktree add ./feature feat/something

왜 worktree인가요?#

AI 에이전트 이전에도 worktree는 프로젝트의 여러 메이저 버전을 유지 관리하는 데 유용합니다. 저는 개발 머신에서 최소 두 개의 worktree가 있는 pnpm 저장소를 사용합니다: 하나는 pnpm v11을 위한 main이고, 다른 하나는 백포트와 유지 보수 릴리스를 위한 v10 브랜치입니다. 이렇게 하면 진행 중인 v11 작업을 stash하지 않고도 v10의 버그를 수정할 수 있습니다 — 두 버전 모두 항상 체크아웃되어 바로 사용할 수 있습니다. 예전에는 pnpm 저장소에서 보통 2~3개의 worktree로 충분했습니다. 하지만 AI 에이전트를 본격적으로 사용하기 시작한 이후로는 에이전트가 여러 작업을 병렬로 수행할 수 있도록 훨씬 더 많은 worktree가 필요합니다.

AI 에이전트와 함께라면 더욱 중요한 이유#

AI 코딩 에이전트와 함께라면 worktree는 편리한 도구에서 필수 도구가 됩니다. 각 에이전트는 다른 에이전트를 방해하지 않고 파일을 편집하고, 빌드를 실행하고, 테스트를 수행할 자체 작업 디렉터리가 필요합니다. Worktree가 없으면 저장소를 여러 번 클론해야 하며, 사본마다 git 기록이 중복됩니다.

Worktree는 git 측면의 문제를 해결합니다 — 모든 에이전트는 기본 git 객체를 공유하면서 자체 격리된 체크아웃을 가집니다. 하지만 각 worktree에는 여전히 자체 node_modules가 필요하며, 이는 수백 메가바이트에 이를 수 있습니다. 바로 여기서 pnpm의 글로벌 가상 스토어가 필요합니다: 이를 활성화하면 각 worktree의 node_modules에는 디스크의 단일 콘텐츠 주소 지정 스토어를 가리키는 심볼릭 링크만 들어 있습니다. 즉, 새 에이전트를 추가하는 작업이 빠르고 추가 디스크 공간이 거의 들지 않습니다.

Important

이 설정은 worktree와 에이전트가 같은 신뢰 경계를 공유한다고 가정합니다. 서로 신뢰하지 않는 에이전트나 사용자에게 하나의 쓰기 가능한 pnpm 스토어를 사용하지 마세요.

설정하기#

1. Bare 저장소 만들기#

git clone --bare https://github.com/your-org/your-monorepo.git your-monorepo
cd your-monorepo

2. 각 브랜치에 대한 worktree 만들기#

# Main development worktree git worktree add ./main main # A feature branch for agent A git worktree add ./feature-auth feat/auth # A bugfix branch for agent B git worktree add ./fix-api fix/api-error

각 worktree는 자체 파일을 가진 완전한 체크아웃이지만, 모두 같은 .git 객체 스토어를 공유합니다.

3. 글로벌 가상 스토어 활성화하기#

저장소의 pnpm-workspace.yaml에 enableGlobalVirtualStore: true를 추가하세요:

packages:
  - 'packages/*'

enableGlobalVirtualStore: true

4. 각 worktree에 종속성 설치하기#

cd main && pnpm install
cd ../feature-auth && pnpm install
cd ../fix-api && pnpm install

첫 번째 pnpm install은 패키지를 글로벌 스토어에 다운로드합니다. 다른 worktree에서의 이후 설치는 같은 스토어에 대한 심볼릭 링크만 생성하므로 거의 즉시 완료됩니다.

작동 방식#

글로벌 가상 스토어가 없으면 각 worktree는 node_modules 안에 모든 패키지의 하드 링크나 복사본이 있는 자체 .pnpm 가상 스토어를 가지게 됩니다. enableGlobalVirtualStore: true를 사용하면 pnpm은 모든 패키지 콘텐츠를 하나의 공유 디렉터리(글로벌 스토어로, pnpm store path를 실행하여 찾을 수 있습니다)에 보관하며, 각 worktree의 node_modules에는 그곳을 가리키는 심볼릭 링크가 들어 있습니다:

your-monorepo/                      (bare git repo)
├── main/                           (worktree: main branch)
│   ├── packages/
│   └── node_modules/
│       ├── lodash → <global-store>/links/@/lodash/...
│       └── express → <global-store>/links/@/express/...
├── feature-auth/                   (worktree: feat/auth branch)
│   └── node_modules/
│       ├── lodash → <global-store>/links/@/lodash/...  ← same target
│       └── express → <global-store>/links/@/express/...
└── fix-api/                        (worktree: fix/api-error branch)
    └── node_modules/
        ├── lodash → <global-store>/links/@/lodash/...  ← same target
        └── express → <global-store>/links/@/express/...

즉, 다음과 같습니다:

  • Worktree당 오버헤드가 거의 없음 — 로컬 node_modules에는 공유 글로벌 가상 스토어에 대한 심볼릭 링크만 들어 있습니다. 콘텐츠 주소 지정 스토어의 파일을 로컬 node_modules/.pnpm 디렉터리로 하드 링크하는 pnpm의 기본 동작과 달리, 글로벌 가상 스토어를 사용하면 worktree에 파일이 전혀 복사되거나 하드 링크되지 않습니다.
  • 새 worktree의 즉시 설치 — 패키지가 이미 글로벌 스토어에 있습니다.
  • 충돌 없음 — 각 worktree는 자체 node_modules 트리를 가지므로, 에이전트는 서로 방해하지 않고 서로 다른 브랜치에서 서로 다른 종속성 버전을 설치할 수 있습니다.

예시: pnpm 모노레포 자체#

pnpm 저장소는 bare git 저장소와 enableGlobalVirtualStore: true를 사용하여 정확히 이 설정을 사용합니다. Worktree 관리를 더 쉽게 해 주는 헬퍼 스크립트가 포함되어 있습니다:

pnpm worktree:new <branch-name|pr-number> — 새 worktree를 생성하고 설정합니다:

# Create a worktree for a branch (creates it from main if it doesn't exist)
pnpm worktree:new feat/my-feature

# Create a worktree for a GitHub PR (fetches the PR ref automatically)
pnpm worktree:new 10834

이 스크립트는 단순한 git worktree add 외에도 몇 가지를 처리합니다:

  • PR 번호는 git fetch origin pull/<number>/head를 통해 가져오므로 포크에서도 작동합니다.
  • 슬래시가 포함된 브랜치 이름(예: feat/my-feature)은 디렉터리 이름에서 대시로 변환됩니다(예: feat-my-feature).
  • .claude 디렉터리는 bare 저장소의 git 공통 디렉터리에서 새 worktree로 심볼릭 링크되므로, 모든 worktree가 같은 Claude Code 설정과 승인된 명령을 공유합니다.

스크립트를 감싸고 새 worktree로 cd하는 셸 헬퍼 shell/wt.sh도 있습니다:

# Source it in your shell config, then:
wt feat/my-feature
wt 10834

팁#

  • 에이전트용 worktree 만들기: AI 에이전트를 실행할 때 전용 worktree를 만드세요. 에이전트는 다른 에이전트에 영향을 주지 않고 파일을 편집하고, 테스트를 실행하고, 패키지를 설치할 수 있는 완전한 격리 환경을 갖게 됩니다.
  • 정리: 더 이상 필요하지 않은 worktree는 git worktree remove ./feature-auth로 제거하세요. 남은 worktree는 비용이 적지만 쌓일 수 있습니다.