coredot.today
에이전트 여럿, 저장소 하나 1편 — 폴더를 동기화하지 말고 Git으로 주고받기
블로그로 돌아가기
Gitgit worktreeAI 에이전트Claude CodeCodex병렬 개발SyncthingiCloud Drive폴더 동기화index.lockgitignore

에이전트 여럿, 저장소 하나 1편 — 폴더를 동기화하지 말고 Git으로 주고받기

Claude Code와 Codex를 동시에 여러 개 돌리고, 맥북과 사무실 Mac을 오가며 같은 프로젝트를 만지기 시작하면 가장 먼저 떠오르는 방법이 '저장소 폴더를 동기화하자'입니다. 이 글은 그 방법이 왜 조용히 작업을 잃게 만드는지 직접 재현한 실험으로 보여 주고, 원격 하나 → Mac마다 clone → 작업마다 worktree로 이어지는 전체 설계도와 세 원칙, 그리고 소스·비밀·대용량 파일·빌드 산출물을 각각 어디에 둘지 정리합니다.

코어닷투데이2026-10-0773분

에이전트 여럿, 저장소 하나 1편크게 보기

들어가며: 에이전트가 둘, Mac이 둘이 된 민지

「Git, 이것만 알면 협업한다」 시리즈에서 민지는 동네 카페 웹사이트 cafe-menu를 만들며 git을 익혔고, 마지막에는 Claude Code에게 가을 메뉴 페이지를 맡겨 보기까지 했습니다. 그 뒤로 몇 달이 지나면서 cafe-menu는 주문까지 받는 작은 웹앱으로 자랐습니다. index.html과 menu.md 옆에 src/order.js와 package.json이 생겼고, 결제 테스트용 API 키가 든 .env도 생겼습니다.

일하는 방식도 달라졌습니다. 민지는 이제 에이전트를 하나가 아니라 여럿 씁니다. 한 터미널에서는 Claude Code에게 메뉴 검색 기능을 맡기고, 다른 터미널에서는 Codex에게 쿠폰 기능을 맡깁니다. 그러다 퇴근할 때는 맥북을 들고 나가고, 다음 날 아침에는 사무실의 맥 스튜디오에서 이어서 일합니다. 개발자 도윤도 자기 Mac에서 같은 저장소를 만집니다.

이쯤 되면 누구나 한 번은 이런 생각을 합니다.

"맥북과 맥 스튜디오의 cafe-menu 폴더를 iCloud Drive나 Syncthing으로 동기화해 두면, 어디서든 하던 그대로 이어서 할 수 있지 않을까?"

편해 보이지만, 이 생각이 이 시리즈가 가장 먼저 말리고 싶은 방법입니다. 이 글의 결론을 한 문장으로 먼저 적어 둡니다.

저장소 폴더를 동기화하지 말고, Git으로 주고받습니다.

이번 1편은 시리즈 전체의 설계도입니다. 실제로 겪은 사고 하나로 시작해서, 세 원칙과 전체 구조를 그림으로 보고, 폴더 동기화가 왜 위험한지 직접 재현한 실험으로 확인한 뒤, 파일 종류마다 어디에 두어야 하는지 정리합니다. 구체적인 명령과 도구는 2편부터 한 편씩 깊게 다룹니다.

📌
이 글의 기준 — 2026년 9월 기준입니다. git 출력은 모두 git 2.55에서 직접 실행한 결과이며, 읽기 쉽게 영어 출력(LC_ALL=C)으로 통일했습니다. "두 Mac"은 한 컴퓨터 안에서 같은 가짜 원격(git init --bare)을 clone한 두 폴더로 재현했고, 출력에 찍힌 임시 경로는 https://github.com/minji/cafe-menu.git과 /Users/minji/work로 바꿔 적었습니다. 해시와 출력 구조는 손대지 않았습니다.

1. 실제로 겪은 일: 한 폴더에서 세 세션이 동시에 일한 날

먼저 남의 이야기가 아닌 사례부터 보겠습니다. 지금 읽고 계신 이 블로그를 만드는 저장소에서 실제로 있었던 일입니다.

2026년 9월, 이 블로그는 새 글을 빠르게 내기 위해 Claude Code 세션을 여러 개 동시에 띄워, 세션마다 글 한 편과 그 글의 인터랙티브 위젯을 맡겼습니다. 문제는 모든 세션이 같은 폴더 하나에서 일했다는 점입니다. 사람이 보기에는 "글이 다르니 파일도 다르고, 그러니 괜찮겠지"였습니다. 실제로는 세 군데에서 부딪혔습니다.

🧺
① 미추적 파일이 뒤섞였다
한 세션에서 git status를 치면, 다른 세션이 아직 쓰고 있는 위젯 파일과 이미지가 함께 "Untracked files"로 떴습니다. 어느 파일이 누구 것인지 git은 알려 주지 않습니다. 저장소에는 예전에 git add .로 모든 파일을 한 번에 담는 자동 커밋 스크립트가 12MB짜리 스크린샷, 17MB짜리 PPTX, 빌드 로그까지 쓸어 담은 전례도 있었습니다. 한 폴더를 여럿이 쓰면 "내 것만 커밋하기"가 사람의 주의력에 맡겨집니다.
🔒
② 개발 서버의 잠금 파일이 부딪혔다
한 세션이 글이 제대로 보이는지 확인하려고 개발 서버(next dev)를 띄워 두자, 다른 세션은 같은 폴더에서 두 번째 개발 서버를 띄우지 못했습니다. Next.js가 빌드 폴더 안에 .next/dev/lock이라는 잠금 파일을 만들어 "이 폴더의 개발 서버는 이미 돌고 있다"고 표시하기 때문입니다. 게다가 먼저 떠 있던 서버는 다른 세션이 새로 만든 글을 몰랐습니다.
📋
③ 자동 생성되는 공용 파일 하나가 모두의 변경을 빨아들였다
이 블로그는 위젯 폴더를 훑어 "위젯 목록" 파일 하나를 자동으로 만듭니다. 한 세션이 이 목록을 다시 만들면, 다른 세션이 아직 커밋하지 않은 위젯까지 전부 목록에 들어갔습니다. 결국 커밋할 때마다 그 파일에서 자기 줄만 골라 스테이징하는 수작업이 필요했습니다.

세 문제의 뿌리는 같습니다. 작업은 여럿인데 작업 폴더가 하나였습니다. 이 셋은 모두 worktree로 막을 수 있었습니다. worktree(워크트리)는 저장소 하나에서 작업 폴더를 여러 개 꺼내 쓰는 git 기능입니다(기존 시리즈 7편에서 짧게 소개했고, 2편에서 제대로 다룹니다).

  • 세션마다 자기 worktree가 있으면 git status에는 자기 작업만 보입니다.
  • .next/ 같은 빌드 폴더도 worktree마다 따로 생기므로 잠금 파일이 부딪히지 않습니다(포트만 다르게 주면 됩니다).
  • 자동 생성 파일도 worktree마다 한 부씩 있어서, 각 세션은 자기 위젯만 들어간 목록을 만들고 커밋합니다. 합치는 일은 나중에 브랜치를 머지할 때 git이 합니다.

