git worktree와 Claude Code로 여러 갈래를 동시에 누리미디어 테크블로그

git worktree와 Claude Code로 여러 갈래를 동시에

누리미디어 테크블로그

about 1

AI 에이전트가 서로 부딪히지 않는 1인 개발 루프 : git worktree와 Claude Code로 여러 갈래를 동시에

제목없음

about 1

신규 기능을 한창 짜고 있는데 운영에서 핫픽스 요청이 들어옵니다. 손에 쥔 변경은 아직 커밋할 단계가 아니어서 git stash로 일단 치워 둡니다.핫픽스 브랜치로 갈아타는 순간 편집기에 열어 둔 파일이 통째로 바뀌고, 돌려놓던 개발 서버가 흔들리고, 빌드 캐시가 어긋나죠. 핫픽스를 끝내고 돌아와 stash pop을 하면 충돌이 기다리고 있습니다.머릿속에 펼쳐 두었던 작업 지도가 그사이 한 번 접혔다 펴진 셈이에요. 여기에 AI 에이전트까지 끼면 통증은 더 커집니다.빨라진 김에 Claude Code를 두 개 띄워 서로 다른 일을 시켰더니, 둘이 같은 폴더에서 같은 파일을 동시에 고치며 서로의 변경을 덮어쓰고 있습니다.거기에 한쪽이 커밋하는 사이 다른 쪽이 작업 트리를 바꾸면 추적이 불가능해지는 문제도 발생하죠. 저는 이 통증을 세 갈래로 정리했습니다. 그리고 도구를 층층이 쌓아 하나씩 끊어 나갔어요.이 글은 그 과정을 정리한 기록입니다.

먼저 말씀드리면, ①과 ②는 git worktree와 Claude Code 두 가지만으로 이미 일상에서 껐습니다.③은 지금 다음 도구로 끄는 중이고요. 그 이야기는 글 끝에서 따로 다루겠습니다.

제목없음

제목없음

1층 ㅡ git stash 지옥에서 나가는 비상구

첫 번째 도구는 git에 이미 들어 있던 git worktree였습니다. clone이 저장소를 통째로 복사하는 일이라면, worktree는 같은 .git 데이터베이스를 공유하면서 작업 폴더만 하나 더 여는 기능이에요.같은 서재를 공유하되 책상만 하나 더 놓는 셈이죠. 커밋 이력·브랜치·원격 설정은 모두가 함께 보고, 작업 디렉토리와 현재 브랜치(HEAD)만 폴더별로 갈라집니다.그래서 브랜치를 갈아탈 일이 없어져요. 핫픽스가 들어오면 폴더를 하나 더 열면 그만입니다.

여기서 한 가지 오해를 먼저 풀어야 했습니다."워크트리를 푸시한다"거나 "워크트리를 병합한다"는 동작은 없어요. 푸시와 병합의 대상은 언제나 브랜치이고, 워크트리는 그 브랜치를 편집하는 입구일 뿐입니다.워크트리를 지워도(git worktree remove) 브랜치와 커밋은 그대로 남고 폴더만 사라지죠.이 한 줄을 이해하고 나니 운용이 단번에 직관적으로 바뀌었습니다. 이렇게 작업 파일이 폴더 단위로 완전히 갈라지면서 통증 ①(맥락 끊김)과 ②(파일 충돌)가 동시에 사라졌습니다. 한 가지 팁이 있어요.저는 워크트리를 추적 대상 폴더 안이 아니라 별도의 전용 폴더에 모으고, 루트 .gitignore로 통째로 제외합니다.그래야 중복 소스가 코드 검색(ripgrep 기반)을 오염시키지 않고, git status도 깨끗하게 유지되거든요.모노레포나 서브모듈 환경이라면 특히 효과가 큽니다.

제목없음

제목없음

2층 ㅡ 에이전트마다 독방을 줬습니다

폴더가 갈라지자 다음 수가 자연스럽게 보였습니다.격리된 폴더마다 Claude Code를 한 명씩 들여놓는 것이죠. 워크트리가 파일 충돌을 막아 주니, 에이전트는 옆 작업을 신경 쓰지 않고 한 갈래의 처음부터 끝까지를 마음 놓고 맡을 수 있어요.

그런데 막상 띄우려니 한 가지가 걸렸습니다.워크트리 폴더를 .gitignore로 빼 두었으니, "루트에서 시작한 Claude가 이 코드를 제대로 다룰 수 있나? IDE 소스 제어 패널에 변경점이 뜨긴 할까?" 하는 의심이었죠.결론부터 말하면 다룰 수 있습니다. 다만 딱 한 곳, 에이전트의 '자동 탐색'에서만 워크트리가 빠져요. 그 빈틈을 운용 규칙으로 메우는 게 2층의 핵심입니다. (왜 자동 탐색에서만 빠지고 git·IDE는 멀쩡히 추적하는지는, 궁금하실 테니 바로 뒤 '막간'에서 따로 풀게요.)

