모든 AI 에이전트에 개별 Git 워크트리 제공

발행: (2026년 6월 7일 AM 10:51 GMT+9)
11 분 소요
원문: Dev.to

Source: Dev.to

한 주 아침에 Claude Code 에이전트 5개를 동시에 실행했습니다. 상황이 정리될 때쯤이면 서로 다른 3개의 git 충돌이 발생했고, 한 브랜치에 두 개의 전혀 관계 없는 탭 커밋이 뒤섞였으며, 강제 푸시가 필요한 복구 작업을 직접 승인해야 했습니다. 모든 작업은 살아남았지만, 계획에 없던 1시간을 소모했으며, 문제의 원인은 단 한 파일에 있었습니다.

아침에 일어난 “점심 전 7개의 PR” 이야기는 하이라이트였고, 이번 글은 그때 한 규칙을 빼먹고 같은 패턴을 실행했을 때 마주친 버그를 다룹니다.

병렬 탭 패턴

하나의 코디네이터 탭이 하루의 컨텍스트를 보관하고 병합 결정을 내리며, 여러 개의 위성 탭이 각각 하나의 범위된 작업을 수행합니다. 그날 아침에는 다섯 개의 위성 탭이 있었는데, 각각 콘텐츠 기능 Phase 2, 다른 기능 Phase 3, Stripe 문서 수정, Supabase 보안 패스, 그리고 별도 버그를 추적하는 조사 탭이었습니다.

위성 탭들은 git worktree(같은 저장소에 연결된 별도 작업 디렉터리)를 사용했지만, 나머지 두 탭은 사용하지 않았습니다. 두 탭은 모두 공유된 메인 체크아웃 안에서 git 명령을 실행했는데, 작업이 작아 보였기 때문이었습니다.

발생한 3가지 충돌 (순서대로)

  1. 첫 번째 충돌
    한 위성 탭이 첫 커밋을 만들었는데, 그 커밋이 다른 탭의 브랜치에 착륙했습니다. 다른 탭이 git checkout -b mid-task 를 실행하면서 공유 HEAD를 이동시켰고, 커밋을 만든 탭은 이를 인지하지 못했습니다. 복구는 --mixed 리셋으로 올바른 커밋으로 되돌린 뒤 브랜치를 다시 분리하는 방식이었습니다. 잘못된 위치에 있던 작업은 푸시하기 전에 잡아냈기 때문에 보존될 수 있었습니다.

  2. 두 번째 충돌
    Stripe‑fix 탭이 git checkout -b fix/stripe-checklist-doc-rot 를 실행하면서 다시 공유 HEAD를 이동시켰습니다. 그 뒤 보안 탭이 커밋을 하면 그 커밋이 Stripe 브랜치에 들어갔습니다. 결과적으로 원격 브랜치에는 전혀 관계 없는 두 커밋(보안 작업과 문서 수정)이 겹쳐졌습니다. 이를 풀어내려면 다음 명령이 필요했습니다.

    git rebase --onto db0f96c 93d66ce
    git push --force-with-lease

    강제 푸시는 파괴적인 작업이므로, 제 규칙은 명시적인 승인 없이는 실행되지 않게 하는 것이었습니다. 따라서 복구 과정이 멈추고 제가 승인하기 전까지 원격에 영향을 주지 않았습니다.

  3. 세 번째 충돌
    복구 중에 보안 탭은 Stripe 탭의 작업 트리를 살펴봐야 했습니다. 두 에이전트가 서로의 커밋되지 않은 상태를 읽어가며 “누가 무엇을 했는지”를 재구성해야 했기 때문입니다.

한 문장으로 요약하면: .git/HEAD 파일은 체크아웃당 하나이며, 같은 디렉터리에서 여러 터미널이 이 파일을 동시에 읽고 쓰게 됩니다.

예시:

  • 탭 A가 git checkout branch-a 를 실행 → HEAD가 branch-a 를 가리킴.
  • 같은 디렉터리에서 탭 B가 git status 를 실행 → HEAD가 branch-a 로 보임(자신이 생각하는 브랜치가 아니라).
  • 탭 B가 커밋을 하면 그 커밋은 branch-a 에 기록됩니다.

이것은 특수한 레이스 컨디션이 아니라, git이 설계대로 동작하는 결과입니다. 체크아웃은 공유된 가변 상태이며, 저는 다섯 개의 에이전트가 동시에 이를 수정하고 있었습니다. 공유 가변 상태와 병렬 프로세스는 가장 오래된 버그 유형이지만, 사람은 보통 한 디렉터리에서 동시에 다섯 개의 체크아웃을 실행하지 않기 때문에 git에서는 거의 마주치지 않습니다.

