서론
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 클라이언트에서도 양쪽 모두 동시에 검증할 수 있다.
| 역할 | 툴 |
|---|
| 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로의 PR
hotfix/* → 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