모호하게 시키면, 엉뚱한 놈을 고칩니다

.gitignore로 빠진 워크트리는 Claude의 자동 탐색(Grep·Glob)에서 건너뛰어집니다.그래서 "로그인 버그 고쳐줘"처럼 모호하게 시키면, 에이전트가 워크트리 사본을 못 찾고 엉뚱하게 메인 체크아웃을 고쳐 버릴 수 있어요.무시당하는 게 아니라 엉뚱한 사본을 고치는 것 — 조용해서 더 위험한 함정이죠. 이 빈틈은 개인 환경 설정이 아니라 git에 커밋되는 공유 규약으로 메웠습니다.팀원마다 다른 개인 설정에 의존하면 공유가 안 되니까요. 세션은 항상 워크스페이스 루트에서 시작합니다. 그래야 설계 문서와 워크트리를 권한 마찰 없이 함께 탐색할 수 있어요. 작업 대상 경로를 항상 명시합니다. 탐색은 rg --no-ignore … , 변경 확인은 git -C diff로 못 박습니다. 워크트리마다 .work-context.md 앵커 파일을 둡니다. 세션이 길어져 맥락을 잊어도 이 파일만 다시 읽으면 대상·브랜치·관련 문서가 복원돼요. 커밋은 사람이 승인합니다. 에이전트가 변경점을 보고하면, IDE로 워크트리 폴더를 열어 확인한 뒤 커밋합니다. 이 규약을 Claude Code가 세션 시작 시 읽는 CLAUDE.md(프로젝트 메모리 파일)에 적어 두니, 모든 세션이 시작과 동시에 같은 규칙 위에서 움직이더라고요.

도구 둘로, 통증 둘 OUT

git worktree와 Claude Code, 딱 두 가지만으로 통증 ①과 ②가 사라졌습니다. 같은 모듈의 여러 브랜치를 동시에 열어 두고 각각 수정해도 충돌이 없고, 한 에이전트가 한 갈래를 격리된 폴더에서 처음부터 끝까지 완성합니다.커밋 이력은 깔끔하게 남고, 변경은 언제든 git diff로 검토할 수 있어요. 무엇보다 worktree와 Claude Code는 모든 OS에서 똑같이 동작하니, 윈도우든 리눅스든 그대로 쓸 수 있다는 점이 좋았습니다.여기까지가 제가 매일 쓰는, 검증이 끝난 일상입니다.

제목없음

제목없음

막간 ㅡ 분명 .gitignore 했는데, 왜 다 보이죠?

여기서 잠깐, 아까 미뤄 둔 궁금증을 풀고 갈게요. 여기서 잠깐, 아까 미뤄 둔 궁금증을 풀고 갈게요..gitignore로 무시한 폴더인데, 어째서 git diff도 IDE 소스 제어 패널도 변경을 멀쩡히 잡아낼까요? 직접 파 보고 나서야 오해가 풀렸습니다. .gitignore(추적할 파일 거르기)와 git의 '저장소 발견'(이 폴더가 어느 저장소인가)은 애초에 별개의 일이었거든요.워크트리가 추적되는 건 두 번째 덕분이고, 루트의 ignore 규칙은 첫 번째에만 작용합니다. 서로 만날 일이 없어요. 비밀은 워크트리 폴더 안의 .git에 있습니다.보통 저장소의 .git은 디렉토리지만, 워크트리의 .git은 자기 git 디렉토리를 가리키는 한 줄짜리 텍스트 파일(gitfile)이에요. 열어 보면 포인터 한 줄이 전부입니다.

git 클라이언트는 폴더에서 위로 거슬러 올라가며 .git을 찾고, 이 포인터를 따라가 워크트리 자신의 저장소를 발견합니다. 이 '발견' 단계에 루트의 .gitignore는 끼어들 자리가 없어요.제가 쓰는 환경은 서브모듈이라 포인터가 .git/modules//worktrees/로 한 번 더 꺾이는데, "하위 모듈의 .git에 저장된 worktree를 기반으로 동작한다"고 느꼈던 게 정확히 이 구조였습니다. 핵심은 한 줄로 정리돼요. 저장소가 둘이면 변경을 적는 장부(index)도 둘입니다.루트의 ignore는 루트 장부만 거를 뿐, 워크트리 장부가 제 변경을 추적하는 것까지는 막지 못해요. ignore는 저장소 경계를 넘지 못하거든요. 그러니 ignore가 실제로 막는 건 에이전트의 자동 탐색뿐이었습니다.스스로 파일을 찾아 나서는 Grep·Glob은 무시된 경로를 건너뛰지만, 경로를 직접 짚는 Read·Edit이나 git -C diff 같은 명시 작업은 gitignore와 무관하게 잘 돌아가요. IDE도 그래서 멀쩡합니다.VS Code는 1.103 버전부터 이 포인터 파일을 알아보고 워크트리를 별도 저장소로 띄워 '워크트리' 배지까지 달아 주거든요(git.detectWorktrees).이런 동작 방식 덕에 git ignored 폴더임에도 소스 제어 패널에 변경점이 표시되고, 사람의 눈으로 리뷰할 수 있는 상태가 됩니다.

