Claude Code와 Codex를 같은 리포지토리에서 병렬로 실행하기: git worktree의 함정과 상호 리뷰
요약
Claude Code와 Codex 같은 AI 에이전트를 하나의 리포지토리에서 병렬로 실행할 때 발생하는 충돌 문제를 다룹니다. 이 글은 'git worktree'를 활용하여 각 에이전트에게 독립적인 작업 공간을 제공하고, 안전하게 협업하는 방법을 제시합니다.
핵심 포인트
- AI 에이전트 간의 파일 덮어쓰기 및 작업 트리 충돌 방지
- git worktree는 리포지토리 외부(sibling folder)에 분리하여 사용하는 것이 안전함
- 같은 브랜치는 두 개의 worktree에서 동시에 사용할 수 없으므로, 별칭 브랜치 사용이 권장됨
무슨 일이 일어나는가
Claude Code와 Codex를 같은 리포지토리에서 동시에 실행하면, 대략 다음 세 가지 중 하나에 해당합니다.
- 두 에이전트가 같은 파일을 덮어쓰면서, 한쪽의 변경 사항이 다른 쪽의 작업 트리를 망가뜨림
- 한쪽 계정이 사용량 제한에 도달하여 작업이 중간에 멈춤
- 생성된 차이점을 작성한 에이전트 자신이 리뷰해도 동일한 실수를 함
본문에서는 이 문제들을 하나씩 '에이전트별로 git worktree를 분리하기', '다른 에이전트에게 커밋되지 않은 변경 사항을 읽게 하기', '로그인을 분리하여 제한에 대비하기' 방식으로 해결합니다. 실제로 Mac에서 실행하며 확인한 내용과 그렇지 않은 내용을 마지막에 나누어 설명하겠습니다.
검증 환경은 macOS, git 2.50.1, Claude Code 2.1.296, Codex CLI 0.162.1입니다.
1. 에이전트별로 worktree를 분리하기
git worktree는 하나의 리포지토리(.git)에 여러 작업 디렉토리를 연결하는 기능입니다. 브랜치마다 다른 폴더가 생기므로, 에이전트들이 각각의 폴더에서 자유롭게 파일을 편집해도 서로의 작업 트리에 영향을 주지 않습니다.
cd ~/work/app
git worktree add ../app-refactor -b refactor
git worktree add ../app-bugfix -b bugfix
...
/Users/me/work/app c5cd751 [main]
/Users/me/work/app-bugfix c5cd751 [bugfix]
/Users/me/work/app-refactor c5cd751 [refactor]
그 후에는 폴더별로 에이전트를 실행하기만 하면 됩니다.
cd ../app-refactor && claude # 첫 번째 터미널
cd ../app-bugfix && codex # 두 번째 터미널
Claude Code에는 -w 또는 --worktree [name]이라는 옵션이 있어, 세션용 worktree를 CLI 측에서 만들어 줍니다. Codex에는 이에 해당하는 옵션이 없기 때문에, 본문에서는 양쪽에 모두 사용할 수 있는 순수 git으로 설명합니다.
worktree는 리포지토리 내부가 아닌 형제 폴더(sibling folder)에 두는 것이 안전합니다. 만약 리포지토리 내부에 둔다면 .gitignore에 추가하지 않으면 메인 작업 트리에서 추적되지 않는 폴더로 보이게 됩니다.
2. 실제로 걸리는 지점들
아래 내용은 테스트용 리포지토리에서 실제로 명령어를 입력해 확인한 동작입니다.
같은 브랜치는 두 개의 worktree에 둘 수 없다
git worktree add ../dup refactor
fatal: 'refactor' is already used by worktree at '/tmp/.../app-refactor'
두 에이전트가 같은 브랜치를 건드리게 하는 것은 git이 거부합니다. 이는 안전장치로 작동하기 때문에 오히려 환영할 만한 동작입니다. 같은 시작점에서 시도하고 싶다면, -b를 사용해 별칭의 브랜치를 만들면 됩니다.
.env나 node_modules는 따라오지 않는다
새로운 worktree에는 추적되고 있는 파일만 체크아웃됩니다. .gitignore 대상인 .env도, npm install이 완료된 node_modules도 새로운 폴더에는 없습니다. 에이전트가 처음에 '환경 변수가 없다', '의존성이 들어있지 않다'로 실패하는 원인은 대부분 이것 때문입니다.
작성과 동시에 .env를 복사하는 함수를 ~/.zshrc에 넣어두면 편리합니다.
wt() {
local name=$1 root dest
root=$(git rev-parse --show-toplevel) || return
...
cd ~/work/app && wt refactor # ~/work/app-refactor가 생성되고, .env도 복사됨
의존성 설치는 프로젝트마다 다르기 때문에 여기에는 넣지 않았습니다. pnpm install처럼 스토어를 공유할 수 있는 패키지 매니저라면, worktree를 늘려도 디스크 증가 폭이 작아집니다.
stash는 worktree 간에 공유된다
.git이 하나이기 때문에, stash도 하나입니다. 다른 worktree에서 git stash를 사용한 변경 사항이 메인 git stash list에 나타났습니다.
$ git -C ../app-x stash -q
$ git stash list # 메인 worktree에서
stash@{0}: WIP on x: c5cd751 init
에이전트가 git stash나 git stash pop을 사용하는 것은 드문 일이 아닙니다. 다른 세션의 stash를 pop해 버리는 사고를 방지하기 위해, 병렬로 구동하는 동안 에이전트용 지시 파일(CLAUDE.md나 AGENTS.md)에 'stash는 사용하지 말고, 작업 중인 변경 사항은 임시 커밋으로 보관한다'고 적어두면 안전합니다.
변경 사항이 남아있는 worktree는 삭제할 수 없습니다
$ git worktree remove ../app-refactor
fatal: '../app-refactor' contains modified or untracked files, use --force to delete it
--force를 붙이면 지울 수 있지만, 그때 커밋되지 않은 변경 사항은 손실됩니다. 붙이기 전에 git -C ../app-refactor status를 보는 습관이 필요합니다. 작업을 마쳤으면, merge한 후 git worktree remove ../app-refactor로 삭제하고, 브랜치는 git branch -d refactor로 정리합니다.
그 외
여기부터는 이 글을 위해 실험한 것이 아니라, 병렬 실행에서 일반적으로 발생하는 이야기입니다. 개발 서버를 구동하는 에이전트가 여러 개 있으면, 같은 포트(3000번 등)를 차지하려고 합니다. PORT를 환경 변수로 변경할 수 있는 구성으로 만들어두면, worktree마다 PORT=3001처럼 분배할 수 있습니다.
3. 커밋되지 않은 차이점을 다른 에이전트에게 리뷰시키기
작성한 에이전트에게 자신의 차이점을 리뷰하게 하면, 같은 착각을 물려받기 쉽습니다. 회사 모델이 다른 곳에 읽게 하는 편이, 놓치는 종류가 달라 도움이 된다는 것이 필자의 체감입니다.
Codex에게 맡기기
Codex CLI에는 review 서브 커맨드가 있으며, 도움말에 따르면 --uncommitted로 스테이징된 변경 사항, 미스테이징 변경 사항, 추적되지 않은 변경 사항을 한 번에 리뷰합니다.
cd ../app-refactor
codex review --uncommitted
베이스 브랜치와의 차이점을 보려면 --base main을, 하나의 커밋이라면 --commit <SHA>를 사용합니다.
Claude Code에게 맡기기
Claude Code는 -p (--print)로 비대화형으로 작동하기 때문에, 차이점을 표준 입력에서 전달할 수 있습니다.
git add -N . # 추적되지 않은 파일을 차이점에 포함시키기 위해
git diff HEAD | claude -p
Claude와 Codex 모두 일정 기간 동안 사용량 상한선이 있습니다. 병렬로 구동할수록 더 빨리 도달합니다. 여러 개의 계정으로 로그인해 두면, 상한에 도달했을 때 다른 계정으로 전환하여 계속 작업을 진행할 수 있습니다.
### 설정 폴더를 분리하고 로그인을 나누기
두 CLI 모두 환경 변수를 통해 설정 및 인증 정보의 저장 위치를 변경할 수 있습니다. Claude Code는 `CLAUDE_CONFIG_DIR`이고, Codex는 `CODEX_HOME`입니다. 빈 폴더를 지정하여 로그인 상태가 분리되는 것을 확인했습니다.
$ CLAUDE_CONFIG_DIR=/tmp/cfgA claude auth status
{
"loggedIn": false,
...
이것을 사용하여 로그인별로 별칭(alias)을 만듭니다.
alias claude-b='CLAUDE_CONFIG_DIR=$HOME/.claude-b claude'
alias codex-b='CODEX_HOME=$HOME/.codex-b codex'
claude-b auth login # 두 번째 계정으로 로그인
세션 기록도 설정 폴더별로 저장되기 때문에, `claude-b`는 `claude`의 기록을 알지 못합니다. 이것이 다음 문제점이 됩니다.
### 중간에 전환할 때는 인수인계를 직접 남겨야 함
상한에 도달하여 로그인을 바꿀 경우, 새로운 세션은 이전 세션이 무엇을 시도했는지 알지 못합니다. 대화 기록 파일을 다른 설정 폴더로 옮기는 방법은 이 글에서는 시도하지 않았으므로 추천하지 않습니다. 확실한 방법은 상한에 도달하기 전에 에이전트가 상황을 파일로 작성하게 하는 것입니다.
목적
인증 미들웨어를 TypeScript로 전환하기
...
`CLAUDE.md` 등에
⚠️ `[IMG:N]` 형식 토큰은 이미지 placeholder 입니다. 번역하지 말고 원래 위치에 그대로 유지하세요.
| 내용 | 상황 |
|---|---|
| 같은 브랜치를 2개의 worktree에서 사용할 수 없음, `.env` 파일이 따라오지 않음, stash가 공유됨, 변경 사항이 있는 worktree는 `remove` 할 수 없음, `git diff HEAD`가 추적되지 않은 파일을 출력하지 않고 `add -N`으로 출력함 | 테스트용 리포지토리에서 실행하여 확인 |
`wt` 함수 | zsh에서 실행하여 확인 |
`CLAUDE_CONFIG_DIR`, `CODEX_HOME`으로 로그인 상태가 분리됨 | 빈 폴더에서 확인 |
`codex review --uncommitted`, `claude -w`의 존재와 설명 | `--help`로 확인 |
`codex review`와 `claude -p`로 모델에 실제로 리뷰시킨 결과 | 실행하지 않음 (명령어 형태만) |
| 포트 점유 문제 | 실험하지 않음 |
초안에는 AI(Claude)를 사용하고, 명령어는 작성자가 직접 실행하여 내용을 확인했습니다.
### Discussion

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