실제로 그 뒤 이 저장소는 개발 서버 충돌을 피하려고 별도 worktree를 만들어 검증하기 시작했고, 지금은 .claude/worktrees/ 아래에 worktree가 따로 떠 있습니다. 한 폴더를 여럿이 쓰던 방식이 "작업마다 자기 책상" 방식으로 바뀐 것입니다.

한 폴더를 여러 에이전트가 함께 쓰며 파일이 뒤섞이는 모습크게 보기

이 사례는 Mac이 한 대일 때의 이야기입니다. 여기에 Mac이 두 대가 되고, 두 Mac의 폴더를 동기화 도구로 이어 붙이면 어떻게 될까요? 같은 문제가 이번에는 네트워크 너머로 번집니다. 그 이야기를 하기 전에, 이 시리즈가 제안하는 전체 그림부터 보겠습니다.


2. 한 줄 메시지와 세 원칙

이 시리즈 전체를 관통하는 원칙은 셋입니다. 외우기 쉽게 등호로 적습니다.

원칙 1
작업 1개 = worktree 1개 = 브랜치 1개 = 에이전트 세션 1개
"메뉴 검색"이라는 일을 맡기면, 그 일만을 위한 폴더(worktree)와 기록 갈래(브랜치)를 새로 만들고, 에이전트 세션 하나를 그 안에서만 돌립니다. 두 에이전트가 같은 폴더를 나눠 쓰지 않습니다.
원칙 2
Mac 사이 이동 = push / fetch
맥북에서 하던 일을 맥 스튜디오에서 이어 하려면, 맥북에서 커밋해 원격에 올리고(push), 맥 스튜디오에서 받아 옵니다(fetch). 폴더를 복사하거나 동기화하지 않습니다.
원칙 3
개발환경 = 저장소 안의 파일로 재현
"이 프로젝트는 Node 몇 버전으로 돌리고, 어떤 명령으로 테스트하며, 에이전트는 무엇을 지켜야 하나"를 mise.toml·AGENTS.md 같은 파일로 저장소에 넣어 둡니다. 새 Mac, 새 worktree, 새 에이전트가 clone만 하면 같은 환경을 다시 만들 수 있습니다.

세 원칙은 비유 하나로 묶입니다. 원격 저장소는 도서관의 원본 서가, 각 Mac의 clone은 집에 빌려 온 사본, worktree는 그 사본을 펼쳐 놓은 책상입니다. 책상은 일마다 하나씩 따로 씁니다(원칙 1). 집과 사무실 사이에는 책상을 통째로 옮기지 않고, 고친 내용을 원본 서가에 반납했다가 다시 빌려 옵니다(원칙 2). 그리고 어느 집에서든 같은 책상을 꾸릴 수 있게 "책상 차리는 법"을 책 맨 앞장에 적어 둡니다(원칙 3).

왜 이렇게까지 나누는지 한 번 더 짚어 둡니다. 사람이 혼자 일할 때는 한 폴더에서 브랜치를 바꿔 가며 일해도 큰 문제가 없었습니다. 한 번에 한 가지 일만 하니까요. 에이전트는 다릅니다. 여러 에이전트가 동시에, 빠르게, 사람이 보지 않는 사이에 파일을 바꿉니다. 한 폴더를 함께 쓰면 한 에이전트가 브랜치를 바꾸는 순간 다른 에이전트가 고치던 파일이 통째로 바뀌고, 한 에이전트의 테스트가 다른 에이전트의 반쯤 고친 코드 때문에 실패합니다. 원칙 1은 이런 간섭을 구조적으로 없애는 장치입니다.

원칙 3도 에이전트 때문에 더 중요해졌습니다. 사람은 "이 프로젝트는 Node 22로 돌려야 해" 같은 사실을 기억하지만, 새로 뜬 에이전트 세션은 매번 백지에서 시작합니다. 그 사실이 저장소 안의 파일에 적혀 있어야 에이전트도 읽습니다. 공식 문서 기준으로 Codex는 "작업을 하기 전에 AGENTS.md 파일을 읽고", Claude Code도 CLAUDE.md와 함께 저장소의 AGENTS.md를 읽을 수 있습니다. mise 문서는 프로젝트의 mise.toml이 "도구, 환경 변수, 작업(task)을 선언한다"고 설명합니다. 이 파일들을 어떻게 쓰는지는 6편에서 자세히 다룹니다.


3. 전체 설계도: 원격 하나, Mac마다 clone, 작업마다 worktree

세 원칙을 그림 하나로 그리면 이렇습니다. 위에서 아래로 읽고, 마지막에 모든 화살표가 다시 원격으로 돌아온다는 점을 봐 주세요.

☁️ 원격 저장소 (정본은 딱 하나)
github.com/minji/cafe-menu.git
main + 작업 브랜치들 · PR · 리뷰 기록
↓ git clone (Mac마다 처음 한 번)
💻 맥북
~/work/cafe-menu
자기 .git · 자기 .env
🖥️ 맥 스튜디오
~/work/cafe-menu
자기 .git · 자기 .env
↓ git worktree add (작업마다 하나)
검색 기능
worktree + agent/search-claude
Claude Code 세션 1개
쿠폰 기능
worktree + agent/coupon-codex
Codex 세션 1개
영수증 기능
worktree + agent/receipt-claude
(맥 스튜디오) Claude Code 세션 1개
↓ commit → push → PR → 리뷰 → main에 머지
☁️ 다시 원격으로
모든 작업은 커밋이 되어 같은 정본으로 돌아온다

그림에서 중요한 것은 무엇이 연결되어 있지 않은가입니다.

  • 맥북 폴더와 맥 스튜디오 폴더 사이에는 선이 없습니다. 두 Mac은 서로를 직접 보지 않고, 원격만 봅니다.
  • worktree끼리도 선이 없습니다. 같은 Mac 안의 worktree들은 .git(기록 저장소)을 함께 쓰지만, 작업 폴더의 파일은 서로 독립입니다. git 공식 문서의 표현으로는 새 worktree가 "HEAD, index 같은 worktree별 파일을 뺀 모든 것을 공유"합니다.
  • .env, node_modules/, .next/는 그림 어디에서도 이동하지 않습니다. 각 폴더가 자기 것을 따로 가집니다(6장에서 정리합니다).

아래 탐색기에서 상자를 하나씩 눌러 보세요. 그곳에 무엇이 있고 무엇이 없는지, 어떤 git 명령으로 일이 드나드는지 볼 수 있습니다.

3-1. 이 흐름을 실제 명령으로

