
Git Worktrees를 활용한 병렬 AI 코딩: 충돌 없이 여러 에이전트 실행하기
요약
여러 AI 에이전트가 동일한 작업 디렉토리를 공유할 때 발생하는 파일 충돌 및 레이스 컨디션 문제를 Git Worktrees로 해결하는 방법을 제시합니다. Git Worktrees를 통해 저장 공간을 절약하면서도 각 에이전트에게 독립된 환경을 제공하여 병렬 코딩 효율을 극대화할 수 있습니다.
핵심 포인트
- 동일 디렉토리 공유 시 파일 쓰기 충돌 및 락 파일 손상 발생
- Git Worktrees를 통해 단일 저장소 기반의 독립적 작업 디렉토리 확보
- 전체 클론 방식 대비 디스크 공간 및 클론 시간 오버헤드 절감
- AI 에이전트 간의 간섭 없는 병렬 개발 워크플로우 구축 가능
Git Worktrees를 활용한 병렬 AI 코딩: 충돌 없이 여러 에이전트 실행하기
대부분의 병렬 AI 개발 문제는 단 하나의 구조적 실수에서 비롯됩니다. 바로 여러 에이전트가 동일한 작업 디렉토리 (working directory)를 공유한다는 점입니다. 팀들이 세 개의 Claude Code 인스턴스를 실행하고 이를 동일한 프로젝트 폴더로 지정하면, 파일 쓰기 충돌이 발생하고, 브랜치 체크아웃 (branch checkout)이 서로를 방해하며, 락 파일 (lock files)이 손상되는 것을 목격하게 됩니다. 이러한 증상은 레이스 컨디션 (race condition)처럼 보이지만, 근본적인 원인은 파일 시스템 설계에 있습니다.
Git worktrees는 단일 .git 저장소를 공유하면서 각 에이전트에게 독립된 작업 디렉토리를 제공함으로써 이 문제를 해결합니다. 이러한 구분은 매우 중요합니다. 개발자는 전체 클론 (full clones)에 따른 저장 공간 오버헤드 없이 병렬 실행을 할 수 있으며, 에이전트들은 서로의 파일 핸들 (file handles)을 침범하지 않고 별도의 브랜치에서 작동합니다. 이 패턴은 Git 2.5부터 존재해 왔지만, AI 코딩 워크플로우가 등장하면서 마침내 필수적인 인프라가 되었습니다.
충돌 문제: 왜 여러 AI 에이전트가 작업 디렉토리를 공유할 수 없는가
다른 프로세스가 파일을 읽고 있는 디렉토리에서 git checkout feature-A를 실행하면, 파일 시스템 상태가 해당 읽기 프로세스 하단에서 변경됩니다. 다른 프로세스는 원자적 전환 (atomic transitions)을 보는 것이 아니라, 부분적인 쓰기, 누락된 파일, 그리고 일관성 없는 의존성 그래프 (dependency graphs)를 보게 됩니다. TypeScript 컴파일러는 "Cannot find module" 오류와 함께 실패합니다. 개발 서버 (Dev servers)는 읽기 도중 감시 중인 파일이 사라져서 충돌합니다. 패키지 매니저의 락 파일은 두 에이전트가 서로 다른 의존성 트리를 가진 서로 다른 브랜치에서 동시에 npm install을 실행할 때 손상됩니다.
명백한 해결책은 에이전트 실행을 시차를 두어 한 번에 하나만 실행되도록 하는 것이지만, 이는 병렬 개발의 목적을 저해합니다. 이 패턴을 시도하는 팀들은 결국 AI 에이전트들이 대기열에서 기다리며, 각 에이전트가 완료될 때까지 다음 에이전트를 차단하는 상황에 직면하게 됩니다. 병목 현상은 인간의 타이핑 속도에서 직렬 실행 (serial execution)으로 옮겨가며, 생산성 향상 효과는 사라지게 됩니다.
전체 저장소 클론 (Full repository clones) 방식도 작동은 하지만 디스크 공간을 낭비하게 됩니다. 2GB 크기의 모노레포 (monorepo)를 5개의 에이전트를 위해 5번 클론하면 10GB의 중복된 Git 객체 (Git objects)를 소비하게 됩니다. 희소 체크아웃 (Sparse checkouts)은 작업 디렉토리 (working directory) 크기를 줄여주지만 중복 문제를 완전히 제거하지는 못합니다. 여기서 발생하는 실패 모드는 미묘하지만 비용이 많이 듭니다. 팀들은 CI 러너 (CI runners)의 저장소 용량 제한에 부딪히고, 로컬 SSD는 가득 차며, 클론 시간 (clone times)이 에이전트 시작 지연 시간 (startup latency)의 대부분을 차지하게 됩니다.
Git Worktrees란 무엇이며 어떻게 에이전트 격리 문제를 해결하는가
워크트리 (worktree)는 공유된 .git 폴더에 연결된 작업 디렉토리 (working directory)입니다. git worktree add ../feature-A feature-A를 실행하면 Git은 feature-A 브랜치가 체크아웃된 ../feature-A 디렉토리를 새로 생성합니다. 기존의 작업 디렉토리와 새로운 워크트리는 모두 동일한 객체 데이터베이스 (object database), 참조 (refs), 그리고 히스토리 (history)를 공유합니다. 디스크 사용량은 전체 저장소가 아닌, 체크아웃된 파일의 크기만큼만 증가합니다.
격리된 작업 디렉토리를 유지하면서 단일 저장소를 공유하는 Git worktrees
각 워크트리는 자신만의 HEAD, 인덱스 (index), 그리고 작업 디렉토리 상태를 유지합니다. 한 워크트리에서의 변경 사항은 다른 워크트리에 영향을 주지 않습니다. feature-A/에서 작업하는 에이전트는 src/api.ts를 수정하고, 테스트를 실행하고, 커밋할 수 있으며, 이 모든 과정은 feature-B/에서 다른 에이전트가 다른 브랜치에서 동일한 파일을 편집하는 동안 동시에 이루어질 수 있습니다. 파일 시스템 수준의 격리 (filesystem-level isolation)는 공유 디렉토리 워크플로를 망가뜨리는 충돌 패턴을 방지합니다.
여기서의 시사점은 워크트리가 조정 오버헤드 (coordination overhead) 없이 진정한 병렬 실행 (parallel execution)을 가능하게 한다는 것입니다. 에이전트들은 충돌을 피하기 위해 세마포어 (semaphores), 파일 잠금 (file locks), 또는 메시지 큐 (message queues)를 사용할 필요가 없습니다. 운영 체제의 디렉토리 구조가 격리 경계 (isolation boundary)를 제공하기 때문입니다. 이는 매우 중요한데, 조정 로직 (coordination logic)은 AI 에이전트들이 공유 자원을 두고 경쟁할 때 가장 먼저 실수하는 부분이기 때문입니다.
워크트리를 사용하여 첫 번째 병렬 에이전트 워크플로 설정하기
Git worktrees를 사용하여 격리된 개발 환경을 관리하는 병렬 에이전트 워크플로 (Parallel agent workflow with Git worktrees managing isolated development environments)
~/projects/app에 있는 기본 저장소(base repository)에서 시작하세요. 각 에이전트 작업에 대해 워크트리(worktrees)를 생성합니다:
import { execSync } from 'child_process';
import { mkdirSync, existsSync } from 'fs';
import { join } from 'path';
...
각 워크트리는 자신만의 node_modules, .env.local, 그리고 빌드 결과물(build artifacts)을 갖게 됩니다. 에이전트 1이 의존성(dependency)을 업데이트하기 위해 npm install을 실행하더라도, 에이전트 2의 워크트리에 있는 파일 워처(file watchers)를 트리거하지 않습니다. tsc나 vite와 같은 빌드 도구들은 별도의 출력 디렉토리에 결과물을 작성합니다. 이로써 파일 시스템 수준에서의 격리가 완벽하게 이루어집니다.
여기서 중요한 세부 사항은 각 워크트리마다 npm install을 실행해야 한다는 점입니다. node_modules를 심볼릭 링크(Symlinking)로 연결하면 격리가 깨집니다. 여러 에이전트가 결국 동일한 의존성 트리(dependency tree)를 공유하게 되어 버전 충돌(version conflicts)이 다시 발생하게 됩니다. 디스크 공간은 저렴하지만, 경쟁 상태(Race conditions)는 비용이 많이 듭니다.
관련된 설정 패턴은 AI 코딩 도구를 위한 의존성 격리를 자세히 다루는 Claude Code plugin packaging guide에서 확인할 수 있습니다.
파일 충돌을 넘어: 데이터베이스 브랜칭 및 포트 격리
파일 수준의 격리는 문제의 절반만을 해결합니다. 나머지 절반은 런타임 리소스(runtime resources), 즉 데이터베이스 연결(database connections), 개발 서버 포트(dev server ports), 그리고 백그라운드 서비스(background services)입니다. 한 워크트리에서 npm run dev를 실행하는 에이전트는 기본적으로 localhost:3000을 사용합니다. 만약 다른 에이전트가 다른 워크트리에서 개발 서버를 시작하면, 포트 충돌(port collision)로 인해 두 프로세스 모두 충돌(crash)하게 됩니다.
해결책은 환경별 설정(environment-specific configuration)입니다. 각 워크트리(worktree)는 고유한 포트 할당 및 데이터베이스 URL이 포함된 개별 .env.local 파일을 갖게 됩니다.
import { writeFileSync } from 'fs';
import { join } from 'path';
...
Neon의 브랜칭(branching) 기능이나 PlanetScale의 배포 요청(deploy requests)과 같은 데이터베이스 브랜칭 도구들은 이 패턴을 프로덕션과 유사한 데이터베이스로 확장합니다. 각 에이전트는 프로덕션 데이터가 포함된 Copy-on-write 데이터베이스 브랜치를 할당받아 독립적으로 마이그레이션(migration)을 실행하고, 스키마(schema) 변경 사항을 메인(main) 브랜치로 다시 병합(merge)합니다. 저장 공간 오버헤드는 최소화됩니다. 변경된 행(row)만 공간을 소비하기 때문입니다.
이 접근 방식은 백그라운드 워커(background workers), Redis 인스턴스, 메시지 큐(message queues)로 확장 가능합니다. 핵심은 결정론적인 리소스 명명(deterministic resource naming)입니다: worker-${port}, queue-agent-${id}, cache-key-prefix-${branch}. 리소스 식별자에 격리 경계(isolation boundary)를 포함하면 충돌(collision)이 발생하는 것은 불가능해집니다.
여러 워크트리 관리하기: 라이프사이클 패턴 및 정리
워크트리는 계속 쌓입니다. 3주간의 병렬 개발을 거치고 나면, 개발자들은 디스크 공간을 차지하고 git worktree list 출력 결과를 어지럽히는 20개의 오래된 워크트리를 갖게 됩니다. 정리 패턴은 간단하지만 규율이 필요합니다:
import { execSync } from 'child_process';
import { rmSync } from 'fs';
...
메인(main)으로의 모든 병합(merge) 후에 이 작업을 실행하십시오. 이 패턴은 디렉토리는 존재하지만 원격(remote)에서 브랜치가 삭제된 상태인 "좀비 워크트리(zombie worktree)" 문제를 방지합니다. Git의 worktree prune 명령은 메타데이터를 정리하지만 디렉토리를 제거하지는 않습니다. 따라서 팀은 명시적인 파일 시스템 정리가 필요합니다.
장기간 실행되는 워크트리의 경우, 주기적인 리베이스(rebase)를 통해 최신 상태를 유지할 수 있습니다:
cd ../agent-api
git fetch origin
git rebase origin/main
...
여기서 시사하는 바는 워크트리가 한 번 실행하고 잊어버리는(fire-and-forget) 대상이 아니라는 점입니다. 워크트리는 수명이 긴 피처 브랜치(feature branches)와 동등한 라이프사이클 관리(lifecycle management)를 필요로 합니다. 워크트리를 일시적인(ephemeral) 것으로 취급하는 팀은 종종 병합 충돌(merge conflicts)과 오래된 의존성(outdated dependencies) 문제에 직면하게 됩니다.
여러 병렬 개발 스트림 전반에 걸친 워크트리(worktree) 생명주기 관리 및 정리
에이전트 격리를 위한 Worktrees vs Full Clones vs Docker Containers
병렬 AI 에이전트를 격리하는 세 가지 패턴이 존재합니다. 각 패턴은 규모가 커질 때 고려해야 할 구체적인 트레이드오프(tradeoffs)를 가지고 있습니다.
병렬 AI 에이전트 개발을 위한 격리 전략 비교
Full Clones(전체 복제)는 최대의 격리를 제공하지만, 디스크 공간을 3~5배 더 많이 소비합니다. 2GB 크기의 저장소(repository)를 5번 복제하면 10GB를 사용하게 됩니다. Fetch(가져오기) 작업은 각 복제본에 대해 독립적으로 원격 저장소에서 데이터를 가져옵니다. 장점은 단순함입니다. 공유 상태(shared state)가 없으므로 조정 로직(coordination logic)이 필요하지 않습니다. 단점은 에이전트가 업스트림(upstream)과 동기화해야 할 때 발생하는 I/O 오버헤드입니다. 디스크 공간이 풍부하고 Fetch 지연 시간(latency)이 중요하지 않은 경우에 Full Clones를 사용하십시오.
Docker Containers(Docker 컨테이너)는 런타임(runtime) 수준에서 격리합니다. 저장소를 볼륨(volume)으로 마운트하고, 에이전트당 하나의 컨테이너를 실행하며, Docker의 파일 시스템 레이어링(filesystem layering)을 활용합니다. 컨테이너는 프로세스 격리(process isolation), 네트워크 네임스페이스(network namespaces), 그리고 리소스 제한(resource limits)을 제공받습니다. 단점은 오케스트레이션(orchestration)의 복잡성입니다. 팀은 Docker Compose 설정, 상태 확인(health checks), 로그 집계(log aggregation)가 필요합니다. 스토리지 오버헤드는 Worktrees와 Full Clones의 중간 수준입니다. 공유 베이스 이미지(base images)를 통해 중복을 줄이지만, 각 컨테이너는 자체적인 쓰기 가능 레이어(writable layer)를 유지합니다.
Worktrees는 디스크 효율성과 Git 작업 속도를 최적화합니다. 한 번만 Fetch(가져오기)하면 모든 worktree가 새로운 refs(참조)를 볼 수 있습니다. 디스크 사용량은 저장소(repository) 크기가 아니라 체크아웃(checked-out)된 파일 수에 따라 선형적으로 증가합니다. 트레이드오프(tradeoff)는 Git 수준의 결합(coupling)입니다. 동일한 브랜치를 여러 worktree에서 동시에 체크아웃할 수 없으며, worktree를 삭제할 때는 공유된 .git 폴더와의 조정이 필요합니다.
의사 결정 매트릭스(decision matrix): 3~10개의 에이전트를 사용하는 로컬 개발에는 worktrees를, 엄격한 격리가 필요한 CI/CD 파이프라인에는 컨테이너(containers)를, 조정 복잡성보다 저장 비용이 더 저렴할 때는 전체 클론(full clones)을 사용합니다. 대부분의 프로덕션(production) 시스템은 하이브리드 방식을 사용합니다. 즉, 개발자에게는 worktrees를, 클라우드 환경에서 실행되는 에이전트에게는 컨테이너를 제공합니다.
AI 에이전트를 위한 컨텍스트 윈도우(context window) 제한에 대한 내용은 2 million token context windows for real web apps에서 확인할 수 있습니다.
프로덕션 패턴: 3개의 에이전트에서 N-Way 병렬 실행까지
3개의 worktree에서 N개로 확장하려면 자동화와 오케스트레이션(orchestration)이 필요합니다. 수동 설정 스크립트는 N이 10을 초과하면 작동하지 않습니다. 팀에는 동적 worktree 할당, 상태 모니터링(health monitoring), 그리고 장애 복구(failure recovery) 기능이 필요합니다.
N-Way 병렬 에이전트 실행을 위한 동적 worktree 할당 및 오케스트레이션
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기



