클라우드 Mac 파일 동기화 실전: Mutagen으로 로컬과 iOS 프로젝트 연결하기

원격 Mac ·약 10분 읽기

클라우드 Mac 파일 동기화 실전: Mutagen으로 로컬과 iOS 프로젝트 연결하기

프로젝트 리포지토리 크기가 40GB에 가깝고, Pods와 node_modules, DerivedData를 합치면 소스 코드 본체보다도 큽니다. 로컬에서 Xcode를 열고 프로젝트를 클라우드 Mac의 SMB 공유로 마운트해봤더니, 인덱싱에 1분 넘게 걸리고 브랜치만 전환해도 네트워크가 끊긴 게 아닌가 싶을 정도로 멈춰버립니다. 이는 개발 환경을 클라우드 Mac으로 옮긴 많은 사람들이 처음 맞닥뜨리는 벽입니다. 공유 마운트는 간편해 보이지만, 실제로 써보면 대규모 iOS 프로젝트의 파일 접근 패턴과 네트워크 파일 시스템이 근본적으로 맞지 않는다는 걸 알게 됩니다. 이 글에서는 HireVPS 노드에서 Mutagen을 사용해 로컬과 원격을 양방향으로 동기화하며, 이 경험을 로컬에 가깝게 되돌리는 과정을 정리합니다.

대규모 iOS 프로젝트에 공유 마운트가 적합하지 않은 이유

NFS, SMB 같은 네트워크 파일 시스템은 "가끔 접근하는 큰 파일"을 전제로 설계되어 있습니다. 그런데 Xcode 인덱싱, SourceKit, CocoaPods 캐시 스캔이 하는 일은 정확히 그 반대로, 짧은 시간 안에 수만 개의 작은 파일에 stat, open, close를 반복하는 것입니다. 매 작업마다 네트워크 왕복이 발생하고, 지연 시간이 20ms에 불과하더라도 이것이 수만 번 누적되면 분 단위의 멈춤으로 이어집니다. 더 골치 아픈 건 연결이 끊겼을 때의 처리입니다. 마운트 포인트는 연결이 불안정해지면 즉시 먹통이 되어 터미널과 에디터가 동시에 멈추고, 결국 강제로 언마운트한 뒤 다시 마운트하는 수밖에 없습니다.

흔히 쓰이는 세 가지 방식을 비교해보면 차이가 뚜렷해집니다.

방식 실시간성 대량의 작은 파일 처리 끊김 복구 적합한 시나리오
SMB/NFS 마운트 강함(보이는 즉시 최신) 나쁨, 파일 단위로 네트워크 왕복 나쁨, 먹통이 되기 쉬움 큰 파일을 가끔 읽고 쓰는 경우
rsync 정기 작업 약함, 동기화 간격 존재 보통, 일괄 전송이라 효율적 좋음, 작업 단위로 재실행하면 됨 정기 백업, 단방향 푸시
Mutagen 양방향 동기화 실시간에 가까움 좋음, 로컬 캐시+증분 전송 좋음, 끊기면 자동 재연결·재전송 지속적인 개발에서의 양방향 협업

저희가 겪었던 교훈 하나는, 처음에는 편하다는 이유로 그냥 마운트를 걸었다는 것입니다. 그 결과 Xcode의 "Building workspace" 단계에서만 평균 15~25초씩 더 기다려야 했고, 팀 전체가 인덱싱 대기로 매일 상당한 시간을 낭비했습니다. Mutagen으로 바꾼 뒤로는 이 대기 시간이 거의 사라졌습니다.

Mutagen으로 양방향 동기화 세션 구성하기

설치와 최초 연결

Mutagen 자체는 단일 바이너리로, 로컬과 클라우드 Mac 어느 쪽에도 별도의 상주 서비스가 필요 없습니다. 연결이 맺어질 때 원격 쪽에 agent가 자동으로 배포됩니다. 먼저 로컬에 설치합니다.

brew install mutagen-io/mutagen/mutagen
mutagen version

클라우드 Mac 쪽에 SSH가 이미 열려 있다고 가정하면(자격 증명은 개통 안내 메일에 있습니다), 동기화 세션을 만듭니다.

mutagen sync create \
  --name=ios-app \
  --ignore-vcs \
  --symlink-mode=posix-raw \
  /Users/me/Projects/ios-app \
  ssh://devuser@your-cloud-mac-host/Users/devuser/Projects/ios-app

--ignore-vcs를 붙이면 .git 내부 오브젝트를 자동으로 건너뜁니다(git 자체는 고유 프로토콜로 동기화하는 게 더 적합하며, Mutagen이 중복으로 옮길 필요는 없습니다). --symlink-mode=posix-raw는 CocoaPods에서 흔히 쓰이는 심볼릭 링크를 그대로 유지하고 변환하지 않도록 해줍니다.

제외 규칙: 절대 동기화하면 안 되는 디렉터리

프로젝트 루트에는 반드시 .mutagenignore를 만들어야 합니다. 그렇지 않으면 첫 동기화만으로도 몇 GB짜리 캐시 디렉터리까지 함께 전송됩니다.

DerivedData/
Pods/
.build/
node_modules/
*.xcuserstate
xcuserdata/
.DS_Store

