이중 플랫폼 팬아웃 · Gradle/CocoaPods/DerivedData 계층 · 큐 게이트와 단일 노드 폴백
여러 Mac Mesh 노드에 Flutter 또는 React Native 모노레포를 올리는 소규모 팀은 캐시를 서로 덮어 쓰는 현상, 큐 기아, 다지역 의존성 풀 때문에 벽시계 시간을 잃기 쉽습니다. 본문은 직렬 대 이중 노드 팬아웃 대 플랫폼별 전용 결정표를 제시하고, Gradle·CocoaPods·DerivedData 캐시 키를 읽기 전용 소비자 경계로 나누며, 여섯 단계 재현 게이트와 단일 노드 폴백을 더합니다. Monorepo 영향 빌드 글과 다지역 아티팩트 팬아웃 글을 함께 참고합니다.
Android와 iOS를 병렬로 돌린다는 뜻이 IO 모델 없이 한 장비에 파이프라인 두 개를 얹는 것은 아닙니다. 잘못 스케줄된 병렬성은 불안정성을 컴파일러에서 스케줄러 쪽으로 옮깁니다. 아래 신호가 보이면 동시성을 더하기 전에 매트릭스로 되돌아갑니다.
Gradle과 Xcode 디스크 경합: 한 노드에서 ~/.gradle과 DerivedData가 함께 커지면 IO 대기가 벽시계 시간을 지배합니다.
CocoaPods 해석과 Android NDK 드리프트: 잠금 파일이 캐시 키에서 빠지면 원격에서는 그린이고 통합에서는 레드인 거짓 히트가 납니다.
Metro와 에뮬레이터의 CPU 다툼: 대화형 RN 점검과 무인 CI 작업이 코어를 나누면 큐 깊이는 오르고 처리량은 떨어집니다.
읽기 전용 경계 없는 팬아웃: 소비자가 공유 접두사를 덮어쓰고 아티팩트 포인터 스왑을 경쟁합니다.
재시도 예산 없는 다지역 캐시 풀: 네트워크 재시도가 불어나 동시 시트를 고갈시킵니다.
아래 축은 다지역 원격 Mac 풀 위 모바일 모노레포를 겨냥하며, 팬아웃을 감사 가능한 필드로 만드는 것이 목표입니다. 저장소에 무거운 네이티브 모듈이 많다면 먼저 영향 빌드 가이드의 전체 빌드 단락 규칙에 맞춥니다.
| 축 | 직렬 선호 | 이중 팬아웃 선호 | 전용 노드 분리 |
|---|---|---|---|
| 변경 형태 | 단일 플랫폼 자산이나 카피 수정 | 두 플랫폼이 의존성이나 바이너리 표면을 함께 올림 | Android는 다중 ABI 매트릭스가 필요하고 iOS는 Archive 구간을 병렬로 써야 함 |
| 큐 건강 | 깊이가 안정적이고 P95 벽시간이 예측 가능 | 대기 시간 합이 팬아웃 절감을 넘어섬 | 한쪽 Archive가 다른 쪽 PR 검증을 막음 |
| 캐시 정책 | 잠금 파일과 툴체인 지문을 키에 넣은 단일 접두사 | 플랫폼별 쓰기 접두사 분리, 소비자는 읽기 전용 | 전용 노드는 격리된 세대 번호와 리스 ID를 씀 |
| 다지역 | 한 리전이 SLA를 충족 | 프라이머리가 쓰고 위성이 읽기 전용으로 미러 | 위성은 어느 접두사도 다시 쓰지 않음 |
| 폴백 | 큐 과충전이나 디스크 알림 시 직렬로 합침 | 연속 두 번 검증 실패 후 팬아웃 동결 | 대규모 Xcode나 AGP 이행 중에는 단일 플랫폼 독점 강제 |
이중 플랫폼 병렬성을 논의하기 전에 재현 가능한 입력을 고정합니다. 순서를 뒤집으면 병렬성은 병렬 실패로 바뀝니다.
러너가 공유 풀 신뢰로 모든 노드에 로그인할 수 있다고 가정합니다. 아티팩트가 대양을 가로지르면 아티팩트 팬아웃 가이드의 manifest와 리스 필드도 구현합니다.
다섯 부분 입력 묶음 고정: 짧은 커밋 해시, Podfile.lock/Gemfile.lock, Gradle 잠금, xcodebuild -version, Android SDK·NDK 메이저를 파이프라인 헤더에 둡니다.
쓰기 접두사 분리: IOS_CACHE_GEN과 AND_CACHE_GEN을 단조로 유지하고, 플랫폼 간에 쓰기 디렉터리 하나를 공유하지 않습니다.
팬아웃 결정: 매트릭스 칸이 이중 팬아웃일 때만 병렬 작업 그래프를 만들고, 그렇지 않으면 직렬을 유지합니다.
프라이머리 쓰기, 소비자 읽기 전용: tarball은 프라이머리만 쓰고, 소비자는 풀기 전에 해시와 크기를 검증합니다.
큐 예산: 네트워크 오류에는 지수 백오프와 상한을 둡니다. 상한을 넘기면 인시던트를 열고 시트를 반환합니다.
한 노드로 폴백: 디스크나 큐 임계치에서 자동으로 직렬로 접고, 사후 분석용으로 FANOUT_DISABLED 태그를 붙입니다.
IOS_KEY="${CI_COMMIT_SHORT_SHA}:pods:${PODFILE_LOCK_SHA}:$(xcodebuild -version | shasum -a 256 | cut -c1-10)"
AND_KEY="${CI_COMMIT_SHORT_SHA}:gradle:${GRADLE_LOCK_SHA}:${ANDROID_SDK_MAJOR}"
export FASTLANE_SKIP_UPDATE_CHECK=1
echo "{\"ios\":\"$IOS_KEY\",\"android\":\"$AND_KEY\"}" > "${CI_PROJECT_DIR}/dual-cache.manifest"
안내: 키 필드는 스택에 맞는 사용자 manifest로 바꿀 수 있습니다. 단조 세대와 플랫폼별 쓰기 접두사는 유지하고, 소비자에서 의존성 그래프를 다시 파싱하지 않습니다.
아래 수치는 리뷰와 용량의 출발점으로만 취급하고, 실제 빌드 히스토그램과 큐 지표로 바꿉니다. 외부 SLA로 쓰지 않습니다. 사후 분석에는 이중 플랫폼 P95, tarball 전개 시간, 시트 보유 시간을 함께 넣습니다.
FANOUT_DISABLED와 사람의 점검을 택합니다.| 팀 신호 | 시작 전략 | 아티팩트 팬아웃 연계 |
|---|---|---|
| 기여자 한 자릿수 | 한 리전, 직렬 이중 플랫폼과 로컬 캐시 | 팬아웃 요구가 낮고 키가 단순함 |
| 다지역 PR | 프라이머리가 두 접두사를 쓰고 위성은 읽기 전용 | rsync나 오브젝트 스토리지 팬아웃과 밀접함 |
| 야간 AI 에이전트 배치 | 대화형 풀과 떨어진 전용 헤비 컴파일 노드 | 야간 작업이 공유 키 공간을 깨뜨리지 않음 |
주의: 소비자가 읽기 전용이 되기 전에 다지역 팬아웃을 열면 반쪽 동기화 문제가 동시 거짓 그린으로 확대됩니다.
노트북에서 Android 에뮬레이터와 iOS Archive를 함께 돌리면 절전, 잠금 화면, 대역 지터로 시간을 자주 잃습니다. 순수 SaaS 모바일 빌드 팜은 네이티브 디버깅이나 사내 네트워크 정책과 충돌하기도 합니다. 이중 플랫폼 지속 배포와 감사 가능한 노드 역할을 한 용량 스토리에 담아야 하는 팀은 DIY 스택에서 관측과 권한 경계를 잃기 쉽습니다.
계약된 노드, 검토 가능한 대역, 리전 선택이 필요한 팀은 명시적 큐 정책을 가진 주문형 클라우드 Mac에서 중요 이중 플랫폼 빌드를 돌릴 때 보통 더 빨리 배포합니다. 크로스 플랫폼 배포와 야간 AI 에이전트 부하를 함께 쓰는 경우 VpsMesh Mac Mini 클라우드 대여가 대체로 더 잘 맞습니다. 노드가 깔끔히 갈리고, 링크는 감사 가능하며, 병렬성은 큐 깊이만큼 측정 가능합니다.
두 축 모두 벽시계 시간이 늘고, 단일 노드 IO나 에뮬레이터와 xcodebuild 경합이 보이거나 큐 깊이가 임계치 위에 머물면 팬아웃합니다. 변경이 단일 플랫폼이면 직렬을 유지합니다. 가지치기 로직은 Monorepo 영향 빌드 글에 더 있습니다.
격리 없이 쓰기 가능 접두사를 공유하지 않습니다. 프라이머리 쓰기와 읽기 전용 소비자로 키와 디렉터리를 계층화합니다. 다지역 팬아웃은 아티팩트 팬아웃 매트릭스 글을 읽습니다.