
당신의 Git 로그가 Claude 에이전트들을 위한 Rebase 파이트 클럽처럼 보이나요?
요약
여러 개의 Claude Code 에이전트를 동시에 사용할 때 발생하는 Git 충돌, 빌드 리소스 경합, 데이터베이스 간섭 문제를 다룹니다. 이를 해결하기 위해 로컬 머지 큐(Merge Queue) 도구와 Claude Code의 워크트리(Worktree) 기능을 활용하는 방법을 제안합니다.
핵심 포인트
- 병렬 에이전트 실행 시 Git 커밋 충돌 및 리소스 경합 발생 가능성
- Claude Code의 워크트리 기능을 통한 편집 사항 격리 방법
- Claude Code Merge Queue를 이용한 로컬 머지 관리 및 동기화
이 중 익숙한 내용이 있나요?
동일한 저장소(repo)에서 세네 개의 Claude Code 에이전트가 동시에 작동하고 있습니다. 좋습니다 — 그것이 바로 병렬로 실행하는 목적이니까요. 그런데 다음과 같은 일이 발생합니다:
- 두 에이전트가 같은 10초 사이에 전체 빌드(full build)를 시작하는 바람에 MacBook의 팬이 제트 엔진처럼 돌아갑니다.
- 한 에이전트의 푸시(push)가 거부되어, 리베이스(rebase) 후 재시도했는데 그 재시도가 같은 시간대에 작업을 마친 두 번째 에이전트의 작업 바로 위에 얹혀버립니다. 이제 어떤 변경 사항이 실제로 반영되었는지 풀어내야 합니다.
- 테스트가 실패해서 다시 실행했더니 통과합니다. 플래키(flaky)한 테스트가 아닙니다. 두 에이전트가 동시에 동일한 테스트 데이터베이스를 리셋하여 서로의 실행 결과가 깨진 것처럼 보였던 것입니다.
- git 로그를 열어보니 마치 논쟁을 하는 것 같습니다 — 의미 없는 "Merge branch" 커밋이 세 개 연속으로 나타납니다.
- CLAUDE.md에 모든 에이전트에게 "항상 머지 큐(merge queue)를 통해 반영하라"고 지시했지만, 급했던 에이전트 하나가 결국 main 브랜치에 직접 푸시해 버렸습니다.
이 중 어느 것도 규율(discipline)의 문제는 아닙니다. 이것은 빠르고 자신감 넘치는 여러 프로세스가 하나의 가변적인 것 — 브랜치, CPU, 데이터베이스 — 을 공유할 때, 구조적으로 서로의 영역을 침범하는 것을 막을 장치가 없을 때 발생하는 현상입니다. 만약 이 중 하나라도 당신의 오후에 일어나고 있다면, 계속 읽어주세요.
아니면 그냥 Claude Code Merge Queue를 확인해 보세요. 병렬 Claude Code 에이전트를 위한 무료 로컬 머지 큐(merge queue)입니다. 런타임 의존성(runtime deps)이 없고, PR(Pull Request)도 필요 없으며, Actions 비용도 들지 않습니다. 이 도구는 코드 내에 머무릅니다 — FIFO 락(lock), git-hook 강제 적용, 설정 구조(config shape)와 함께 말이죠.
워크트리(Worktrees)는 해결되었습니다. 하지만 이것은 아닙니다.
Claude Code는 이제 네이티브하게 에이전트를 격리합니다 — 설정 없이 플래그 하나면 충분합니다:
claude --worktree <name>
v2.1.49 (2026년 2월) 이후로, 이 기능은 각 세션에 고유한 git 워크트리(worktree)를 부여하여 동시 실행되는 에이전트들이 서로의 커밋되지 않은 편집 사항을 덮어쓰는 것을 방지합니다. 좋습니다 — 그 부분은 해결되었습니다. 만약 "에이전트를 어떻게 격리하나요?"라는 질문 때문에 오셨다면, 이미 답을 얻으셨습니다.
격리만으로는 해결할 수 없는 것은 네 개의 독립된 에이전트가 동시에 배포(land), 빌드, 테스트를 시도할 때 무슨 일이 벌어지는지입니다.
- 모두가 같은 브랜치에 푸시(push)합니다. 한 명이 성공하고, 다른 한 명은 non-fast-forward 거부를 받습니다. 패배한 쪽이 리베이스(rebase)를 하고 다시 푸시하면 — 만약 세 번째 에이전트가 그와 같은 시간대에 배포된다면, 이 재시도까지 경쟁하게 됩니다. 에이전트 수가 많아질수록 이는 해결되는 것이 아니라 복합적으로 쌓입니다.
- 전체 빌드는 무겁습니다. 네 개를 동시에 실행하면 동일한 CPU/메모리/디스크 자원을 네 방향에서 사용하며 과부하(thrashing)가 걸립니다.
- 테스트가 공유 리소스 — 데이터베이스나 큐(queue)와 같은 곳 — 에 도달할 경우, 동시 실행들이 서로의 초기화 과정을 방해합니다. 실패는 불안정하게 보이지만, 그렇지 않습니다. 그들은 정직합니다.
이 모든 것이 에이전트들의 기술적인 문제 때문은 아닙니다. 여러 개의 빠르고 자신감 있는 프로세스가 트래픽 제어 장치 없이 하나의 변경 가능한(mutable) 자원을 공유할 때 발생하는 일입니다.
언급할 만한 가치가 있는 단 하나의 설계 결정은 다음과 같습니다: 어디에도 타임아웃 (timeout)이 없다는 점입니다. 특정 초가 지났다고 해서 락 (lock)이 해제되지 않습니다. 그것은 느린 머신이나 거대한 빌드 상황에서 틀리기 딱 좋은 마법의 상수 (magic constant)일 뿐입니다. 대신, 락은 소유자가 이를 해제하거나, 다른 대기자 (waiter)가 소유자의 프로세스가 더 이상 존재하지 않음을 감지할 때까지 유지됩니다. 프로세스가 여전히 살아있는지 확인하는 것은 비용이 저렴하고 정확하므로, 조정해야 할 노후화 윈도우 (staleness window)도 없고, "30초 후에 죽었다고 가정하고 아무것도 실행 중이지 않기를 바라는" 식의 방식도 필요 없습니다.
이는 빌드 도중 락을 보유한 프로세스를 kill -9로 종료하더라도 아무것도 엉키지 않는다는 것을 의미합니다. 바로 다음 대기자가 프로세스가 사라졌음을 확인하고 즉시 제어권을 넘겨받습니다. 오래된 락을 정리하는 스크립트도, "그냥 노트북을 재부팅하고 다시 시도하세요"라는 말도 필요 없습니다.
또 다른 요소는 공정성 (fairness)입니다. 대기자들은 락이 해제되는 즉시 락을 차지하기 위해 다투지 않습니다. 먼저 요청한 사람이 먼저 가져가는 엄격한 선입선출 (FIFO, first-in-first-out) 순서를 따릅니다. 그렇지 않으면 가장 빠르게 폴링 (polling)하는 에이전트가 영원히 새치기를 할 수 있으며, 그동안 계속 기다려온 에이전트를 기아 상태 (starving)로 만들 수 있습니다.
build-lock과 land는 서로 다른 이름 아래 있는 정확히 동일한 락입니다. 빌드는 랜딩 (landing)과 경합하지 않으며, 그 반대도 마찬가지입니다.
강제성: 관습만으로는 에이전트와의 접점에서 살아남을 수 없다
claude-code-merge-queue land는 당신의 레인 (lane)을 통합 브랜치 (integration branch)로 리베이스 (rebase)하고, 해당 락을 통해 한 번에 하나의 레인씩 푸시 (push)합니다. 하지만 "항상 여기를 통해 랜딩하세요"라는 것은 단지 관습일 뿐이며, 시간 압박을 받는 빠른 에이전트는 관습을 건너뛰고 정확히 잘못된 순간에 단 한 번의 일반적인 git push를 직접 수행하여, 그 관습을 무의미하게 만들어 버릴 것입니다.
따라서 실제 보장(guarantee)은 지침 파일(instructions file)에 들어있는 것이 아닙니다. 그것은 git pre-push hook에 존재하며, 에이전트가 규칙을 기억하든 아니든 모든 push 시점에 실행됩니다. 이 hook은 큐(queue) 자체의 랜딩 단계(landing step)를 통해 들어오는 경우가 아니라면, 통합 브랜치(integration branch)나 보호된(protected) 것으로 표시된 다른 모든 곳으로의 직접적인 push를 차단합니다. 랜딩 단계는 차단을 해제하는 단 하나의 플래그를 전환할 수 있는 유일하게 허용된 경로입니다. 그 외의 모든 직접적인 push는 모호한 경고 대신 실행해야 할 실제 명령어를 제공하며 즉시 거부됩니다.
동일한 hook에서 실제 체크(checks)도 실행됩니다 — lint, typecheck, 테스트, 빌드 등 CI에서 신뢰할 만한 것이라면 무엇이든 가능하며, 이 체크들이 실패하면 push도 실패합니다. 체크 명령어를 구성하지 않은 상태로 두면 기본적으로 모든 push가 실패합니다. 이 도구는 사용자가 "체크를 건너뛰겠다"는 의도였다고 가정하는 대신, 검증되지 않은 코드가 조용히 랜딩되는 것을 거부합니다.
이 모든 것을 우회할 수 있는 방법은 정확히 하나뿐입니다. 여러 개의 오버라이드(overrides)가 아니라, 진정한 비상 상황(break-glass)을 위한 단 하나의 비상 환경 변수(emergency environment variable)를 사용하는 것입니다. 이것이 무엇이고 무엇이 아닌지에 대해 솔직해질 가치가 있습니다 — 이것은 실수와 망각에 의한 지름길은 막아주지만, 해당 변수를 스스로 설정하거나 hook을 삭제하는 의도적인 에이전트를 막지는 못합니다. 여기서 그 어떤 것도 보안 경계(security boundary)는 아닙니다. 이것은 적대적인 에이전트에 대한 방어책이 아니라, 빠르고 자신감 넘치지만 잘 잊어버리는 에이전트들을 위한 조정(coordination) 해결책입니다.
왜 그냥 GitHub의 Merge Queue를 사용하지 않나요
GitHub는 이를 제공합니다. 하지만 이것이 왜 "한 명의 사용자, 여러 개의 로컬 에이전트"라는 문제와는 다른 문제를 해결하기 위해 만들어졌는지 이해할 가치가 있습니다:
| GitHub Merge Queue | Claude Code Merge Queue | |
|---|---|---|
| Private repo | Enterprise Cloud 전용 | 모든 플랜, 모든 리포지토리 |
| ... |
만약 당신이 혼자서 빠르게 개발하고 있으며, 이러한 diff(차이점) 대부분에 대한 유일한 리뷰어가 본인의 테스트 스위트(test suite)뿐이라면, PR(Pull Request)은 뒤를 받쳐줄 리뷰어가 없는 형식적인 절차에 불과합니다. 또한 GitHub의 2026년 가격 정책(2026년 3월부터 self-hosted runner에 대한 새로운 분당 과금 방식 도입)은 측정 범위를 줄이는 것이 아니라 오히려 늘리는 방향으로 흐르고 있습니다. PR을 건너뛰는 것은 리뷰를 건너뛰는 것이 아닙니다. 모든 diff를 읽을 시간이 없는 인간을, 매번 동일한 방식으로 모든 diff를 확인하는 기계로 교체하는 것입니다.
가장 유사한 기존 사례, 그리고 한계점
이 분야가 비어 있다고 암시하기보다는 직접적으로 이름을 언급할 가치가 있습니다. 비어 있지 않기 때문입니다. 다만, 이들의 '조합'이 빠져 있을 뿐입니다.
- tfriedel/claude-worktree-hooks는 worktree 생명주기(env 파일, 의존성 설치, 포트 할당, 정리 등)를 다루지만, 자체 README에서 명확하게 선을 긋고 있습니다: 빌드 큐(build queues), 머지 큐(merge queues), git hook 강제 적용 또는 테스트 격리(test isolation)는 다루지 않습니다.
- 이 dev.to 포스트는 대신 GitHub 자체의 브랜치 보호(branch-protection) 방식을 취합니다. 즉, 필수 PR 및 상태 체크(status checks)를 사용하는 방식인데, 이는 전적으로 플랫폼에 의해 강제되며 로컬 큐가 없고 Actions-minutes 비용에 대한 언급도 없습니다.
- 이 도구 출시 이후 공개된 yongjip/mergetrain은 다른 형태의 방식으로 유사한 문제를 해결합니다. 이는 한 번에 하나의 차선(lane)을 안착시키는 대신, SQLite 기반의 큐와 단일 lease-fenced runner를 사용하여 여러 에이전트의 브랜치를 하나의 검증된 "기차(train)"로 병합한 후 원자적 푸시(atomic push)를 수행합니다.
이 도구가 하지 않는 것
- 단일 머신용이며, 플릿(fleet)이 아닙니다. FIFO 큐는 로컬 임시 저장소에 존재하며, 저장소(repo)를 키(key)로 사용합니다. 다른 머신으로 확장되는 범위는 전혀 없습니다. 두 대의 노트북이 동일한 순간에 랜딩(landing)을 시도하면, 단순히 Git의 일반적인 non-fast-forward 거부(rejection)가 발생합니다. 이는 데이터가 손상되는 것이 아니라 단지 조정되지 않은 상태일 뿐이며, 큐가 전혀 없는 공유 브랜치에 푸시하는 현재의 모든 팀이 겪는 상황과 정확히 일치합니다.
- 보안 경계가 아닙니다. 위에서 언급했듯이, 이는 적대적인 에이전트(adversarial agent)가 아닌 실수나 관습의 이탈(convention drift)을 다룹니다.
- 체크가 실행되었음을 보장할 뿐, 그 결과가 훌륭함을 보장하지는 않습니다. 이 도구는
checkCommand가 존재하고 통과했는지를 강제합니다. 그것이 실제 테스트 스위트(suite)인지 아니면 단순히echo ok인지 여부는 전적으로 사용자에게 달려 있습니다. - 실질적인 처리량 제한(throughput ceiling)이 존재합니다. 락(lock)은
checkCommand가 실행되는 전체 기간 동안 유지됩니다. 즉, 머신 전체에서 한 번에 하나의 랜딩만 가능합니다. 3~4분 정도 소요되는 테스트 스위트의 경우, 추가적인 큐 대기 시간을 제외하고도 시간당 랜딩 횟수가 20회 미만으로 제한됩니다.
핵심 요약 (Takeaways)
- 워크트리(Worktree) 격리와 랜딩 조정(landing coordination)은 서로 다른 두 가지 문제입니다. 첫 번째 문제(2026년 2월부터 Claude Code에 내장됨)를 해결한다고 해서 두 번째 문제가 해결되는 것은 아닙니다.
- 타임아웃(timeout) 대신 PID 생존 여부(PID-liveness)를 키로 사용하는 FIFO 락이 이 도구 전체를 관통하는 핵심 아이디어입니다. 이를 통해 오래된 락(stale-lock)을 정리할 필요도, 조정해야 할 마법 같은 유효 기간(staleness constant) 상수가 필요하지도 않습니다.
- 에이전트가 건너뛸 수 있는 관습(convention)은 보장이 아닙니다. 실제 강제(enforcement)는 에이전트에게 전달하는 지침이 아니라 Git 훅(git hook)에서 이루어집니다.
Claude Code Merge Queue는 MIT 라이선스이며, 런타임 의존성(runtime deps)이 전혀 없습니다. npx claude-code-merge-queue init 명령어를 통해 두 번의 명령만으로 기존 저장소에 연결할 수 있습니다. 만약 이미 동일한 저장소에 대해 하나 이상의 Claude Code 에이전트를 실행하고 있다면, 아마도 이 문제의 어떤 버전을 경험해 보셨을 것입니다. 댓글을 통해 어떻게 해결하고 계신지 알려주시면 감사하겠습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기