설계도가 추상적으로 느껴진다면 실제로 한 바퀴 돌려 보겠습니다. 맥북에서 검색 기능용 worktree를 만들고, 에이전트가 작업한 것처럼 파일 하나를 커밋해 push한 뒤, 맥 스튜디오에서 받아 이어 가는 흐름입니다. 앞에서 말한 대로 두 Mac은 같은 가짜 원격을 clone한 두 폴더로 재현했습니다.

bash
# 맥북 ~/work/cafe-menu 에서
git worktree add ../cafe-menu-search -b agent/search-claude
git worktree list
text
Preparing worktree (new branch 'agent/search-claude')
HEAD is now at aa0d5de Add matcha latte
/Users/minji/work/cafe-menu        aa0d5de [main]
/Users/minji/work/cafe-menu-search aa0d5de [agent/search-claude]

git worktree add는 옆 폴더 cafe-menu-search에 새 작업 폴더를 꺼내고, 그 폴더 전용 브랜치 agent/search-claude를 만듭니다. git worktree list를 보면 폴더 두 개가 서로 다른 브랜치를 체크아웃하고 있습니다. 이제 이 폴더에서 Claude Code를 띄우면, 그 에이전트는 이 폴더의 파일만 만집니다. 여기서는 에이전트 대신 파일 하나를 직접 만들어 커밋했습니다.

bash
cd ../cafe-menu-search
git add src/search.js
git commit -m "Add menu search helper"
git push -u origin agent/search-claude
text
[agent/search-claude 2a67a97] Add menu search helper
 1 file changed, 1 insertion(+)
 create mode 100644 src/search.js
To https://github.com/minji/cafe-menu.git
 * [new branch]      agent/search-claude -> agent/search-claude
branch 'agent/search-claude' set up to track 'origin/agent/search-claude'.

다음 날 아침, 맥 스튜디오에서는 받아 오기만 하면 됩니다.

bash
# 맥 스튜디오 ~/work/cafe-menu 에서
git fetch
git switch agent/search-claude
git log --oneline -2
text
From https://github.com/minji/cafe-menu
 * [new branch]      agent/search-claude -> origin/agent/search-claude
Switched to a new branch 'agent/search-claude'
branch 'agent/search-claude' set up to track 'origin/agent/search-claude'.
2a67a97 Add menu search helper
aa0d5de Add matcha latte

맥북에서 만든 커밋 2a67a97이 해시까지 똑같이 맥 스튜디오에 도착했습니다. 폴더를 복사한 것이 아니라 기록을 주고받았기 때문에, 두 Mac이 같은 커밋을 같은 이름으로 부릅니다. 이것이 원칙 2입니다. 맥 스튜디오에서도 main 폴더에서 직접 이어 가는 대신 worktree를 하나 더 만들어 이어 갈 수 있고, 그 방법과 "같은 브랜치를 두 Mac이 동시에 만질 때"의 규칙은 4편에서 다룹니다.

원격 하나에서 두 Mac과 여러 worktree로 이어지는 구조크게 보기


4. 왜 저장소 폴더를 동기화하면 안 되나: 직접 재현한 세 실험

이제 처음 질문으로 돌아갑니다. "폴더를 동기화하면 편하지 않나?" 말로 설명하기보다 직접 망가뜨려 보는 편이 빠릅니다.

4-1. 동기화 도구와 Git은 "같은 것"을 보지 않는다

먼저 두 도구가 무엇을 단위로 생각하는지 비교해 봅시다.

구분폴더 동기화 (Syncthing·iCloud Drive·Dropbox)Git (push / fetch)
옮기는 단위파일 하나하나. 바뀐 파일부터 차례로커밋. 한 번의 변경을 통째로, 해시로 확인하며
언제 옮기나파일이 바뀌는 즉시(작업이 끝났는지 모름)사람이나 에이전트가 "여기까지"라고 정했을 때
충돌이 나면한쪽이 이기거나, 이름 붙은 사본 파일이 생김push가 거절되고, 합칠 때 충돌을 파일 안에 보여 줌
.git 폴더를 보면그냥 수천 개의 작은 파일서로 맞물린 하나의 데이터베이스
실행 중인 작업잠금 파일·반쯤 쓴 파일까지 그대로 복사끝난 커밋만 옮김

핵심은 .git 폴더입니다. 이 폴더 안에는 커밋 내용(objects), "main은 지금 이 커밋"이라는 이름표(refs), 다음 커밋에 들어갈 목록(index), 이름표가 움직인 기록(logs)이 서로 맞물려 들어 있습니다. git은 이것들을 한 번에 일관되게 바꾸려고 무척 조심합니다. git 개발 문서(api-lockfile)는 파일을 바꿀 때 먼저 <파일이름>.lock을 만들어 거기에 새 내용을 쓰고, 다 쓰면 원래 이름으로 한 번에 바꿔치기(atomic rename) 한다고 설명합니다. 다른 git 프로세스가 이미 잠금 파일을 만들어 두었다면 그 사실을 알아채고 실패하도록 되어 있습니다.

동기화 도구는 이런 사정을 모릅니다. 파일 하나가 바뀌면 그 파일을 옮기고, 다른 파일이 바뀌면 또 그 파일을 옮깁니다. 그 순간이 git이 일하는 도중이든 아니든 상관없습니다. 그래서 아래 세 가지 일이 생깁니다.

4-2. 실험 ①: 커밋 도중에 복사된 폴더에는 잠금 파일이 남는다

첫 실험은 "커밋이 진행되는 도중에 동기화 도구가 폴더를 가져가면"입니다. 커밋은 보통 순식간에 끝나지만, 커밋 전에 린트나 테스트를 돌리는 pre-commit 훅(커밋 직전에 자동으로 실행되는 스크립트, 6편에서 다룹니다)이 있으면 몇 초씩 걸립니다. 에이전트가 커밋할 때도 똑같습니다. 여기서는 5초 걸리는 훅을 만들어 그 상황을 재현했습니다.

bash
# 맥북: 5초 걸리는 pre-commit 훅 (린트가 도는 상황 흉내)
printf '#!/bin/sh\nsleep 5\n' > .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

echo '- 말차라테 5,500원' >> menu.md
git commit -a -q -m "Add matcha latte" &   # 커밋 시작 (백그라운드)
sleep 1
ls -1 .git | grep lock                      # 커밋 도중 .git 안을 보면
text
index.lock

커밋이 진행되는 동안 .git/index.lock이 있습니다. git이 "지금 index를 고치는 중이니 다른 git은 손대지 마"라고 세워 둔 표지판입니다. 바로 이 1초 사이에 동기화 도구가 폴더를 가져갔다고 치고, cp -R로 폴더를 통째로 복사했습니다. 그리고 커밋이 끝나기를 기다렸다가 양쪽을 비교했습니다.

bash
cp -R ~/work/cafe-menu ~/studio-synced/cafe-menu   # "동기화"가 이 순간 폴더를 가져감
sleep 6

# 맥북 원본
ls -1 .git | grep lock
git log --oneline -1
text
aa0d5de Add matcha latte

