여러 대의 Mac에서 Claude Code 설정을 동기화하는 방법
MacBook Pro와 Mac Mini를 동시에 쓰고 있다. 두 머신 모두 Claude Code를 쓰는데, 한쪽에서 설정한 커스텀 스킬, 에이전트, 메모리 같은 것들이 다른 쪽에는 없다. 매번 수동으로 복사하는 건 말이 안 된다.
~/.claude 폴더를 두 머신 간에 자동으로 동기화하고, 동시에 Git으로 형상관리까지 하고 싶었다.
~/.claude 안에는 뭐가 있나
Claude Code의 모든 설정은 ~/.claude/에 들어있다.
| 경로 | 설명 | 동기화 필요 |
|---|---|---|
CLAUDE.md | 글로벌 지시사항 | O |
settings.json | 설정 (hooks, MCP, permissions) | O |
memory/ | 글로벌 메모리 | O |
skills/ | 커스텀 스킬 | O |
agents/ | 커스텀 에이전트 | O |
commands/ | 커스텀 slash 명령어 | O |
projects/*/memory/ | 프로젝트별 메모리 | O |
*.jsonl | 대화 세션 로그 (~1.6GB) | X |
cache/, debug/ 등 | 캐시, 임시 파일 | X |
settings.local.json | 머신별 로컬 설정 | X |
.credentials.json | 인증 정보 | X |
내 기준 전체 폴더가 약 2GB였는데, 동기화가 필요한 건 ~165MB 정도였다. 나머지는 세션 로그와 캐시다.
세션은 왜 동기화하지 않나
세션 로그(*.jsonl)와 세션 데이터(UUID 폴더)는 동기화 범위에서 제외했다. 용량이 점점 커질 것이기도 하고, 하루에도 수많은 세션을 시작하고 종료하는데 이걸 굳이 동기화할 필요는 없다. 나는 하루 안에 끝나는 세션은 주로 MacBook에서, 장기로 가져가야 하는 세션은 Mac Mini에서 실행하는 패턴이 정해져 있어서 세션 동기화가 필요 없었다. 당연히 대화 내역을 GitHub에 올리고 싶지도 않았고.
동기화 방법 비교
Dropbox
클라우드 기반 동기화. 자동이고 안정적이다. 하지만 ~/.claude를 Dropbox 폴더로 옮기고 심링크를 걸어야 한다. 이 구조에서는 Git 형상관리 시 심링크가 커밋되는 문제가 생긴다. 무료 플랜은 기기 3대 제한. 유휴 시 RAM 200-400MB.
rsync
단방향 복사 도구. cron으로 자동화할 수 있지만, 양방향 동기화와 충돌 처리를 직접 구현해야 한다. 사실상 수동이나 마찬가지.
Syncthing
P2P 실시간 양방향 동기화. 오픈소스(GitHub 70k+ stars). 기기 수, 용량, 대역폭 모두 무제한. 유휴 시 RAM 30-50MB. ~/.claude 폴더를 그대로 두고 동기화하기 때문에 같은 폴더에서 Git도 쓸 수 있다. 심링크가 필요 없다.
Syncthing을 선택한 이유: 무료, 가볍고, 심링크 없이 원본 폴더를 그대로 동기화할 수 있어서 Git 형상관리와 양립 가능하다.
설정 방법
1. 설치 및 실행
양쪽 머신 모두:
brew install syncthing
brew services start syncthing
http://localhost:8384에서 WebUI 접속 가능.
2. 두 머신 연결
- 머신 A WebUI에서 Actions > Show ID로 Device ID 복사
- 머신 B WebUI에서 Add Remote Device > Device ID 붙여넣기
- 머신 A에서 연결 요청 Accept
한쪽에서만 하면 양방향 연결된다.
3. 동기화 폴더 설정
머신 A에서 Add Folder:
- Folder Label:
claude-config - Folder Path:
~/.claude - Folder ID:
claude-config
Save 후 Edit > Sharing 탭에서 상대 머신 체크. 머신 B에서 공유 요청 Accept.
4. .stignore로 제외 패턴 설정
~/.claude/.stignore:
// 세션 로그
*.jsonl
// 세션 데이터 (UUID 폴더)
projects/*/????????-????-????-????-????????????
projects/*/????????-????-????-????-????????????.jsonl
// 메모리&스킬도 동기화 필요없는 프로젝트
projects/-Users-sh
// 캐시 및 임시
cache/
debug/
file-history/
paste-cache/
session-env/
shell-snapshots/
sessions/
telemetry/
tasks/
downloads/
ide/
backups/
plans/
// 머신별
settings.local.json
.credentials.json
// 시스템
.DS_Store
// Git
.git/
홈 디렉토리(~)에서 Claude Code를 종종 실행하는데, 이때 projects/ 아래에 홈 경로 기반의 프로젝트 폴더가 생긴다. 예를 들어 홈 폴더 사용자명이 sh라면 projects/-Users-sh가 생기는데, 퀵하게 쓰고 종료하는 용도라 메모리/스킬 동기화가 필요 없어서 통째로 제외했다.
주의: Syncthing의.stignore는[0-9a-f]같은 문자 범위를 지원하지 않는다. UUID 패턴은?와일드카드로 매칭해야 한다.
5. Git 형상관리 추가
같은 ~/.claude 폴더에서 Git을 초기화한다. .gitignore는 .stignore와 거의 동일하되, Syncthing 메타데이터(.stfolder/, .stversions/)를 추가로 제외한다.
cd ~/.claude
git init
gh repo create .claude --private --source=. --push
외부 git repo를 포함하는 스킬은 submodule로 관리하면 된다.
git submodule add https://github.com/garrytan/gstack.git skills/gstack
삽질 포인트
.credentials.json을 동기화하면 안 된다
처음에 .credentials.json을 제외하지 않고 동기화했더니, 한쪽 머신의 인증 정보가 다른 쪽으로 덮어씌워지면서 로그인이 풀렸다. 이 파일은 머신별로 고유해야 한다. .stignore와 .gitignore 모두에서 반드시 제외할 것.
.stignore의 glob 문법은 .gitignore와 다르다
.gitignore에서는 [0-9a-f] 같은 문자 범위가 동작하지만, Syncthing의 .stignore에서는 파싱 에러가 난다. UUID 폴더를 제외하려면 ????????-????-????-????-???????????? 패턴을 써야 한다.
skills 안에 git repo가 있을 수 있다
커뮤니티 스킬 중 일부는 git clone으로 설치된다. 이런 폴더를 그냥 git add하면 embedded git repo 경고가 뜬다. submodule로 등록하거나, .gitignore에서 제외하면 된다.
결과
한쪽 머신에서 새 스킬을 설치하거나 메모리를 추가하면, 다른 쪽에서도 몇 초 안에 반영된다. Git으로는 설정 변경 이력을 추적할 수 있고, GitHub에 백업도 된다.
유지 관리는 거의 없다. 가끔 WebUI(localhost:8384)에서 상태를 확인하는 정도. macOS 업데이트 후 brew services list로 Syncthing이 잘 돌고 있는지 보면 된다.
업데이트 2026-05-02: Syncthing과 git이 같은 파일을 만질 때
한 달 굴려보니 한 가지 반복되는 문제가 나타났다. .stignore나 .gitignore 같은 파일을 편집하면 git pull 시 "Your local changes would be overwritten by merge" 오류가 자주 떴다.
원리를 풀어보면 단순하다.
- A 머신에서
.stignore수정 - Syncthing이 파일 단위로 감지해서 B 머신에 수 초 안에 전파 (FSEvents 기반)
- A에서 commit하고 push
- B에서
git pull→ 실패. working tree는 이미 새 커밋의 내용과 동일한데(Syncthing이 먼저 도착했으니까), git HEAD는 아직 이전 커밋이라 git이 그 내용을 "uncommitted local modification"으로 인식한다.
요컨대 Syncthing과 git이 같은 파일을 다른 레이어에서, 다른 스케줄로 만지고 있다. Race condition이다.
해결: sync-rule 파일은 Syncthing 범위에서 빼기
.stignore에 자기 자신과 .gitignore를 추가한다.
// git만으로 관리 (Syncthing/git 이중 sync 충돌 방지)
.gitignore
.stignore
이제 .gitignore와 .stignore는 git-only다. Syncthing은 이 두 파일을 건드리지 않고, 갱신은 오직 git push / git pull로만 흐른다. Race 없다.
이게 통하는 이유는, 문제를 일으키는 파일이 정확히 sync-rule 파일 자기 자신이기 때문이다. 자기 참조 구조이긴 하지만 위험은 없다. 각 머신이 자기 .stignore를 따로 갖되, git을 단일 진실 공급원으로 두면 결국 똑같은 내용으로 수렴한다.
이 패턴을 남용하지 말 것
이 트릭은 Syncthing-vs-git race가 실제로 자주 터지는 파일에만 쓸 가치가 있다. 그 외 파일들(스킬, 에이전트, 스크립트 등)은 충돌이 드물고, 한두 번 떠도 한 줄로 해결 가능하다.
cd ~/.claude
# 흔한 케이스: 로컬 내용이 이미 remote와 같음. diff만 정리하고 pull
git checkout HEAD -- <file>
git pull
로컬 수정사항을 진짜로 지키고 싶다면 stash로 우회한다.
git stash push <file>
git pull
git stash pop
복구 중에 조심할 것
git reset --hard origin/master는 git-tracked 파일의 커밋 안 된 수정사항을 전부 날린다. .gitignore에 등록된 파일만 살아남는다. 여러 파일을 수정 중인 상태에서 꼬였다면 reset 전에 commit하거나 /tmp 등으로 백업해두자.