Linux/Windows에서 원격으로 Xcode 자동 테스트를 실행하려면?

테스트 코드는 어떤 OS에서든 작성할 수 있지만, xcodebuild test는 macOS 전용——원격 트리거가 정답입니다.

iOS CI · 자동화 테스트  ·  2026.07.23  ·  약 14분

Linux와 Windows에서 CI 파이프라인으로 macOS의 Xcode 자동 테스트를 원격 트리거

팀 주력 머신은 Windows 노트북이나 Linux 서버인데, iOS 파이프라인에는 단위 테스트와 UI 테스트가 필수——이 모순, 크로스플랫폼 팀이라면 누구나 겪습니다. 먼저 「Windows용 Xcode」「Linux에서 iOS 시뮬레이터」를 검색하다 Apple이 실행 평면을 macOS에 가둔다는 걸 깨닫습니다. 진짜 과제는 「Xcode를 가져오는 것」이 아니라 비 Mac 환경에서 테스트를 안정적으로 트리거·모니터링·결과 회수하는 것입니다.

먼저 「제어 평면 / 실행 평면」 경계를 정리하고, 4가지 구현 방식(SSH, GitHub Actions self-hosted runner, Fastlane, 범용 CI)을 비교합니다. 복사 가능한 명령어와 workflow 조각, 시뮬레이터 선택, .xcresult 회수, 흔한 함정까지 다룹니다. 빌드·배포가 목적이면 먼저 클라우드 Mac으로 Windows iOS 빌드 문제 해결을, Actions 대기열에 막혔다면 macOS CI 대기열 문제를 참고하세요.

4
가지 구현 방식
0
로컬 Mac 하드웨어(선택)
1
개의 핵심 명령 xcodebuild test

왜 Linux/Windows에서 Xcode 자동 테스트를 로컬 실행할 수 없나?

Xcode는 「다른 플랫폼에 크로스 컴파일하면 되는」 일반 IDE가 아닙니다. Apple은 컴파일러, 링커, 시뮬레이터 런타임, 코드 서명 툴체인을 모두 macOS 시스템 이미지에 묶어 둡니다. Windows에서 Swift 소스를 쓰거나 swift build 일부 기능(크로스플랫폼 Swift 패키지)을 쓸 수는 있지만, 다음은 macOS 없이는 존재하지 않습니다:

  • XCTest / XCUITest 실행기——Xcode 내장 테스트 호스트와 시뮬레이터 통신에 의존
  • iOS Simulator——그래픽 스택, Metal, SpringBoard가 macOS 커널 확장 위에 구현
  • xcodebuild test——CLI 진입점은 Mac에 Xcode가 설치된 경우에만
  • Provisioning Profile과 Keychain 서명——실기기 테스트에는 macOS 보안 도메인 필요

따라서 「Linux/Windows에서 원격으로 Xcode 테스트 호출」의 올바른 이해는: 개발 PC나 CI 스케줄러가 비 Mac 시스템에 있고, 테스트 명령은 SSH / Runner / API로 온라인 macOS에 전달되어 그곳에서 실행하고 결과를 반환한다는 것입니다. 이는 타협이 아니라 Apple 생태계의 경직한 경계——Windows에서 iOS 개발하는 5가지 방법의 「Windows에서 코딩, Mac에서 빌드」와 같은 논리입니다.

한 장으로 보는 제어 평면과 실행 평면 역할 분담

Linux / Windows테스트 작성 · PR 제출 · CI 트리거
원격 macOSXcode · 시뮬레이터 · xcodebuild test
제어 평면으로 복귀PR 체크 통과 · 리포트 보관

제어 평면에서 가능

  • 백엔드 / Android 테스트(ubuntu-latest)
  • Lint, Danger, 코드 리뷰 Bot
  • 다중 job 파이프라인 오케스트레이션

제어 평면에서 불가

  • 로컬 iOS 시뮬레이터 시작
  • Mac 없이 xcodebuild test 실행
  • Linux 컨테이너에 Xcode 설치
비 Mac 시스템은 「언제 테스트할지」를 담당. macOS는 「어떻게 테스트할지」를 담당. SSH 또는 CI Runner 프로토콜로 연결됩니다.

원격 Xcode 테스트 표준 아키텍처: 제어 평면 vs 실행 평면

어떤 도구를 선택하든 안정적인 파이프라인은 같은 계층 구조를 따릅니다:

