한 대의 Mac이 모든 UI 테스트를 직렬로 돌리면 모바일 릴리스 열차가 자주 멈춥니다. 이 글은 샤딩이 값어치 있는 시점, 단일 노드와 다중 노드 전략을 한 매트릭스에서 비교하는 방법, 러너 레이블과 실행 시간 예산 정의, 전용 Mac mini M4 클라우드 머신에 XCTest UI 작업을 펼치는 여덟 단계, 예측 가능한 큐 수학까지 다룹니다.
셀프호스티드 GitHub Actions 러너를 표준화 중이라면 샤딩은 “빠른 Mac 한 대”를 리뷰어가 맥락을 바꾸기 전에 PR 검증을 끝내는 플릿으로 바꾸는 방법입니다. 더 넓은 빌드 병렬성은 CI/CD용 병렬 Mac 빌드 노드도 참고하세요.
막강한 Mac 한 대로도 UI 테스트 SLA를 못 맞추는 이유
Xcode는 단위 테스트 병렬화에 강하지만 UI 테스트는 시뮬레이터 기동, 전환 애니메이션, SpringBoard 대기에 시간을 씁니다. 단일 호스트에서는 CPU 코어 수에 선형으로 늘지 않습니다.
- 시뮬레이터 GPU 경합:한 대의 M4에서 UI 스위트 세 개를 동시에 돌리면 프레임 시간이 테스트가 암묵적으로 가정하는 33 ms를 넘겨 탭 불안정과 오탐 실패를 만듭니다.
- 디스크 증폭:각 샤드가 DerivedData 쓰기를 복제합니다. 500 GB 이상 SSD 여유가 없으면 여유 공간이 약 15% 아래로 떨어질 때 병렬 작업이 무너집니다.
- 큐 불투명:샤드별 레이블이 없으면 느린 PR이 UI 인프라 대기인지 컴파일 대기인지 알 수 없어 컴파일 러너만 과투자하고 UI 마감은 놓칩니다.
의사결정 매트릭스: 단일 Mac 대 샤딩 플릿
| 기준 | 단일 M4 “전부 처리” | 전용 UI 샤드 Mac |
|---|---|---|
| 90분 UI 스위트 벽시계 | 직렬 ≈ 90분 | 3샤드이고 균형이면 ≈ 35–40분 |
| 플레이크 민감도 | 과부하 시 높음 | 머신당 스위트 하나면 낮음 |
| 운영 복잡도 | 낮음 | 중간. 레이블과 대시보드 필요 |
| 최적 지리 | 임의 | HK / JP / KR / SG / US에 개발자 옆에 샤드 배치 |
하드웨어를 더 사기 전에 테스트 버킷화
샤딩은 인프라 문제로 가장한 스케줄링 문제입니다. 실행 시간 분산이 비슷한 버킷으로 나누고, 각 버킷이 중앙값의 ±20% 안에 오도록 하세요. 그렇지 않으면 매번 같은 샤드만 꼬리가 됩니다.
- 과거 소요 시간보내기(Xcode Cloud, XCTest 로그, CI DB) 후 p95 기준 정렬.
- 로그인 집약 흐름은 별 버킷으로 분리해 독립 가능한 결제·설정 흐름을 막지 않기.
- 물리 디바이스 랩이 필요한 테스트는 별 태그—클라우드 Mac mini는 시뮬레이터에 강하고 USB 팜이 아님.
- 버킷 크기를 PR 예산 안으로. 여전히 25분을 넘기면 기능 모듈로 다시 쪼개기.
- 버킷 맵을 git에 버전 관리(
ui-shards.json)해 재실행 재현성 확보.
팁:컴파일 샤드와 UI 샤드는 레이블을 분리하세요. 무거운 xcodebuild test가 시뮬레이터용 타임아웃으로 구성된 머신을 가져가면 큐 역전이 납니다.
UI 샤드용 러너 레이블 계약
| 레이블 집합 | 목적 | runs-on 예 |
|---|---|---|
| self-hosted, macOS, m4, ios-compile | 빌드 + 단위 테스트만 | [self-hosted, macOS, m4, ios-compile] |
| self-hosted, macOS, m4, ios-ui, shard-1 | UI 버킷 A | [self-hosted, macOS, m4, ios-ui, shard-1] |
| self-hosted, macOS, m4, ios-ui, shard-2 | UI 버킷 B | B/C/D… 동일 패턴 |
GitHub Actions 매트릭스: 이중 스케줄 방지
많은 팀이 샤드를 매트릭스 차원으로 표현합니다. 실패 패턴은 shard: [1,2,3]과 모든 Mac에 매칭되는 넓은 runs-on을 함께 선언하는 것입니다. GitHub가 여러 샤드를 한 호스트에 올리면 하드웨어 비용이 허공으로 갑니다. 각 다리를 고유 러너 레이블에 고정하거나 호스트명과 1:1인 저장소 변수를 쓰세요.
matrix:
shard: [1, 2, 3]
include:
- shard: 1
runner_labels: [self-hosted, macOS, m4, ios-ui, shard-1]
- shard: 2
runner_labels: [self-hosted, macOS, m4, ios-ui, shard-2]
- shard: 3
runner_labels: [self-hosted, macOS, m4, ios-ui, shard-3]
jobs:
ui-tests:
runs-on: ${{ matrix.runner_labels }}
timeout-minutes: 40
UI 작업에 shard-* 레이블이 없는 워크플로를 거절하는 저장소 규칙을 두면, 의도 좋은 기여자가 병렬도를 깨뜨리는 일을 막습니다. 둘째 리전(예: 싱가포르 컴퓨트 + 도쿄 리뷰어)에 Mac mini를 추가할 때는 리전별로 레이블 체계를 복제(shard-1-sg)해 네트워크 근접성을 YAML에 드러내세요.
클라우드 Mac mini M4에서 UI 샤드 가동 여덟 단계
NodeMac Mac mini M4에 SSH로 접속할 수 있다고 가정합니다. 연결 패턴은 도움말 센터를 참고하세요.
- N대 프로비저닝. N = 목표 샤드 수 + 플레이크 재실행용 핫 스페어 1대.
- 동일 빌드의 Xcode와 시뮬레이터 런타임 사전 설치. 샤드 간 드리프트는 “내 러너에선 된다”는 거짓 음성을 만듭니다.
- GitHub 러너를 고유 이름으로 등록하고 해당 샤드만 서빙할 레이블만 부여.
- 워크플로마다
timeout-minutes설정—UI 샤드는 먼저 40분 후 p95로 조이기. - 샤드 식별자를
xcodebuild에 전달(스킴 또는 테스트 플랜, 버킷별-only-testing목록). - 디스플레이 절전 끄기. 하네스가 GUI 세션을 요구하면 CI 사용자 로그인 유지를 문서화하고 보안 검토에 따르기.
- 아티팩트 중앙 수집(JUnit, 스크린샷). 파일명에 샤드 이름을 넣어 트리아지를 명확히.
- 샤드 왜곡 알림:한 샤드가 연속 세 번 다른 샤드보다 1.5배 이상 느리면 버킷 재조정.
자주 묻는 질문
Mac mini M4 한 대에 UI 테스트 샤드를 몇 개까지 올릴 수 있나요?
여유를 측정하지 않았다면 시뮬레이터 부하가 큰 UI 샤드는 머신당 하나의 주 작업으로 취급하세요. 가벼운 스위트는 두 샤드도 가능하지만 GPU와 스토리지 경합이 벽시계 이득을 없애는 경우가 많습니다.
샤드는 컴파일 전용 작업과 같은 러너 레이블을 써야 하나요?
아니요. ios-ui-shard 같은 전용 레이블을 써서 추가 시뮬레이터와 화면 세션을 가정해 구성한 머신을 컴파일 작업이 가져가지 않게 하세요.
홍콩·도쿄·서울·싱가포르·미국 팀에서 몇 분 거리의 머신이 필요하면 위 큐 수학에 맞춰 NodeMac 요금제를 비교하고 감으로 샤드 수를 정하지 마세요.
Mac mini M4는 iOS UI 작업에 실용적인 샤딩 단위입니다. Apple Silicon은 XCTest 오케스트레이션에 강한 단일 스레드 성능을 주고, 여러 시뮬레이터 서비스에는 통합 메모리가 맞으며, PR 공백에서 러너가 기다릴 때 유휴 전력이 낮습니다. NodeMac은 HK·JP·KR·SG·US에서 SSH와 VNC가 있는 전용 물리 Mac mini를 임대해 각 샤드를 원격으로 디버깅 가능한 실제 하드웨어에 매핑합니다. 옷장 가득 Mac을 사는 것보다 온디맨드 임대로 샤드 맵을 검증하고 실제 텔레메트리로 버킷을 다시 맞추며 CapEx를 줄이세요.