맥북 원본에서는 커밋이 무사히 끝났고, 잠금 파일도 git이 스스로 지웠습니다(grep 결과가 비어 있습니다). 이제 복사본을 봅니다.

bash
# 동기화된 사본
cd ~/studio-synced/cafe-menu
ls -1 .git | grep lock
git log --oneline -1
git status --short
text
index.lock
b947708 Add cold brew
 M menu.md

사본에는 세 가지가 어긋나 있습니다.

  1. 잠금 파일이 남았습니다. 원본의 git은 일을 마치고 자기 잠금 파일을 지웠지만, 사본에 옮겨진 잠금 파일은 지울 프로세스가 없습니다. api-lockfile 문서에 따르면 git은 프로그램이 끝나거나 중단될 때 자기가 만든 잠금 파일을 치우도록 되어 있는데, 그 정리는 잠금 파일을 만든 그 컴퓨터의 그 프로세스만 할 수 있습니다.
  2. 최신 커밋이 없습니다. 사본의 마지막 커밋은 aa0d5de(말차라테)가 아니라 그 이전인 b947708입니다.
  3. 이미 커밋된 수정이 "아직 안 한 수정"으로 보입니다. 원본에서는 커밋으로 끝난 말차라테 한 줄이, 사본에서는 M menu.md(수정했지만 커밋 안 함)로 보입니다.

이 사본에서 맥 스튜디오의 에이전트가 아이스티 메뉴를 추가하고 스테이징하려 하면 이렇게 됩니다.

bash
echo '- 아이스티 4,000원' >> menu.md
git add menu.md
text
fatal: Unable to create '/Users/minji/studio-synced/cafe-menu/.git/index.lock': File exists.

Another git process seems to be running in this repository, or the lock file may be stale

"다른 git 프로세스가 이 저장소에서 돌고 있는 것 같다, 아니면 잠금 파일이 오래되어 남은 것일 수 있다." 맥 스튜디오에는 git 프로세스가 하나도 없는데도 말입니다. 인터넷에서 이 오류를 검색하면 거의 모든 답이 "index.lock을 지우라"고 합니다. 한 컴퓨터에서 git이 비정상 종료해서 남은 잠금 파일이라면 맞는 처방입니다. 하지만 동기화된 폴더라면 지금 다른 Mac에서 정말로 git이 돌고 있을 수도 있습니다. 그 상태에서 잠금 파일을 지우고 작업하면, 두 Mac이 같은 index를 동시에 고치게 됩니다. 잠금이라는 안전장치가 동기화 때문에 "진짜 경고인지 가짜 경고인지 알 수 없는 신호"로 바뀐 것입니다.

⚠️
이 실험은 실제 동기화 도구가 아니라 cp -R로 한 순간을 복사한 재현입니다. 실제 동기화 도구는 한 순간을 통째로 복사하는 것이 아니라 파일을 하나씩 다른 시각에 옮기므로, 사본은 이보다 더 뒤죽박죽이 될 수 있습니다. 예를 들어 새 커밋 객체는 도착했는데 main 이름표는 아직 옛날 것이거나, 그 반대일 수 있습니다.

4-3. 실험 ②: 두 Mac이 각자 커밋하면, 한쪽 커밋이 조용히 사라진다

두 번째 실험은 더 흔한 상황입니다. 두 Mac이 동기화된 폴더를 쓰는데, 잠깐 동기화가 끊긴 사이(비행기 안, 카페 와이파이, 동기화 앱이 잠시 멈춤) 양쪽에서 각자 커밋을 한 경우입니다. 에이전트를 여러 대 돌리면 이런 순간은 반드시 옵니다.

동기화 도구가 "같은 파일이 양쪽에서 바뀌었을 때" 하는 일은 크게 두 가지입니다. 나중에 쓴 쪽이 이기거나, 양쪽을 다 남기되 한쪽 이름을 바꾸거나. 먼저 "나중에 쓴 쪽이 이기는" 경우를 rsync -au(파일마다 수정 시각이 더 새로운 쪽으로 맞춤)로 흉내 냈습니다.

bash
# 동기화가 맞춰 둔 두 폴더 (내용 동일)
cp -R cafe-menu sync-a    # 맥북 쪽
cp -R cafe-menu sync-b    # 맥 스튜디오 쪽

cd sync-a   # 맥북: 호지차라테 추가
echo '- 호지차라테 5,500원' >> menu.md
git commit -qam "Add hojicha latte (macbook)"
git log --oneline -1

sleep 2
cd ../sync-b   # 맥 스튜디오: 조금 뒤 딸기라테 추가
echo '- 딸기라테 5,500원' >> menu.md
git commit -qam "Add strawberry latte (studio)"
git log --oneline -1
text
650add2 Add hojicha latte (macbook)
2b138a7 Add strawberry latte (studio)

이제 동기화가 다시 연결되어 양방향으로 맞춰졌다고 칩니다.

bash
rsync -au sync-b/ sync-a/
rsync -au sync-a/ sync-b/

cd sync-a   # 맥북에서 확인
git log --oneline -3
git status --short
tail -2 menu.md
text
2b138a7 Add strawberry latte (studio)
aa0d5de Add matcha latte
b947708 Add cold brew
- 말차라테 5,500원
- 딸기라테 5,500원

맥북의 git status는 깨끗합니다. 오류도, 경고도, 충돌 표시도 없습니다. 그런데 맥북에서 방금 만든 호지차라테 커밋이 기록에서 사라졌고, menu.md에서도 호지차라테 줄이 없어졌습니다. .git/refs/heads/main이라는 작은 파일(내용은 커밋 해시 한 줄)이 맥 스튜디오 쪽의 더 새 파일로 덮였고, 작업 폴더의 menu.md도, 이름표가 움직인 기록(reflog)도 똑같이 덮였기 때문입니다.

커밋 자체가 완전히 지워진 것은 아닙니다. 먼저 reflog부터 봅니다.

bash
git reflog | head -3
text
2b138a7 HEAD@{0}: commit: Add strawberry latte (studio)
aa0d5de HEAD@{1}: commit: Add matcha latte
b947708 HEAD@{2}: commit: Add cold brew

평소라면 사라진 커밋을 되찾을 때 가장 먼저 보는 곳이 reflog인데(기존 시리즈 5편), 여기에도 호지차라테 커밋이 없습니다. reflog 파일까지 맥 스튜디오 것으로 덮였기 때문입니다. 남은 방법은 git 데이터베이스를 직접 검사하는 git fsck입니다.

bash
git fsck --no-reflogs | grep dangling
git show --stat --oneline 650add2 | head -3
text
dangling commit 650add24b72a2d1534f9611f459c4cb10f41b5aa
650add2 Add hojicha latte (macbook)
 menu.md | 1 +
 1 file changed, 1 insertion(+)

