
Git Worktree × Claude Code × Conductor를 이용한 병렬 개발 실전 메모
요약
Git Worktree와 Conductor를 활용하여 여러 개의 Claude Code 에이전트를 병렬로 실행하는 효율적인 개발 워크플로우를 소개합니다. 이를 통해 에이전트의 작업 대기 시간을 최소화하고, 독립적인 작업 디렉토리를 확보하여 개발 생산성을 극대화할 수 있습니다.
핵심 포인트
- Git Worktree를 통해 여러 브랜치를 독립된 디렉토리에서 동시에 실행 가능
- Claude Code 에이전트 간의 파일 편집 및 빌드 결과물 충돌 방지
- git-flow 전략을 기반으로 feature 및 hotfix 브랜치의 병렬 처리 최적화
- 에이전트의 추론 시간을 개발자의 유효 작업 시간으로 전환
서론
Claude Code로 개발할 때, "Claude에게 태스크를 맡긴다 → 출력을 기다린다 → 리뷰한다"를 직렬로 진행하면 대기 시간이 너무 지루하다.
그래서 git worktree와 Conductor를 조합하여, 여러 개의 Claude Code 에이전트를 동시에 병렬로 실행하는 워크플로우를 운용하고 있다. 개발부터 검증까지 병렬로 돌릴 수 있게 되면서, 혼자서 여러 태스크를 진행하는 감각이 상당히 달라졌다.
이 기사는 그 운용을 통해 얻은 지식의 실전 메모이다. git-flow를 전제로 하고 있다.
왜 병렬 개발인가
worktree를 활용하면 다음과 같이 된다.
- 혼자서 독립성이 높은
feature/*브랜치를 동시에 여러 개 실행할 수 있다 (예:feature/login-form과feature/refactor-api를 병행) develop에서의 작업을 중단하지 않고,hotfix/*나 급한 리뷰에 대응할 수 있다- "Claude가 생각하는 시간"이 다른 worktree에서의 자신의 작업 시간으로 변한다
전제: 브랜치 운용은 git-flow
이 기사는 git-flow를 전제로 한다. 주요 브랜치는 다음과 같다.
| 브랜치 | 역할 | 파생 원천 | 머지 대상 |
|---|---|---|---|
main | 운영 릴리스 버전 | - | - |
develop | 개발 통합 | main | main (release 경유) |
feature/* | 기능 개발 | develop | develop |
release/* | 릴리스 준비 | develop | main 및 develop |
hotfix/* | 운영 긴급 수정 | main | main 및 develop |
병렬 개발에서는 대부분의 worktree가 feature/* (develop에서 파생)가 된다. hotfix/*는 별도 계통으로, develop의 통상적인 작업을 멈추지 않고 main에서 파생시켜 병행하여 진행하는 방식이 특히 효과적이다.
git worktree란 (기초)
git worktree는 하나의 리포지토리(Repository)로부터 여러 개의 작업 디렉토리를 파생시킬 수 있는 Git의 표준 기능이다.
통상적으로 git switch feature-a로 브랜치를 전환하면 작업 디렉토리의 내용이 해당 브랜치의 상태로 통째로 교체된다. 동시에 두 개의 브랜치 내용을 다룰 수는 없다.
worktree를 사용하면 feature-a와 feature-b를 각각 별도의 디렉토리에 체크아웃(Checkout)할 수 있으며, 양쪽을 동시에 열어 편집, 빌드, 실행할 수 있다. .git 자체는 공유되므로 원격에서의 fetch나 커밋 이력은 하나로 통합된다.
왜 병렬 개발과 궁합이 좋은가
파일 편집의 혼선이 일어나지 않는다. Claude 에이전트를 2개 실행할 때, 같은 디렉토리에서 작업하게 하면 동일한 파일을 동시에 수정하게 된다. worktree라면 물리적으로 디렉토리가 다르기 때문에 양쪽 모두 안심하고 쓸 수 있다.
빌드 결과물이 망가지지 않는다. git switch로 오가다 보면 node_modules나 빌드 캐시가 한쪽 브랜치에 맞춰 업데이트되어 버린다. worktree라면 각 디렉토리가 독립된 결과물을 가질 수 있다.
"잠시 다른 브랜치를 보고 싶다"는 비용이 격감한다. stash 할 필요도, 커밋한 뒤 돌아올 필요도 없다. 옆 디렉토리로 이동하기만 하면 된다.
단순한 사용법
# 통상적으로는 이렇게 작성하지만, Conductor를 사용하면 직접 입력하는 일은 거의 없다
# feature 브랜치는 develop에서 파생
git worktree add -b feature/login-form ../repo-feature-login-form develop
...
전체상
┌────────────────────────────────────────────────────┐
│ Conductor가 worktree를 자동 생성 │
│ │
...
포인트는 "worktree마다 개발 서버의 포트와 Docker 컨테이너 (DB 등)를 분리하는 것"이다. 이를 통해 동일한 리포지토리의 다른 브랜치를,
겉보기에는 별개의 앱처럼 동시에 실행할 수 있어, 브라우저에서도 DB 클라이언트에서도 양쪽 모두 동시에 검증할 수 있다.
툴 스택 (Tool Stack)
| 역할 | 툴 |
|---|---|
| worktree 관리·라이프사이클 자동화 | Conductor (Scripts / Actions) |
| 에이전트 (Agent) | Claude Code |
| 환경 분리 | Docker / docker compose (DB·미들웨어) |
| 포트·컨테이너명 분리 | .env (worktree 별로 수동 설정) |
| 정형 커맨드 묶음 | Makefile (make up / make down 등) |
Conductor가 worktree의 라이프사이클(생성·실행·뒷정리)을 전담하여 맡아주기 때문에, git worktree add를 직접 입력하는 일은 거의 없다.
셋업 (Setup)
.env로 환경을 분리하기
worktree마다 .env를 생성하여 다음과 같은 방식으로 덮어쓴다.
- 개발 서버 포트 (예:
PORT=3001) - docker compose의 프로젝트명 (예:
COMPOSE_PROJECT_NAME=app-wt-a) - Docker에서 호스트로 publish하는 포트 (예:
DB_PORT=5433) - 앱 측에서 참조하는 DB 접속 정보 (예:
DATABASE_URL=postgresql://...:5433/...)
COMPOSE_PROJECT_NAME을 변경하면 컨테이너명·네트워크명·볼륨명이 모두 해당 프리픽스(Prefix)로 생성되므로, 다른 worktree의 DB와 혼동하는 사고가 일어나기 어려워진다.
번호 할당은 느슨하게 예약해 두면 편리하다.
- 앱: 3001, 3002, 3003, ...
- DB: 5433, 5434, 5435, ...
Makefile에 작업을 집약해 두기
셋업·기동·뒷정리는 Makefile에 모아두면, Conductor의 Scripts에서 호출할 때나 수동으로 입력할 때나 동일한 인터페이스가 되어 편리하다.
setup: ## 의존성 설치 + 컨테이너 생성
npm ci
docker compose build
...
Conductor Scripts로 라이프사이클 자동화
Scripts는 worktree의 라이프사이클 이벤트에 연결된 훅(Hook)이다. Makefile의 타겟(Target)을 호출하는 것만으로 worktree의 준비와 정리가 모두 자동화된다.
| 타이밍 | 할 일 | 내용 (예) |
|---|---|---|
| worktree 생성 시 | 컨테이너 생성·의존성 설치·개발 서버 기동 | make setup && make up |
| 아카이브(Archive) 시 | 컨테이너 삭제·개발 서버 정지 | make down |
이렇게 하면 .env만 직접 작성하면 되고, 나머지는 Conductor 측에서 알아서 컨테이너가 생성되거나 삭제되는 상태로 만들 수 있다. "아카이브 시 컨테이너 삭제를 잊어 고아 볼륨(Orphan Volume)이 쌓이는" 사고도 없어진다.
Conductor Actions로 커스텀 조작 심기
Actions는 worktree에 대해 원클릭으로 실행하는 수동 액션이다. 팀 특유의 운영 규칙을 Claude에게 매번 구두로 설명할 필요가 없도록 만들 수 있다.
실제로 심어둔 예:
- Review: 코드 리뷰 시, 독자적인 리뷰 관점(명명 규칙, 보안 체크 항목, UX 상의 확인 포인트 등)을 Claude가 추가로 의식하도록 하기 위한 프롬프트(Prompt)
- PR 생성: PR 생성 시, 리포지토리의 PR 템플릿(배경·변경점·확인 절차·스크린샷란 등)을 따르도록 하기 위한 프롬프트
이것들을 Actions로 등록해 두면, 누가 수행하더라도 동일한 관점에서 리뷰와 PR이 나오므로 품질이 균일해진다.
일상 오퍼레이션 (Daily Operation)
이 부분이 본 기사의 핵심이다.
병렬 개발의 마음가짐 (먼저 읽을 것)
worktree가 나열되어 있고 Claude가 여러 개 실행 중이라 하더라도, 어떤 하나의 태스크에 대해서는 "요건을 이해하고, Claude에게 정확하게 지시하며, 출력을 리뷰하는" 프로세스를 하나씩 진지하게 수행해야 한다. 병렬이라고 해서 대충 해도 되는 부분은 없다.
병렬로 진행하면 자신의 뇌 전환 비용 (Context Switching Cost) 은 확실히 올라간다. 태스크 A의 문맥을 머리에서 퇴피시키고, 태스크 B의 문맥을 다시 로드하는 과정을 몇 번이고 반복하기 때문에 직렬일 때보다 더 피로하다.
병렬로 진행할수록 리뷰의 책임은 무거워진다. Claude가 내놓는 차분(diff)의 양은 직렬일 때의 N배가 되므로, 자신이 책임지고 확인할 수 있는 범위를 넘지 않도록 주의할 것.
새로운 태스크를 시작할 때
- Conductor로 새로운 worktree를 생성
- 일반 개발이라면
develop에서feature/xxx를 파생 - 운영 긴급 수정이라면
main에서hotfix/xxx를 파생 - 릴리스 준비라면
develop에서release/x.y.z를 파생 - 일반 개발의 경우:
- Setup Script가 실행되어 의존성(dependency)과 Docker가 자동으로 구동됨
.env를 편집하여 포트/컨테이너 이름을 채번(assign) - 필요에 따라 재시작
- 해당 worktree에서 Claude Code를 실행하고 태스크를 던짐
- Claude가 작업하는 동안, 다른 worktree에서 자신은 다른 일을 하거나 별도의 Claude를 실행함
병렬로 Claude에게 맡길 때의 팁
태스크의 입도는 "파일이 겹치지 않는" 단위로 자른다. worktree가 물리적으로 나누어져 있더라도, 최종적으로 머지(merge)하는 것은 동일한 develop (hotfix의 경우 main과 develop 모두)이므로, 컨플릭트(conflict)가 발생하는 양이 적은 방식을 선택한다.
병렬에 적합한 태스크
- 독립적인 신규 화면 추가 (기존 파일에 미치는 영향이 국소적임)
- 문서·README 업데이트
- 단발성 리팩토링 (범위가 명확하고 로직 변경을 동반하지 않는 것)
- 라이브러리 버전 업그레이드 (의존성 영향 범위를 파악할 수 있는 것)
- 테스트 추가
직렬로 진행하는 것이 안전한 태스크
- DB 스키마 변경을 동반하는 기능 추가 (마이그레이션이 충돌하기 쉬움)
- 공통 기반 (인가·로깅 등)의 재구축 (모든 worktree가 그 위에 올라가 있으므로, 나중에 전부 다시 작업해야 함)
- 아키텍처 판단이 포함된 규모가 큰 설계 변경 (병렬로 진행하기 전에 방침을 결정해야 함)
검증까지 병렬로 수행
포트와 DB를 분리해 두었기 때문에, 브라우저 탭에서 localhost:3001과 localhost:3002를 동시에 열어 양쪽 worktree의 성과물을 동시에 확인할 수 있다. 리뷰 전 셀프 체크 시에는 다음과 같은 작업을 브랜치 전환 없이 수행할 수 있다.
- 개발 서버에서 동작 확인
- DB 상태 확인 (컨테이너 이름이 분리되어 있어 혼동 없음)
- 필요하다면 다른 worktree와 동작을 비교
리뷰와 PR
Conductor의 Actions에 등록한 Review / PR 생성 프롬프트를 호출하여, 독자적인 관점에서의 셀프 리뷰 → 템플릿에 따른 PR 생성까지를 Claude에게 맡긴다.
머지·정리
- 완료된 worktree의 PR을 생성
feature/*→develop로의 PRhotfix/*→main으로의 PR (머지 후,develop에도 반영하는 PR을 별도로 생성)release/*→main으로의 PR (머지 후,develop에도 반영)
- PR을 머지
- 대상 브랜치 (
develop또는main)를 pull - 남아있는 worktree에서 대상 브랜치를 반영 (rebase 또는 merge)
- Conductor로 worktree를 아카이브 (Archive Script가 실행되어 컨테이너와 볼륨이 자동으로 삭제됨)
빠지기 쉬운 함정과 대책
포트/컨테이너 이름의 충돌
- 증상:
docker compose up이 "포트가 사용 중"이라며 실패하거나, 다른 worktree의 DB에 접속하여 데이터가 섞임 - 원인:
.env의 포트 채번이 겹치거나COMPOSE_PROJECT_NAME을 변경하는 것을 잊음 - 대책: worktree를 만든 첫 단계로
.env를 덮어쓰는 것을 습관화. 채번표를 README에 비치
node_modules 등 무거운 의존성
- worktree마다
npm install을 실행하면
시간과 디스크 용량이 부족해짐 - 대책: 패키지 매니저(Package Manager)의 shared cache를 사용 (pnpm의 store, yarn의 global cache 등)
DB 스키마가 브랜치에서 분기되었을 때
- worktree A와 worktree B가 서로 다른 마이그레이션 (Migration)을 추가하면,
develop
에 머지 (Merge)한 후에 다른 worktree가 망가짐 - 대책: 마이그레이션을 동반하는 태스크는 가급적 직렬로 진행한다.
병렬로 진행할 경우에는, 나중에 작업하는 worktree가
develop
을 가져온 시점에
반드시 로컬 DB를 다시 구축한다.
release/hotfix의 더블 머지(Double Merge)를 잊는 경우
hotfix/*와release/*는main과develop양쪽 모두에 머지할 필요가 있지만, 한쪽을 잊기 쉬움 - 대책: PR 템플릿에 「다른 한쪽으로의 반영 PR」 체크란을 넣는다.
Conductor의 Action에 「더블 머지 누락 여부를 확인하는 리뷰 관점」을 넣어두는 것도 유효하다.
Claude의 컨텍스트 경계 (Context Boundary)
- worktree마다 별도의 Claude가 실행되므로, 한쪽 Claude가 알고 있는 설계 판단을 다른 쪽은 알지 못함 - 대책: 설계 판단은
CLAUDE.md나docs/에 작성해 두면, 모든 worktree가 동일한 베이스에서 파생되었을 경우 자연스럽게 공유된다. worktree 한정 지시는 해당 worktree의 Claude에게만 전달한다.
「어느 worktree에서 무엇을 요청했는지 알 수 없게 됨」
- 병렬 작업을 너무 많이 늘렸을 때 나타나는 전형적인 증상
- 대책: 2개의 태스크부터 시작한다. Conductor의 worktree 이름에 「용도」를 포함한다 (예:
feature-login-form).
병렬 개발을 진행할 때의 마음가짐
- 리뷰할 때 여러 PR이 동시에 들어오므로 「베이스가 되는
develop(hotfix라면main)이 어느 시점인가」를 의意识한다. 후발 PR은 최신 반영 상태를 확인한 뒤에 보는 것이 사고를 줄이는 길이다. - 너무 많이 병렬화하면 자신의 리뷰 대역폭(Bandwidth)이 한계에 도달한다. 동시에 실행하는 개수는 자신이 책임지고 검토할 수 있는 범위 내로 유지한다.
- 계속 켜두는 Claude 세션은 비용으로 누적되므로, 끝나면 정지하거나 삭제한다.
- Conductor의 Scripts / Actions는 팀과 공유할 수 있는 자산이다. 좋은 Action이 만들어지면 다른 멤버에게도 전파한다.
치트시트 (Cheat Sheet)
# Conductor로 worktree 생성 → Setup Script가 자동 실행
# make setup && make up (컨테이너 생성, 의존성 설치, 개발 서버 실행)
# .env를 편집하여 포트와 프로젝트 이름을 채번 (필요 시 재시작)
...
Makefile의 최소한의 예시
setup:
npm ci
docker compose build
...
다음에 시도하고 싶은 것 (조사 중)
Claude를 루프(Loop) 처리로 돌리기
병렬 worktree를 통해 일단 「여러 태스크를 동시에 진행할 수 있는」 상태에는 도달했다. 다음으로는 각 worktree의 Claude를 루프 처리로 돌리는 것을 시도하고 싶다.
이미지는 다음과 같다.
- Claude에게 태스크를 던지고 출력을 기다리는 것에 그치지 않고, 계획 → 구현 → 셀프 테스트 (Self-test) → 수정의 루프를 Claude 스스로 돌리게 한다.
- worktree N개 × 루프 N회의 조합으로, 인간이 개입하는 밀도를 더욱 낮춘다.
- 인간은 「루프 시작 지시」와 「루프 종료 시의 리뷰」에만 집중한다.
검토 중인 논점:
- 폭주를 막는 메커니즘
- 최대 반복 횟수 (예: 5회 반복해도 테스트를 통과하지 못하면 강제 종료)
- 비용 상한선 (태스크당 토큰 소비의 천장)
- 명시적인 정지 조건 (테스트 전체 통과, lint 클린 등)
- 루프 중에 Claude가 망가뜨린 상태를 감지하는 방법 (테스트, lint, 타입 체크를 루프 정지 판정에 포함)
PR 리뷰 분담 재검토
병렬로 PR이 양산되기 시작하면, 인간의 리뷰 대역폭이 병목(Bottleneck)이...
될 것이다. 따라서 리뷰 분담 자체를 재검토하고 싶다.
로직 부분은 AI에게 맡긴다: 타입(Type)·테스트(Test)·린트(Lint)·명명(Naming)·구현의 타당성 체크 등, 기계적으로 검증할 수 있는 영역
인간은 도메인 부분만 본다: 사양(Specification)과의 정합성, UX 판단, 비즈니스 로직의 정확성, 엣지 케이스(Edge case) 누락 등, AI가 판단하기 어려운 영역
이것이 돌아가기 시작하면, worktree × Claude의 병렬도를 높여도 리뷰가 따라갈 수 있는 상태로 만들 수 있을 것이다. Conductor의 Actions에 'AI 리뷰 관점'을 심어 PR 코멘트로 자동 투하하는 방식과 같은 운영이 후보가 된다.
마치며
worktree × Claude Code × Conductor의 조합으로, 혼자서 여러 태스크를 동시에 진행할 수 있다는 감각은 상당히 변했다. 한편으로는 "태스크 하나하나에 쏟는 진지함은 바꾸지 않는다", "처음에는 2개의 태스크부터 시작한다"라는 원칙을 지키지 않으면, 허술한 리뷰나 미아가 된 worktree로 인해 오히려 효율이 떨어지게 된다.
비슷한 구성으로 운영하고 있는 분이 있다면, 주의할 점이나 노하우를 알려주시면 감사하겠다.
참고 링크
Conductor
Claude Code
git worktree
git-flow
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기