제목없음

제목없음

다음 걸음 ㅡ 이쯤 되면 지휘자가 필요하다 (도입 준비 중)

2층까지 오면 한 갈래는 깔끔하게 굴러갑니다. 하지만 운용이 단선적이에요. A를 끝낼 때까지 B를 시작하기 어렵고, 각 작업이 어디까지 갔는지 폴더마다 들어가 확인해야 합니다.통증 ③(길 잃음)이 그대로 남아 있죠. 그래서 지금 마지막 한 층을 준비하고 있습니다.cmux(macOS용 터미널 멀티플렉서, 여러 터미널 화면을 한 창에서 분할·제어하는 앱)입니다.tmux처럼 화면을 나누되, cmux CLI로 다른 화면을 외부에서 조종할 수 있다는 점이 끌렸어요. 아직 전면 도입은 안 했습니다.Claude Code 환경에서 cmux surface(화면 표면)를 열고 연동이 되는지 테스트한 정도예요.다만 리서치와 손풀기를 하면서 그림은 충분히 그려졌습니다. 작업마다 전용 작업 공간에 Claude를 띄우면 어느 갈래가 무엇을 하는지 사이드바에서 한눈에 들어오고,cmux semaphore(신호)로 "구현이 끝나면 → 테스트를 시작"하는 파이프라인까지 자동으로 묶을 수 있겠더라고요. 여기서 미리 새긴 원칙이 하나 있어요.병렬화의 성패는 8할이 작업 분해에서 갈립니다.서로 다른 파일 영역을 건드리는 작업끼리 묶으면 안전하지만, 같은 파일을 두 갈래가 동시에 고치면 병합 충돌이 이득을 다 까먹어요. 순서 의존이 강한 일은 차라리 직렬로 묶는 편이 낫습니다.이건 cmux 없이 워크트리를 여러 개 굴릴 때도 그대로 통하는 원칙이라, 지금부터 지키고 있습니다.

제목없음

제목없음

값비싼 교훈 ㅡ 리서치가 세 번 거짓말을 했다

cmux를 리서치하면서 가장 값진 교훈은 도구 자체가 아니라 검증 태도였습니다.처음에 모아 둔 자료가 세 군데나 틀렸거든요.

자료는 cmux new 명령으로 워크트리를 만든다고 했지만, 실제로는 그런 명령 자체가 없었습니다(Unknown command 'new'). 설정이 ~/.cmux/에 저장된다고 했지만, 그 폴더는 존재하지 않았어요. 상태는 ~/.cmuxterm/에 있었습니다. 팀 모드가 --teammate-mode 플래그로 켜진다고 했지만, 그런 플래그는 없었습니다. 환경변수로 제어되고 있었죠. 그래서 핵심 주장은 전부 로컬에서 직접 두드려 확인하고, 문서마다 ✅(로컬 검증)·📄(공식 문서)·⚠️(교정됨) 배지를 붙여 어디까지 믿을 수 있는지 투명하게 남겼습니다. AI가 빠르게 끌어다 준 자료일수록 실행으로 한 번 더 검증해야 한다는 점, 이번에 제대로 새겼어요.

제목없음

제목없음

격리·실행·지휘, 셋이 안 싸우는 이유

길게 돌아온 이야기를 한 문장으로 줄이면 이렇습니다.

예시

격리·실행·지휘. 세 도구의 역할이 한 군데도 겹치지 않기 때문에, 포개어 써도 머릿속이 복잡해지지 않아요.오케스트라에 비유하면 worktree는 연주자마다 따로 마련한 방, Claude Code는 그 방 안에서 실제로 연주하는 연주자, cmux는 전체를 내려다보는 지휘대입니다. 처음부터 셋을 다 갖출 필요는 없습니다.저는 worktree와 Claude Code, 두 가지만으로 이미 브랜치 전쟁과 파일 충돌에서 벗어났어요.한 화면 지휘대(cmux)는 갈래가 더 늘어났을 때를 위한 다음 걸음이고, 지금 차근차근 검증하며 들이는 중입니다. 혼자서 여러 갈래를 동시에 굴리는 일이 더 이상 곡예처럼 느껴지지 않습니다.통증 두 개가 사라진 자리에, 에이전트들이 각자의 방에서 조용히 일하는 풍경이 남았어요.세 번째 통증도 머지않아 같은 자리에 놓일 겁니다. 읽어주셔서 감사합니다. (E.O.D)

제목없음

people

채용 공고 보러가기 ↗︎

img

Created by