dangling commit은 "아무 브랜치도 가리키지 않는, 주인 잃은 커밋"이라는 뜻입니다. 이 명령을 알고, 무언가 사라졌다는 사실을 눈치챈 사람만 되살릴 수 있습니다. 에이전트가 밤새 만든 커밋 열 개가 이렇게 사라졌다면, 아침에 그걸 알아챌 방법이 마땅치 않습니다. 잃어버렸는데 잃어버린 줄도 모르는 것, 이것이 폴더 동기화의 가장 나쁜 실패입니다.

4-4. 충돌 사본을 남기는 도구라면: git이 이상한 브랜치를 본다

"우리 동기화 도구는 덮어쓰지 않고 충돌 사본을 남겨 준다"면 괜찮을까요? Syncthing 공식 문서에 따르면, 양쪽에서 동시에 바뀐 파일이 있으면 수정 시각이 더 오래된 쪽을 <파일이름>.sync-conflict-<날짜>-<시각>-<수정한 기기>.<확장자> 이름으로 바꿔 남기고, 이 충돌 사본도 보통 파일처럼 모든 기기로 퍼집니다. Dropbox도 비슷하게 원래 파일 이름에 편집한 사람 이름, "conflicted copy", 날짜를 붙인 사본을 만든다고 도움말에 적혀 있습니다.

일반 문서라면 사람이 두 사본을 열어 보고 합치면 됩니다. 그런데 그 파일이 .git/refs/heads/main이라면 어떻게 될까요? Syncthing 문서의 이름 규칙대로 충돌 사본 파일을 손으로 만들어 넣고(날짜·기기 부분은 예시 값), git이 이것을 어떻게 읽는지 확인했습니다.

bash
git rev-parse 650add2 > .git/refs/heads/main.sync-conflict-20261007-093015-7XK2QPL
git branch
git log --oneline --graph --all | head -5
text
* main
  main.sync-conflict-20261007-093015-7XK2QPL

* 2b138a7 Add strawberry latte (studio)
| * 650add2 Add hojicha latte (macbook)
|/  
* aa0d5de Add matcha latte
* b947708 Add cold brew

git은 refs/heads/ 아래 파일을 모두 브랜치로 읽으므로, 충돌 사본이 main.sync-conflict-…라는 브랜치로 나타납니다. 이번엔 커밋을 되찾을 실마리가 남은 셈이지만, 이 이상한 브랜치를 누가 어떻게 정리할지는 또 다른 문제입니다. 에이전트는 이 이름을 보고 "누군가 만든 브랜치"로 여길 수 있고, git push --all 같은 명령을 쓰면 원격에까지 퍼집니다. 게다가 이런 사본이 생길 수 있는 파일은 이름표만이 아닙니다. index, packed-refs, logs/HEAD, config처럼 .git 안에서 자주 바뀌는 파일은 모두 후보입니다.

Syncthing을 만든 Jakob Borg(포럼 아이디 calmh)는 2016년 Syncthing 포럼의 "Syncthing으로 로컬 Git 저장소를 안정적으로 동기화할 수 있나?"라는 질문에 이렇게 답했습니다. "이 질문에 대한 답은 분명히 '아니요'입니다." 그러면서 저장소의 내부 동작을 잘 알고 조심하면 어떤 경우에는 될 수도 있지만 그건 "안정적"이라고 할 수 없다고 덧붙였습니다. 도구를 만든 사람이 직접 말리는 쓰임새인 셈입니다.

폴더 동기화에서는 파일이 부딪히고, Git에서는 커밋이 차례로 합쳐지는 모습크게 보기

4-5. 실험 ③: node_modules와 .next는 규모부터 다르다

세 번째는 실험이라기보다 측정입니다. 이 블로그 저장소에서 git이 추적하는 파일과, 설치·빌드로 생긴 폴더의 크기를 비교했습니다(2026년 9월 27일 측정).

bash
git ls-files | wc -l
du -sh node_modules .next
find node_modules -type f | wc -l
find .next -type f | wc -l
text
    2359
921M	node_modules
3.2G	.next
   59500
   62185
git이 추적하는 파일
2,359개
node_modules/
59,500개
.next/
62,185개

사람이 실제로 쓰고 고치는 파일은 2,359개인데, 설치와 빌드로 생긴 파일은 합쳐서 12만 개가 넘습니다. 폴더를 통째로 동기화하면 동기화 도구는 대부분의 시간을 다시 만들 수 있는 파일을 옮기는 데 씁니다. 특히 .next/는 개발 서버가 도는 동안 계속 새로 쓰이고, 1장에서 본 .next/dev/lock 같은 잠금 파일도 그 안에 있습니다. 한 Mac에서 개발 서버를 띄우면 다른 Mac의 폴더에도 "개발 서버가 돌고 있다"는 표지판이 복사되는 셈입니다.

node_modules/는 옮겨도 쓸 수 없는 경우가 있습니다. 같은 폴더 안을 보면 이렇습니다.

bash
ls node_modules/@next/ | grep swc
ls node_modules/@img
uname -m
text
swc-darwin-arm64
colour
sharp-darwin-arm64
sharp-libvips-darwin-arm64
arm64

이름에 darwin-arm64가 붙은 패키지는 Apple Silicon Mac용으로 컴파일된 실행 파일입니다. 설치할 때 npm이 그 컴퓨터의 CPU와 운영체제에 맞는 것을 골라 받습니다. 두 Mac이 모두 Apple Silicon이면 우연히 돌아갈 수도 있지만, Intel Mac이나 리눅스 서버, 에이전트용 컨테이너로 옮기면 맞지 않습니다. 그래서 공유할 것은 node_modules/ 자체가 아니라 무엇을 설치할지 적은 목록, 즉 package-lock.json입니다. 파이썬의 .venv/도 만든 컴퓨터의 파이썬 경로가 안에 박혀 있어 같은 이유로 공유하지 않습니다.

4-6. iCloud Drive와 Dropbox에만 있는 함정

