jaysnote
6분

여러 AI 에이전트를 동시에 돌리는 법 — git worktree로 작업을 물리적으로 나누기

Backend·Frontend 에이전트를 동시에 돌리기 위해 git worktree와 branch로 작업을 물리적으로 격리한 이유와 실제 흐름을 정리했다

문제: 왜 한 브랜치에서는 안 되는가

Galaxy Device Issue Triage Console이라는 작은 웹앱 하나를 만드는 데, Backend(서버 분류 로직)와 Frontend(대시보드 UI)를 AI 에이전트 둘에게 동시에 맡기고 싶었다. 같은 폴더, 같은 브랜치에서 두 에이전트를 동시에 돌리면 무슨 일이 생길까.

한쪽이 server.py를 고치는 동안 다른 쪽이 index.html을 고쳐도, 파일 자체는 안 겹치니 괜찮아 보인다. 그런데 커밋 이력이 한 브랜치에 뒤섞이는 순간, “이 커밋이 Backend가 한 건지 Frontend가 한 건지”가 diff만 봐서는 구분이 안 된다. 게다가 한 에이전트가 실수로 상대 파일까지 건드리면 그걸 막을 장치가 전혀 없다. 순서대로 하나씩 시키면 이런 문제는 없지만, 그러면 두 번째 에이전트는 첫 번째가 끝날 때까지 그냥 기다려야 하니 병렬로 시키는 의미가 없다.

필요한 건 이거였다. 여러 에이전트가 서로의 미완성 코드를 아예 보지 못하게 작업 공간을 나누면서도, 나중에 “누가 뭘 했는지”를 커밋 단위로 정확히 추적할 수 있는 구조.

해결: worktree(물리적 격리) + branch(이력 격리)를 역할에 매핑

git에는 이 둘을 각각 해결하는 기능이 이미 있다.

  • worktree는 같은 저장소를 가리키지만 디스크상 완전히 다른 폴더에 체크아웃된 작업 공간이다. 에이전트(Codex CLI) 세션은 자기가 실행된 폴더 안만 보므로, backend_worktree에서 도는 세션은 frontend_worktree에 뭐가 있는지 아예 알 수 없다.
  • branch는 그 작업 공간에서 만든 커밋들을 다른 작업의 이력과 섞이지 않게 분리한다.

이 둘을 결합하면 “물리적으로 분리된 작업장 + 독립된 변경 이력”이 한 번에 생긴다. 이 실습에서는 이 조합을 역할 5개에 그대로 매핑했다.

역할별 worktree·branch 구조 — Main이 갈라 보내고, Backend·Frontend가 동시에 구현하고, Integration이 합쳐서 검증하고, Human이 최종 merge한다

Main은 작업을 나누고 마지막에 검토만 할 뿐 코드를 직접 만지지 않는다. Backend와 Frontend는 같은 지점(main)에서 동시에 갈라져 나가 서로 다른 파일만 건드리고, Integration은 그 둘을 병합하고 검증만 할 뿐 구현 코드에는 손대지 않는다. 그리고 실제로 main이 바뀌는 순간은 사람이 마지막 merge 버튼을 누르는 그 한 번뿐이다.

이 구조를 지탱하는 핵심 결정 네 가지

1. 파일 범위로 역할을 가른다. Backend는 server.py만, Frontend는 index.html, src/app.js, src/styles.css만 건드리도록 프롬프트 단계에서 못박는다. 파일 범위가 안 겹치니 두 에이전트가 진짜로 “동시에” 일해도 충돌이 안 난다.

2. Backend와 Frontend는 반드시 같은 지점(main)에서 분기한다. 한쪽이 다른 쪽 위에서 시작하면 그 순간부터 서로에게 의존하게 되어 병렬 구조가 깨진다. 완전히 독립적으로 시작해야 서로 기다릴 이유가 없다.

3. Integration은 합치기만 하고, 절대 구현 코드를 직접 고치지 않는다. merge 충돌이나 테스트 실패가 나도 Integration이 그 자리에서 땜질하지 않고, 담당 역할의 브랜치로 돌려보낸다. 이 규칙이 없으면 나중에 “이 코드를 누가 짰는지” 추적이 끊긴다.

4. main이 바뀌는 시점은 오직 사람이 최종 merge를 실행하는 순간뿐이다. 에이전트가 아무리 여러 브랜치에 커밋을 쌓아도, 사람이 검토하고 merge하기 전까지는 기준이 되는 브랜치는 그대로다. 승인 문서(approval_draft.json)의 상태값도 항상 pending으로 못박아, 에이전트가 스스로 “승인됨”으로 바꿔치기하는 걸 규칙으로 막는다.

실제로 이렇게 흘러갔다

말로 설명한 구조가 실제 커밋 그래프로 어떻게 나타났는지는 아래와 같다.

실제 커밋 그래프 — main에서 backend-branch와 frontend-branch가 동시에 갈라지고, integration-branch가 backend-branch 위에서 frontend-branch를 merge한 뒤, 최종적으로 사람이 그 결과를 main으로 merge한다

backend-branchfrontend-branch는 같은 시작점(a5e8cbf)에서 갈라져 각자 구현 커밋 하나씩만 남겼다. integration-branchbackend-branch의 최신 커밋 위에서 만들어져 frontend-branch를 merge하고, 검증 결과를 담은 문서 커밋을 하나 더 남겼다. 그리고 이 모든 과정이 끝난 뒤에야 사람이 integration-branchmain으로 merge했다. 이 시점 전까지 main은 시작 커밋 그대로였다.

무엇을 근거로 신뢰하는가

에이전트가 여러 개 동시에 돌아가면 “다 됐다”는 보고를 그대로 믿을 수는 없다. 이 구조는 신뢰의 근거를 사람의 말이 아니라 확인 가능한 것으로 바꾼다.

대신 믿는 것안 믿는 것
changed files, git diff에이전트가 스스로 요약한 “무엇을 했다”
terminal의 테스트 실행 결과”테스트 통과했습니다”라는 주장
git branch와 commit 이력대화창에 남은 설명
저장소 안의 report 문서저장소 밖 어딘가에 따로 남긴 메모

언제 이 구조가 필요한가

상황더 맞는 선택이유
여러 에이전트가 겹치지 않는 파일 범위를 동시에 맡는다worktree + branch 분리물리적 격리 없이는 서로의 미완성 코드를 보게 되고, 실수로 남의 파일을 건드릴 위험도 그대로 남는다
에이전트 하나가 순서대로 작업한다그냥 한 브랜치에서 진행병렬로 얻을 이득이 없는데 worktree를 여러 개 만들면 관리할 폴더와 브랜치만 늘어난다
역할 간 파일 범위가 실제로는 겹친다작업을 먼저 더 잘게 쪼개거나 순차 진행파일이 겹치면 이 구조의 핵심 전제(충돌 없는 병렬)가 깨진다

worktree 폴더는 병합이 끝나면 지워도 된다. 실제 작업 결과는 이미 branch와 commit에 남아 있어서, git loggit show backend-branch:server.py처럼 언제든 다시 꺼내볼 수 있다.

한 줄 요약

역할을 나누고(파일 범위 분리) → 격리된 worktree에서 병렬로 구현하고 → 통합 브랜치에서 merge와 검증까지 그 자리에서 끝내고 → 사람이 마지막에 merge한다. 핵심은 개별 git 명령이 아니라, “누가 무엇을 언제 만질 수 있는가”를 물리적 폴더와 브랜치 경계로 미리 정해두는 것이다.

관련 글

← 목록으로