git worktree 로 해결

git worktree 를 사용하면 각 에이전트가 자신만의 작업 디렉터리와 HEAD를 갖게 되면서도 동일한 객체 저장소를 공유합니다.

git worktree add ../wt-stripe-fix -b fix/stripe-checklist-doc-rot origin/main

이제 해당 탭은 전용 디렉터리, 전용 HEAD, 전용 체크아웃 브랜치를 갖게 됩니다. 다른 탭의 HEAD를 건드릴 수 없으므로 충돌이 발생하지 않습니다. 그 아침에 worktree 를 사용한 두 탭은 충돌이 전혀 없었고, 사용하지 않은 세 탭이 모든 세 충돌을 일으켰습니다.


지표 (그 아침)

항목
병렬 에이전트 실행 수5
자체 worktree 를 부여받은 탭2
메인 체크아웃을 공유한 탭3
격리된 탭에서 발생한 충돌0
공유된 탭에서 발생한 충돌3
강제 푸시가 필요한 복구1

결과는 명확합니다. 격리된 환경에서는 충돌이 0, 공유된 환경에서는 3. “작은 변경이라 괜찮다”는 중간 지점은 존재하지 않았습니다.

현재 규칙 (예외 없음)

  • 모든 위성 에이전트는 자체 git worktree 를 사용한다.
    작은 3줄 수정이라도 예외는 없습니다. 변경 규모와는 무관하게 git checkout -b 는 공유 HEAD 를 이동시키기 때문입니다.

  • 코디네이터 탭은 여전히 메인 체크아웃에 남아 있어도 안전합니다. 코디네이터는 병합과 문서 작업만 담당하고, 여기서는 브랜치를 만들지 않기 때문입니다.

  • 디스패치 프롬프트에는 반드시 git worktree add … 명령이 포함됩니다. 에이전트가 스스로 필요 여부를 판단하게 하지 않습니다.

  • 위성 에이전트는 첫 단계에서 git rev-parse --show-toplevel 을 실행합니다. 반환값이 공유 메인 경로이면 “shared worktree detected” 라고 알리고 커밋을 중단합니다. 이 검사는 몇 초면 끝나며, 한 시간짜리 충돌을 방지합니다.

  • 코디네이터 탭 자체는 메인 체크아웃에 머물러도 안전합니다. 브랜치를 만들지 않으니 충돌 위험이 없습니다.

이러한 규칙은 “작은 일이라 조심하면 된다”는 기억에 의존하지 않습니다. 안전한 경로만을 도구가 제공하도록 설계했기 때문입니다. 디스패치 프롬프트에 명령을 넣고 첫 단계에서 강제 검사를 수행함으로써, 기억이 아닌 프로세스에 규율을 옮긴 것입니다. 몇 주 전 전체 운영 매뉴얼을 서술형에서 구조화된 형태로 바꾼 것과 같은 원리입니다.

병렬 에이전트를 운영한다면 따라야 할 패턴

  • 에이전트당 하나의 worktree 를 항상 사용합니다.

    git worktree add ../wt-<agent-name> -b <branch-name> origin/main

    공유 저장소 덕분에 비용은 거의 들지 않으며, 별도 HEAD 덕분에 서로 충돌하지 않습니다.

  • 첫 번째 동작은 위치 확인 입니다.

    git rev-parse --show-toplevel

    잘못된 디렉터리이면 즉시 중단하고, 진행‑희망이 아니라 중단 합니다.

  • 작업이 끝난 뒤에는 정리 합니다.

    git worktree remove <path>
    git branch -D <branch-name>

    다음 세션이 깨끗한 상태에서 시작됩니다.

병렬 에이전트를 사용하면 처리량이 크게 늘어나지만, 동시에 공유 상태 버그도 그만큼 늘어납니다. worktree 는 최적화가 아니라, 병렬성을 안전하게 만들기 위한 격리 경계입니다.

5 명 에이전트 + 1 HEAD → 3 건 충돌
5 명 에이전트 + 5 HEAD → 0 건 충돌

모든 에이전트에게 자체 worktree 를 제공하세요.


저는 ConnectEngine OS 를 프로덕션 환경에서, 공개적으로,

0 조회
Back to Blog

관련 글

더 보기 »