Syncthing 말고도 Mac 사용자가 흔히 쓰는 동기화 도구에는 도구마다 고유한 함정이 하나씩 더 있습니다.

  • iCloud Drive — 파일이 "구름 속에만" 있을 수 있습니다. Apple 도움말 「Mac의 저장 공간 최적화」에 따르면, iCloud에 저장 옵션을 켜 두면 공간이 필요할 때 최근에 연 파일만 Mac에 남기고 나머지는 iCloud에만 두었다가 필요할 때 내려받습니다. 사람이 문서를 열 때는 편리한 기능이지만, git은 .git/objects 안의 수천 개 파일을 언제든 바로 읽을 수 있다고 가정합니다. 어떤 파일이 지금 Mac에 있고 어떤 파일이 구름 속에만 있는지를 git도 에이전트도 알지 못합니다. 문서·데스크탑 폴더를 iCloud로 동기화하는 설정을 켜 둔 채 그 아래에 저장소를 만들면, 모르는 사이에 이 상황이 됩니다.
  • Dropbox — 제외 설정이 폴더를 따라다니지 않습니다. Dropbox 도움말은 특정 폴더를 동기화에서 빼려면 터미널에서 확장 속성을 붙이라고 안내합니다(최신 File Provider 방식의 Dropbox는 com.apple.fileprovider.ignore#P, 예전 방식은 com.dropbox.ignored). 이 설정은 .gitignore처럼 저장소에 적힌 규칙이 아니라 그 폴더 자체에 붙는 표시입니다. 폴더에 붙은 표시이므로 node_modules/를 지우고 다시 설치하거나 새 worktree를 만들면, 새로 생긴 폴더에는 표시가 없어 다시 붙여야 합니다. 에이전트가 worktree를 수시로 만들고 지우는 방식과는 잘 맞지 않습니다.
🩹
".git만 빼고 동기화하면 되지 않나요?" — 작업 파일만 동기화하고 .git은 제외하는 방법도 거론됩니다. 그러면 기록 데이터베이스가 깨지는 문제는 피하지만, 두 Mac의 작업 폴더와 기록이 서로 어긋나는 새로운 문제가 생깁니다. 맥 스튜디오의 git status는 맥북에서 이미 커밋한 수정까지 "아직 커밋 안 한 수정"으로 보여 주고, 두 에이전트가 같은 파일을 동시에 고치면 결국 파일 단위 충돌로 돌아옵니다. 이 시리즈는 이 방법도 권하지 않습니다.

4-7. 같은 상황을 Git으로 겪으면

이제 실험 ②와 똑같은 상황을 Git 방식으로 겪어 보겠습니다. 두 Mac이 각자 clone을 가지고 있고, 맥북이 먼저 호지차라테를, 맥 스튜디오가 조금 뒤 딸기라테를 커밋합니다.

bash
# 맥북
git commit -am "Add hojicha latte"
git push
text
[main 860f707] Add hojicha latte
 1 file changed, 1 insertion(+)
To https://github.com/minji/cafe-menu.git
   aa0d5de..860f707  main -> main
bash
# 맥 스튜디오 (맥북이 올린 것을 아직 모름)
git commit -am "Add strawberry latte"
git push
text
[main ecb3443] Add strawberry latte
 1 file changed, 1 insertion(+)
To https://github.com/minji/cafe-menu.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/minji/cafe-menu.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

push가 거절됩니다. 원격에 "내가 모르는 작업"이 있으니 먼저 받아서 합치라는 뜻입니다. 동기화 도구와 달리 git은 남의 커밋을 덮어쓰지 않고 멈춥니다. 받아서 합쳐 봅니다.

bash
git pull --rebase
text
From https://github.com/minji/cafe-menu
   aa0d5de..860f707  main       -> origin/main
Rebasing (1/1)Auto-merging menu.md
CONFLICT (content): Merge conflict in menu.md
error: could not apply ecb3443... Add strawberry latte
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Could not apply ecb3443... # Add strawberry latte

두 Mac이 menu.md의 같은 자리(맨 끝)에 줄을 추가했으니 충돌이 나는 것이 당연합니다. 중요한 것은 git이 이 사실을 숨기지 않는다는 점입니다. menu.md를 열면 양쪽 내용이 모두 표시되어 있습니다.

text
- 말차라테 5,500원
<<<<<<< HEAD
- 호지차라테 5,500원
=======
- 딸기라테 5,500원
>>>>>>> ecb3443 (Add strawberry latte)

두 메뉴를 모두 남기도록 표시 줄만 지우고, 저장한 뒤 이어 갑니다(편집기가 열리면 메시지를 그대로 두고 닫습니다). 충돌 푸는 법이 낯설다면 기존 시리즈 5편을, rebase가 낯설다면 8편을 보세요.

bash
git add menu.md
git rebase --continue
git push
git log --oneline -3
text
[detached HEAD 2354b5f] Add strawberry latte
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/main.
To https://github.com/minji/cafe-menu.git
   860f707..2354b5f  main -> main
2354b5f Add strawberry latte
860f707 Add hojicha latte
aa0d5de Add matcha latte

호지차라테(860f707)와 딸기라테(2354b5f)가 둘 다 기록에 남았습니다. 폴더 동기화에서는 오류 없이 한쪽이 사라졌고, Git에서는 오류가 났지만 아무것도 사라지지 않았습니다. 시끄럽게 멈추는 도구가 조용히 잃어버리는 도구보다 안전합니다.

그리고 원칙 1을 지키면 이런 충돌 자체가 줄어듭니다. 두 에이전트가 main에 직접 커밋하지 않고 각자 agent/… 브랜치에서 일하면, push가 거절될 일이 없고 합치는 시점은 PR을 머지할 때로 모입니다. 사람이 리뷰하면서 한 번에 판단하면 됩니다.

아래 위젯에서 두 방식을 단계별로 비교해 보세요. 해시와 출력은 모두 위 실험에서 얻은 값입니다.


5. 그렇다고 Syncthing이 나쁜 도구는 아니다

오해하지 않도록 분명히 해 둡니다. Syncthing, iCloud Drive, Dropbox는 좋은 도구입니다. 문제는 도구가 아니라 쓰임새입니다. 동기화 도구는 "여러 곳에 같은 파일이 있으면 좋은 자료", 특히 한 번에 한 사람이 고치고, 파일 하나가 그 자체로 완결된 자료에 잘 맞습니다.

동기화 도구에 잘 맞는 것예시 (cafe-menu 기준)이유
데이터셋주문 로그 CSV 모음, 테스트용 샘플 데이터크고, 코드와 다른 속도로 바뀌고, 대개 추가만 됨
디자인 원본메뉴 사진 원본, 포스터 PSD·Figma 내보내기바이너리라 git diff가 의미 없음. 디자이너 한 명이 주로 고침
참고 문서납품업체 단가표 PDF, 회의록, 논문읽기가 대부분이고, 버전 기록보다 "어디서든 열림"이 중요
개인 메모할 일 목록, 에이전트에게 줄 프롬프트 초안나만 쓰고, 충돌 사본이 생겨도 사람이 바로 알아봄

실용적인 배치는 저장소 폴더와 동기화 폴더를 아예 따로 두는 것입니다. 예를 들어 저장소는 ~/work/cafe-menu(동기화하지 않는 곳)에 두고, 사진과 데이터는 ~/Sync/cafe-assets에 둔 뒤, 코드는 그 경로를 설정 파일(.env의 ASSETS_DIR 같은 값)로 읽습니다. 이렇게 하면 두 도구가 각자 잘하는 일만 합니다.

한 가지 더 기억할 점이 있습니다. Syncthing 공식 FAQ는 Syncthing이 좋은 백업 도구가 아니라고 분명히 말합니다. 수정과 삭제를 포함한 모든 변경이 모든 기기로 퍼지기 때문입니다. 한 Mac에서 실수로 폴더를 지우면 다른 Mac에서도 지워집니다. 동기화는 "같게 맞추기"이지 "지켜 두기"가 아닙니다. 저장소에서는 원격 저장소와 커밋 기록이 그 "지켜 두기" 역할을 합니다.


6. 무엇을 어디에 두나

이제 저장소 안팎의 파일을 네 칸으로 나눕니다. 이 표 하나가 1편의 실무 결론입니다.

종류예시어디에옮기는 방법
소스·설정·마이그레이션src/order.js, menu.md, package.json, package-lock.json, DB 마이그레이션, mise.toml, AGENTS.mdGitcommit → push → PR
비밀 값.env(API 키·DB 비밀번호)각 Mac의 로컬 파일 (.gitignore에 등록)옮기지 않음. 대신 값이 빈 .env.example을 Git에. 실제 값은 비밀번호 관리자 등으로 사람이 전달
대용량 데이터·모델model.bin(4GB), 메뉴 사진 원본, 데이터셋S3·MinIO 같은 객체 저장소, 또는 Git LFS저장소에는 주소·버전·체크섬만. LFS는 큰 파일을 따로 두고 포인터만 커밋
빌드 산출물·설치물node_modules/, .next/, .venv/, dist/, 캐시·로그어디에도 공유하지 않음 (.gitignore)각 폴더에서 다시 만듦: npm ci, npm run build 등

표를 짧게 풀어 봅니다.

소스와 설정은 Git으로. 사람이 쓰고 고치는 텍스트는 모두 여기입니다. package-lock.json처럼 기계가 만든 파일이라도 "모든 Mac이 같은 결과를 다시 만들기 위해 필요한 정보"라면 커밋합니다. 데이터베이스 구조를 바꾸는 마이그레이션 파일도 코드와 같은 커밋에 들어가야 "이 코드는 이 DB 구조를 기대한다"는 약속이 깨지지 않습니다.

비밀은 로컬에, 견본은 Git에. .env는 절대 커밋하지 않습니다. 한 번 커밋하면 나중에 지워도 기록에 남아 있어서, 저장소를 clone한 누구나 옛 커밋에서 꺼낼 수 있습니다. 대신 변수 이름만 적힌 .env.example을 커밋해서 "이 프로젝트를 돌리려면 이 값들이 필요하다"는 사실만 공유합니다. 새 Mac이나 새 에이전트는 이 견본을 보고 무엇을 채워야 할지 압니다. worktree를 새로 만들 때마다 .env를 복사하는 번거로움은 3편의 .worktreeinclude가 해결해 줍니다.

큰 파일은 전용 저장소로. git은 파일이 바뀔 때마다 새 버전을 통째로 기록에 쌓습니다. 4GB 모델이 몇 번 바뀌면 저장소가 수십 GB가 되고, 모든 Mac과 모든 에이전트 환경이 그것을 clone해야 합니다. 이 블로그 저장소도 한때 이미지와 PDF가 저장소를 크게 불려서, 기록을 다시 쓰고 대용량 파일을 별도 파일 서버로 옮기는 작업을 해야 했습니다. 큰 파일은 처음부터 객체 저장소에 두는 편이 훨씬 쌉니다.

산출물은 공유하지 않고 다시 만든다. 4-5절에서 본 것처럼 규모도 크고, CPU마다 다르고, 실행 중에 계속 바뀝니다. .gitignore에 넣고 각 폴더에서 다시 만듭니다. 이것은 worktree에도 그대로 적용됩니다. worktree는 새로 꺼낸 작업 폴더라 node_modules/가 비어 있으므로, worktree마다 설치를 한 번 해야 합니다(2편에서 설치 시간과 디스크를 아끼는 방법을 다룹니다).

실제 .gitignore가 이 규칙대로 동작하는지는 git check-ignore -v로 확인할 수 있습니다. 어떤 파일이 어느 줄 때문에 무시되는지 알려 줍니다.

bash
cat .gitignore
git status --short
git check-ignore -v .env node_modules/left-pad/index.js .next/cache/x
text
node_modules/
.next/
.env
?? .env.example
.gitignore:3:.env	.env
.gitignore:1:node_modules/	node_modules/left-pad/index.js
.gitignore:2:.next/	.next/cache/x

.env, node_modules/, .next/ 안의 파일은 모두 .gitignore의 해당 줄 때문에 무시되고, git status에는 커밋해야 할 새 파일 .env.example만 보입니다. 에이전트에게 일을 맡기기 전에 한 번 확인해 두면, 에이전트가 git add .를 하더라도 비밀과 산출물이 딸려 들어가지 않습니다.

아래 위젯에서 직접 분류해 보세요. 파일을 고르고 네 칸 중 하나를 누르면 이유가 나옵니다.


7. 첫날 정해 둘 약속 다섯 가지

설계도를 봤으니, 민지와 도윤이 cafe-menu에서 첫날 정한 약속을 정리해 봅니다. 도구가 바뀌어도 이 다섯 가지는 그대로 쓸 수 있습니다.

약속 1
저장소는 동기화 폴더 밖에 둔다. iCloud로 동기화되는 문서·데스크탑 폴더, Dropbox·Syncthing 폴더 아래에 저장소를 만들지 않습니다. 예: ~/work/.
약속 2
에이전트 한 명에게 worktree 하나. 새 작업을 맡길 때마다 worktree와 브랜치를 새로 만들고, 브랜치 이름에 누가 하는 일인지 적습니다. 예: agent/search-claude, agent/coupon-codex.
약속 3
Mac을 떠나기 전에 push. 끝나지 않은 일도 작업 중(WIP) 커밋으로 올립니다. 올리지 않은 커밋과 stash는 다른 Mac에서 보이지 않습니다(4편).
약속 4
main에는 PR로만 들어간다. 에이전트는 자기 브랜치까지만. 합치는 판단은 사람이 리뷰하며 합니다(5편·6편).
약속 5
환경은 파일로 적는다. 도구 버전은 mise.toml, 에이전트 규칙은 AGENTS.md, 필요한 비밀 목록은 .env.example. "내 Mac에서는 되는데"를 줄입니다(6편).

에이전트에게 일을 맡길 때도 이 약속을 말로 한 번 더 전하면 좋습니다. 예를 들면 이렇게요.

"지금 폴더는 agent/search-claude worktree야. 이 폴더 밖의 파일은 건드리지 말고, .env는 읽기만 해. 작업이 끝나면 이 브랜치에 커밋까지만 하고 push는 내가 확인한 뒤에 할게."

에이전트가 이 말을 잊지 않게 하는 장치(Claude Code의 worktree 격리, 권한 설정)는 3편에서, 저장소 파일로 규칙을 고정하는 방법은 6편에서 다룹니다.


8. 다음 편 미리보기

1편에서 전체 그림을 그렸으니, 이제 한 칸씩 채웁니다.

편무엇을 배우나
2편 worktree 완전 정복git worktree add / list / remove / prune / lock / move. worktree끼리 공유되는 것과 독립인 것, 같은 브랜치를 두 곳에서 체크아웃할 수 없는 이유, worktree마다 node_modules와 개발 서버 포트를 다루는 법
3편 에이전트에 worktree 맡기기Claude Code의 --worktree·.worktreeinclude·worktree.baseRef·서브에이전트 격리·정리 규칙, Codex의 worktree, Worktrunk(wt)로 만들고 합치고 치우기
4편 Mac을 바꿔 가며 이어서 작업하기WIP 커밋과 자주 push하기, 브랜치 이름 규칙, 다른 Mac에서 fetch+switch, stash가 Mac을 건너지 못하는 이유, 같은 브랜치를 두 Mac이 만질 때, Mac별 인증과 includeIf
5편 두 에이전트 경쟁 구현같은 일을 Claude Code와 Codex에게 따로 시키고 diff A...B·range-diff·테스트로 비교해 좋은 것만 합치기
6편 에이전트가 길을 잃지 않는 저장소AGENTS.md와 CLAUDE.md, mise.toml, .env.example과 비밀, .gitignore, Git LFS·S3, pre-commit 훅, CI와 main 보호
부록 도구 지도 2026GUI 관제 도구, GitButler, Jujutsu workspace, Forgejo 자체 호스팅까지. 언제 쓰고 언제 안 쓰나

치트시트: 1편에서 나온 명령

하고 싶은 일명령
Mac에 저장소를 처음 가져오기git clone https://github.com/minji/cafe-menu.git
작업 하나에 폴더·브랜치 하나 만들기git worktree add ../cafe-menu-search -b agent/search-claude
지금 있는 worktree 보기git worktree list
작업을 원격으로 올리기 (Mac 떠나기 전)git push -u origin agent/search-claude
다른 Mac에서 받아 이어 하기git fetch → git switch agent/search-claude
push가 거절됐을 때 받아서 합치기git pull --rebase → 충돌 풀기 → git add → git rebase --continue
어떤 파일이 왜 무시되는지 확인git check-ignore -v .env node_modules/…
주인 잃은 커밋 찾기 (비상시)git fsck --no-reflogs | grep dangling
잠금 파일 오류가 났을 때 먼저 할 일이 Mac·다른 Mac에서 git이 도는지 확인. 폴더가 동기화되고 있다면 동기화부터 멈추고 저장소를 동기화 폴더 밖으로 옮기기

자주 하는 질문(FAQ)

Q1. 혼자 쓰는 저장소인데도 폴더 동기화가 위험한가요?

네. 실험 ②는 두 사람이 아니라 한 사람의 두 Mac에서 생긴 일입니다. 에이전트를 여러 개 돌리면 한 사람이라도 동시에 여러 곳에서 커밋이 생깁니다. 게다가 실험 ①의 잠금 파일 문제는 동시에 일하지 않아도, 한쪽에서 커밋이 도는 몇 초 사이에 동기화가 일어나기만 하면 생깁니다.

Q2. 그럼 index.lock 오류가 나면 파일을 지워도 되나요?

한 컴퓨터에서만 쓰는 저장소이고, 그 컴퓨터에서 git이나 git을 부르는 도구(에디터, GUI, 에이전트)가 돌고 있지 않다는 것을 확인했다면 지워도 됩니다. git이 비정상 종료해서 남은 파일이기 때문입니다. 하지만 폴더가 동기화되고 있다면 다른 Mac에서 정말로 git이 돌고 있을 수 있으니, 지우기 전에 동기화를 멈추고 저장소를 동기화 폴더 밖으로 옮기는 것이 먼저입니다.

Q3. 원격 저장소는 꼭 GitHub이어야 하나요?

아닙니다. 설계도의 핵심은 "정본이 하나"라는 점이지 특정 서비스가 아닙니다. GitLab, 사내 서버, 직접 운영하는 Forgejo(부록에서 다룹니다)도 됩니다. 인터넷에 올리기 어려운 프로젝트라면 사무실 서버에 SSH로 접속하는 bare 저장소 하나만 있어도 됩니다. 이 글의 실험도 git init --bare로 만든 가짜 원격으로 진행했습니다.

Q4. worktree 대신 저장소를 폴더마다 따로 clone하면 안 되나요?

동작은 합니다. 다만 clone마다 .git이 따로 생겨서 디스크를 더 쓰고, 한 clone에서 만든 브랜치를 다른 clone에서 보려면 원격을 거쳐야 합니다. worktree는 같은 Mac 안에서는 .git을 함께 쓰므로, 한 worktree에서 만든 커밋을 다른 worktree에서 곧바로 볼 수 있습니다. 그래서 이 시리즈는 "Mac마다 clone 하나, 그 안에서 작업마다 worktree"를 기본으로 합니다(2편).

Q5. 사진이나 데이터 폴더는 Syncthing으로 동기화하고, 코드에서는 그 폴더를 읽어도 되나요?

네, 5장에서 권장한 배치입니다. 저장소 폴더와 동기화 폴더를 분리하고, 코드는 경로를 .env 같은 설정으로 받아 읽습니다. 다만 그 자료의 어느 버전으로 결과를 냈는지가 중요한 경우(모델 학습, 재현이 필요한 분석)에는 객체 저장소에 버전을 붙여 올리고, 그 버전을 저장소에 적어 두는 편이 안전합니다.

Q6. .env를 여러 Mac에 어떻게 전달하나요?

Git으로는 전달하지 않습니다. 비밀번호 관리자의 보안 메모나 팀이 쓰는 비밀 관리 도구로 사람이 직접 옮기고, 저장소에는 .env.example만 둡니다. 같은 Mac 안에서 worktree를 만들 때 .env를 복사하는 일은 Claude Code의 .worktreeinclude처럼 도구가 자동으로 해 줄 수 있습니다(3편).


이번 편 요약

주제기억할 것
한 줄 메시지폴더를 동기화하지 말고 Git으로 주고받는다
세 원칙작업 1개 = worktree 1개 = 브랜치 1개 = 에이전트 세션 1개 · Mac 사이 이동 = push/fetch · 개발환경 = 저장소 안의 파일
설계도원격(정본) 1개 → Mac마다 clone → 작업마다 worktree → commit·push·PR로 다시 원격
동기화가 위험한 이유커밋 도중 복사되면 남의 잠금 파일이 남음 · 동시 커밋은 오류 없이 한쪽이 사라짐(dangling) · 충돌 사본은 이상한 브랜치가 됨 · 산출물 12만 개 파일과 CPU별 바이너리
동기화 도구의 자리데이터셋·디자인 원본·참고 문서·개인 메모. 저장소 폴더와 분리. 백업 도구가 아님
무엇을 어디에소스·설정·마이그레이션 → Git · 비밀 → 로컬 .env + Git의 .env.example · 대용량 → S3·MinIO·LFS · 산출물 → 공유 안 함, 다시 만듦

다음 2편에서는 설계도의 가장 아래 칸, worktree를 손에 익힙니다. 만들고, 목록을 보고, 치우고, 잠그고, 옮기는 명령을 하나씩 실행해 보면서 worktree끼리 무엇을 공유하고 무엇이 따로인지 확인합니다.


출처