계층 전형적 환경 역할
제어 평면 Windows 개발 PC, Linux CI 마스터, GitHub Actions 오케스트레이터 코드 가져오기, 의존성 캐시, 테스트 트리거, 리포트 집계, Slack 알림
실행 평면 Cloud Mac, 사무실 Mac mini, 호스팅 macos-latest xcodebuild test, 시뮬레이터 시작, 서명, .xcresult 생성
산출물 계층 S3, GitHub Artifacts, 사내 NAS 로그, 스크린샷, 커버리지, JUnit XML 보관

실행 평면은 구매한 Mac mini, 임대 Cloud Mac, GitHub 호스팅 풀 중 무엇이든 됩니다——차이는 비용, 대기열, Xcode 버전 고정 가능 여부입니다. 실행 평면이 준비되면 제어 평면 OS는 무관합니다.

4가지 원격 트리거 방식, 어떻게 고를까?

방식 적합 대상 트리거 방법 복잡도
A. SSH + xcodebuild 개인 개발자, PoC, 스크립트화 야간 회귀 ssh mac 'cd repo && xcodebuild test …' ⭐ 최저
B. GitHub Actions runner GitHub 사용 중, PR 체크 필요 runs-on: [self-hosted, macOS] ⭐⭐
C. Fastlane scan 표준 JUnit, 다중 scheme 매트릭스 Mac에서 fastlane scan ⭐⭐
D. Jenkins / GitLab / API 기업 내부망, 기존 하이브리드 CI SSH agent, webhook, 커스텀 REST ⭐⭐⭐

방식 A: SSH + xcodebuild test(최소 구현 경로)

Windows PowerShell이나 Linux bash에서 「원클릭 테스트」를 원한다면 SSH가 최단 경로입니다. 전제: 24/7 온라인 Mac(로컬 또는 Cloud Mac), Xcode와 프로젝트 의존성 설치 완료.

Step 1 — 비밀번호 없는 SSH 설정

Windows(OpenSSH) 또는 Linux에서 키를 생성하고 Mac의 ~/.ssh/authorized_keys에 등록. Cloud Mac은 콘솔에서 SSH 진입점을 제공하는 경우가 많습니다.

Step 2 — Mac에서 시뮬레이터와 의존성 사전 설치

# 在远程 Mac 上执行一次
xcodebuild -downloadPlatform iOS
xcodebuild -runFirstLaunch
cd ~/Projects/YourApp && bundle exec pod install  # 如使用 CocoaPods

Step 3 — Linux/Windows에서 원격 테스트 트리거

# Linux / macOS / Git Bash 示例
ssh -o StrictHostKeyChecking=accept-new macuser@203.0.113.10 bash -s <<'REMOTE'
set -euo pipefail
cd ~/Projects/YourApp
git fetch origin && git checkout main && git pull

xcodebuild test \
  -workspace YourApp.xcworkspace \
  -scheme YourApp \
  -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.4' \
  -resultBundlePath ./TestResults.xcresult \
  -enableCodeCoverage YES \
  | xcpretty --test --color

# 可选:导出 JUnit 供本地 CI 解析
xcrun xcresulttool get test-results tests \
  --path ./TestResults.xcresult \
  --format json > test-output.json
REMOTE

Windows PowerShell에서는 heredoc 대신 ssh macuser@host "cd ... && xcodebuild test ..."을 쓰거나 run-ios-tests.ps1로 파라미터를 래핑합니다.

Step 4 — 테스트 산출물 가져오기

scp -r macuser@203.0.113.10:~/Projects/YourApp/TestResults.xcresult ./

Xcode에서 .xcresult를 열면 실패 케이스와 UI 테스트 스크린샷을 확인할 수 있습니다. xcresulttool로 자동 파싱도 가능합니다.

무인 운영 요점

SSH 세션에는 기본적으로 GUI Keychain 잠금 해제 대화상자가 없습니다. CI 전용 Mac은 보낸 .p12 + 전용 키체인을 사용하고, 스크립트에서 security unlock-keychain -p "$KEYCHAIN_PASSWORD" ~/Library/Keychains/ci.keychain-db를 실행하세요. 시뮬레이터 테스트는 보통 Development 인증서만 필요해 Archive보다 단순합니다.

방식 B: GitHub Actions + macOS self-hosted runner

팀이 GitHub를 쓰고 PR에서 자동 테스트를 원한다면 Cloud Mac 또는 Mac mini에 self-hosted runner를 등록하는 것이 좋습니다. Linux job은 iOS 외 검사, macOS job은 xcodebuild test를 담당합니다.

Mac에서 Runner 등록(1회)

저장소 → Settings → Actions → Runners → New self-hosted runner → macOS 선택 후 config.sh 다운로드·실행. 라벨 macos, apple-silicon 권장.