이 디렉터리들의 공통점은 "어느 쪽에서든 로컬에서 다시 생성할 수 있다"는 것입니다. 동기화하면 전송량만 낭비되는 게 아니라, 다음 절에서 설명할 문제까지 일으킬 수 있습니다.

Xcode 관련 주의사항

실측 성능과 충돌 처리

일상적인 사용에서는 저장 한 번으로 트리거되는 증분 동기화(Swift 파일 몇 개 수정)가 대체로 1~2초 안에 끝나서, 체감상 로컬 저장과 큰 차이가 없습니다. 반면 첫 전체 동기화(수만 개의 소스 파일)는 디렉터리 트리를 스캔해야 해서 눈에 띄게 느려지므로, 작업을 시작하기 전에 한 번 실행해서 안정화시켜두고, 코드를 쓰면서 첫 동기화를 기다리는 식으로 쓰지 않는 게 좋습니다.

충돌 처리에서, 기본 양방향 모드인 two-way-safe는 충돌이 발생하면 자동으로 덮어쓰지 않고 멈춰서 사용자의 처리를 기다립니다. 강제 덮어쓰기 방식보다는 안전하지만, 양쪽에서 동시에 같은 파일을 수정하면 자주 멈추게 됩니다. 팀 협업 상황에서는 다음 설정을 더 추천합니다.

mutagen sync create \
  --name=ios-app \
  --sync-mode=two-way-resolved \
  --default-file-mode-alpha=0644 \
  /Users/me/Projects/ios-app \
  ssh://devuser@your-cloud-mac-host/Users/devuser/Projects/ios-app

two-way-resolved는 충돌 시 기본적으로 로컬 쪽(alpha)이 우선하도록 처리합니다. "같은 시간에는 한 사람만 한쪽에서 코드를 수정한다"는 규칙과 함께 쓰면 변경 사항이 사라지는 일은 거의 없습니다. 실제로 이미지, 폰트 같은 바이너리 리소스에서 충돌이 발생하면 mutagen sync list로 충돌 경로 목록을 확인할 수 있으며, 타임스탬프와 파일 크기를 비교해서 어느 쪽을 남길지 수동으로 결정합니다.

그대로 쓸 수 있는 설정 예시

Mutagen은 프로젝트 단위 설정 파일을 지원합니다. 프로젝트 루트에 mutagen.yml을 두면, 팀원이 리포지토리를 클론한 뒤 명령어 한 줄로 동기화를 시작할 수 있어서 각자 파라미터를 일일이 입력할 필요가 없습니다.

sync:
  ios-app:
    alpha: "."
    beta: "ssh://devuser@your-cloud-mac-host/Users/devuser/Projects/ios-app"
    mode: "two-way-resolved"
    ignore:
      vcs: true
      paths:
        - "DerivedData"
        - "Pods"
        - "node_modules"
        - "xcuserdata"
        - ".build"
    symlink:
      mode: "posix-raw"

자주 쓰는 명령어는 이 정도만 기억해두면 충분합니다.

mutagen sync list
mutagen sync monitor ios-app
mutagen sync pause ios-app
mutagen sync resume ios-app
mutagen sync terminate ios-app

monitor는 "왜 변경 사항이 동기화되지 않았는지"를 확인할 때 특히 유용합니다. 스캔, 스테이징, 전송 세 단계 중 어디서 막혀 있는지를 실시간으로 볼 수 있습니다.

도입 전 체크리스트

이 항목들을 한 번씩 점검하면 클라우드 Mac에서의 편집 경험이 로컬에 거의 가까워지고, 팀 협업 시에도 "누구의 변경이 동기화되지 않았는지"를 두고 서로 책임을 미루는 일이 줄어들 것입니다.

자주 묻는 질문

SMB나 NFS로 마운트하면 되지 않나요?

네트워크 마운트는 작은 파일 접근(Xcode 인덱싱, Pods 조회)마다 왕복 통신이 발생해 대형 프로젝트는 열기만 해도 수십 초가 걸릴 수 있습니다. Mutagen은 로컬 캐시를 유지하며 증분 동기화하기 때문에 편집 반응성이 로컬과 거의 같고, 연결이 끊겨도 파일시스템이 멈추지 않습니다.

DerivedData도 동기화해야 하나요?

아니요. DerivedData의 모듈 캐시와 인덱스는 머신 아키텍처와 절대 경로에 묶여 있어서 동기화하면 재인덱싱이나 빌드 오류가 자주 발생합니다. .mutagenignore에 추가하고 각 머신이 개별적으로 생성하게 두세요.

양방향 동기화 중 충돌이 나면?

mutagen sync list로 충돌 경로를 확인하세요. 소스 코드 충돌은 대부분 two-way-resolved 모드에서 로컬(alpha) 쪽을 우선시하면 해결됩니다. 바이너리 자산은 수동으로 diff해서 판단하고, 동기화 시작 전에 git status로 작업 트리를 정리하는 습관이 충돌을 크게 줄여줍니다.

HireVPS Mac

지금 전용 클라우드 Mac mini를 이용해 보세요

일 단위 대여로 시작하고, 2분 안에 SSH/VNC 접속 정보를 받아 언제든지 사양을 업그레이드할 수 있습니다.

Mac mini 바로 대여하기