Workflow 예시: Linux + macOS 하이브리드

# .github/workflows/ios-test.yml
name: iOS Tests

on:
  pull_request:
  push:
    branches: [main]

jobs:
  lint-and-unit-backend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: SwiftLint / 后端测试
        run: |
          echo "在 Linux 上跑与 iOS 无关的检查"

  ios-test:
    needs: lint-and-unit-backend
    runs-on: [self-hosted, macOS, apple-silicon]
    timeout-minutes: 45
    steps:
      - uses: actions/checkout@v4

      - name: Select Xcode
        run: sudo xcode-select -s /Applications/Xcode_16.4.app

      - name: Install CocoaPods
        run: bundle exec pod install --deployment

      - name: Run unit & UI tests
        run: |
          xcodebuild test \
            -workspace YourApp.xcworkspace \
            -scheme YourApp \
            -destination 'platform=iOS Simulator,name=iPhone 16' \
            -resultBundlePath TestResults.xcresult \
            | xcpretty --test

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: xcresult
          path: TestResults.xcresult

self-hosted runner가 아직 없다면 runs-onmacos-15 또는 macos-latest로 변경——동작하지만 피크에 20–40분 대기할 수 있습니다(대기열 전문 참고). Workspace 격리는 job당 workspace 1개를 참고하세요.

방식 C: Fastlane scan(리포트 표준화)

Fastlane scanxcodebuild test 래퍼로, 한 번 설정해 여러 곳에서 재사용할 수 있고 JUnit XML을 네이티브 출력해 Jenkins / GitLab 테스트 트렌드 표시에 적합합니다.

Fastfile 최소 예시

# fastlane/Fastfile
default_platform(:ios)

platform :ios do
  desc "Run tests on simulator"
  lane :test do
    scan(
      workspace: "YourApp.xcworkspace",
      scheme: "YourApp",
      device: "iPhone 16",
      clean: true,
      code_coverage: true,
      output_types: "junit",
      output_files: "report.junit",
      result_bundle: true
    )
  end
end

Linux CI에서 트리거: Mac에 SSH로 bundle exec fastlane test 실행 후 scpreport.junit 가져오기. 또는 self-hosted runner job에서 직접 fastlane test.

방식 D: Jenkins / GitLab CI / 커스텀 HTTP 트리거

기업 내부망에서 흔한 패턴:

  • Jenkins: macOS 노드를 agent로. Pipeline에서 node('macos') { sh 'xcodebuild test …' }
  • GitLab CI: macOS Runner 등록, tags: [macos, ios] job에서 테스트
  • 커스텀 API: Mac에서 경량 Flask/Go 서비스 실행, Windows webhook 수신 후 비동기 테스트·결과 URL 콜백

커스텀 API는 「Windows 데스크톱 IDE 플러그인 원클릭 테스트」에 맞지만, 대기열·타임아웃·병렬 시뮬레이터 수를 직접 처리해야 합니다——프로덕션에서는 성숙한 CI + self-hosted runner 권장, 바퀴 재발명은 피하세요.

시뮬레이터 vs 실기기: 원격 환경에서의 선택

관점 iOS 시뮬레이터 USB 실기기(Mac 옆)
원격 트리거 난이도 낮음——순수 CLI, 무인 운영 적합 중간——물리 연결, 기기 신뢰, 대화상자 가능
병렬도 여러 destination 실행 가능(CPU/메모리 제한) 보통 Mac당 연결 대수 제한
하드웨어 기능 카메라 / Bluetooth / 푸시 일부 동작이 실기기와 다름 완전한 하드웨어 경로
PR 파이프라인 1순위 릴리스 전 야간 테스트 또는 전용 job

Mac에서 사용 가능한 시뮬레이터 목록:

xcrun simctl list devices available

원격 Cloud Mac에서는 destination 이름 1–2개를 고정해 스크립트에 넣고, Xcode 업그레이드 후 기본 시뮬레이터 이름 변경으로 인한 CI 실패를 방지하세요.

테스트 결과를 Linux/Windows로 가져오려면?

  1. .xcresult: xcodebuild -resultBundlePath로 생성. 로그, 커버리지, UI 테스트 첨부 포함
  2. JUnit XML: Fastlane scanoutput_types: "junit" 또는 xcresulttool 변환
  3. CI Artifacts: GitHub Actions upload-artifact, GitLab artifacts: 블록
  4. scp / rsync: 스크립트화 야간 회귀로 사내 가져오기
  5. Slack / 채팅 알림: JUnit 실패 수 파싱 후 요약만 푸시
# 查看失败用例摘要(在 Mac 或拉回后本地执行)
xcrun xcresulttool get test-results tests \
  --path TestResults.xcresult \
  --format json | jq '.tests[] | select(.testStatus=="Failure") | .name'

흔한 함정과 해결

① 시뮬레이터 최초 시작 타임아웃

무인 SSH 세션에서 시뮬레이터 콜드 스타트가 기본 타임아웃을 초과할 수 있습니다. 해결: CI 스크립트 시작에 xcrun simctl boot "iPhone 16" || trueopen -a Simulator, 또는 Fastlane prelaunchSimulator: true.

② Keychain 대화상자로 블로킹

SSH에 GUI가 없으면 코드 서명에서 멈춥니다. 해결: CI 전용 키체인 + 스크립트 잠금 해제. 시뮬레이터 테스트는 Debug 구성 사용, Distribution 인증서 회피.

③ Xcode 버전 드리프트

Mac 시스템 업그레이드 후 기본 Xcode가 바뀌어 destination OS=18.4를 찾지 못함. 해결: workflow에서 명시적 xcode-select, xcodebuild -showdestinations로 허용 목록 생성·문서화.

④ 병렬 테스트 리소스 경합

Cloud Mac M4 16GB에서 시뮬레이터 3대 + Ollama 동시 실행 시 OOM 가능. 해결: -maximum-parallel-testing-workers 2로 제한, 또는 AI 추론과 테스트 시간 분할——메모리 설정 가이드 참고.

⑤ DerivedData 오염

self-hosted runner가 워크스페이스를 재사용하면 「로컬은 통과, CI는 실패」가 간헐 발생. 해결: job당 workspace 1개 활성화, 또는 주기적 rm -rf ~/Library/Developer/Xcode/DerivedData.

선택 의사결정 트리: 지금 무엇을 쓸까?

  • 개인, 주 몇 회 테스트 → SSH + xcodebuild test, 일 단위 Cloud Mac 1대 임대
  • GitHub 팀, 매일 테스트 → Cloud Mac self-hosted runner로 macos-latest 대기열 탈출
  • 기존 Jenkins/GitLab → macOS agent 추가, Fastlane scan으로 리포트 형식 통일
  • Flutter/React Native만 → Windows에서 flutter test / Jest. iOS 통합 테스트만 원격 Mac job 트리거
  • Mac 운영 원치 않음 → 단기는 호스팅 macos-latest. 장기는 전용 노드로 환경 고정 권장

자주 묻는 질문

WSL2에 Xcode를 설치할 수 있나요?

불가능합니다. WSL2는 Linux 커널이며 macOS 바이너리와 비호환입니다. WSL은 Android / 백엔드 테스트에 적합. iOS 테스트는 Mac SSH 또는 CI macOS job을 사용하세요.

Xcode Cloud는 「원격 호출」에 해당하나요?

해당하지만 제어 평면은 Apple 클라우드입니다. Windows 브라우저나 appstoreconnect CLI에서 workflow를 트리거하고 실행은 Apple 호스팅 Mac에서 이뤄집니다. App Store Connect를 깊이 쓰는 팀에 맞고, 자체 runner와 병행 가능합니다.

원격 UI 테스트(XCUITest) 추가 주의사항은?

시뮬레이터에는 그래픽 세션이 필요합니다. Cloud Mac은 보통 headless Simulator가 설정되어 있습니다. 검은 화면 실패 시 WindowServer 비활성화 여부를 확인하고, 최초에는 VNC로 허가 대화상자를 완료하세요.

테스트와 빌드를 다른 Mac에 나눌 수 있나요?

가능합니다. PR 파이프라인은 저렴한 M4 16GB에서 시뮬레이터 테스트, Release Archive는 별도 전용 Mac에서 실행. 제어 평면은 동일 workflow 매트릭스로 스케줄하면 됩니다.

다음 단계

테스트 파이프라인이 통과하면 보통 Archive + TestFlight가 이어집니다. 전체 빌드 체인은 클라우드 Mac 빌드 가이드를, AI Agent로 자동 코드 수정을 겹치려면 24/7 AI Coding Agent 배포를 참고하세요.

ZavCloud Cloud Mac

전용 macOS에서 iOS 자동 테스트 실행

Mac mini M4 전용 인스턴스: Xcode 사전 설치, SSH 및 GitHub Actions self-hosted runner 지원——Windows / Linux에서 xcodebuild test 트리거, 결과 즉시 회수.

Cloud Mac 플랜 보기
Cloud Mac 원격 Xcode 테스트