빌드는 성공으로 표시되지만 TestFlight에 올라가지 않거나, 원격 Mac을 재시작한 뒤 Runner가 오프라인으로 남는 문제가 생깁니다.

가장 빠른 해결책은 Runner 등록을 출발점으로만 삼고, Xcode 고정, 서명 자료 격리, 작업 라우팅, 재시작 복구를 실제 프로젝트의 Archive와 업로드까지 차례로 검증하는 것입니다.

이 글이 필요한 개발자

GitHub에 코드를 보관하면서 iOS 앱의 자동 빌드와 배포를 준비하는 독립 개발자를 위한 글입니다.
로컬 Mac 없이 Windows 또는 Linux에서 개발하고 원격 macOS를 상시 빌드 호스트로 쓰려는 경우에도 적용할 수 있습니다.
Xcode 버전, 네이티브 의존성, Apple 서명 자료를 직접 통제해야 하는 소규모 팀도 대상입니다.

운영 가능성을 가르는 호스트 조건

원격 Mac에 GitHub Actions Runner를 설치하는 일 자체는 어렵지 않습니다. 문제는 해당 호스트가 배포 작업을 계속 받을 수 있는 상태인지입니다. Xcode는 macOS와 SDK 조건을 함께 따르므로, 프로젝트가 요구하는 버전이 호스트에서 지원되는지 먼저 Apple의 Xcode 시스템 요구 사항에서 확인해야 합니다.

저장 공간도 단순한 여유 용량 문제가 아닙니다. 소스 코드, 의존성 캐시, Archive, 내보낸 파일, 로그와 디버그 기호가 같은 호스트에 쌓이면 다음 작업이 시작되기 전에 실패할 수 있습니다. 캐시를 무조건 보존하기보다 보관 대상과 삭제 조건을 분리해야 합니다.

네트워크가 잠시 끊기면 현재 작업과 GitHub의 상태 보고가 서로 다르게 보일 수 있습니다. 따라서 원격 화면이 연결되어 있을 때만 Runner를 실행하는 방식은 운영용으로 부적합합니다. GitHub 공식 문서의 Runner 상태 확인과 macOS 문제 해결 절차를 기준으로 온라인 상태, 작업 로그, 호스트 상태를 함께 확인해야 합니다.

다음 항목을 먼저 점검합니다.

  • [ ] 프로젝트의 Xcode 및 macOS 요구 조건을 확인했습니다.
  • [ ] 소스 복원과 빌드 산출물을 저장할 공간 정책이 있습니다.
  • [ ] 원격 세션이 닫혀도 Runner가 계속 실행됩니다.
  • [ ] 호스트 재시작 뒤 온라인 상태를 확인할 방법이 있습니다.
  • [ ] 작업 중단과 네트워크 오류의 로그 위치를 알고 있습니다.

작업 라우팅과 Runner 등록

등록 범위 선택

GitHub는 self-hosted runner를 저장소, 조직 또는 기업 범위에 등록할 수 있습니다. 단일 앱만 운영한다면 저장소 범위가 접근 경계를 좁히기 쉽습니다. 여러 비공개 앱이 같은 호스트를 사용해야 한다면 조직 범위를 검토하되, 모든 저장소가 배포용 Runner에 작업을 보낼 수 있는지 먼저 확인해야 합니다.

Runner 등록 과정에서는 GitHub가 제공하는 임시 등록 정보와 호스트의 작업 디렉터리를 사용합니다. 실제 저장소 주소와 토큰을 예시 파일에 남기지 말고, 아래처럼 명확한 자리표시자로 관리해야 합니다.

./config.sh \
  --url https://github.com/OWNER/REPOSITORY \
  --token RUNNER_REGISTRATION_TOKEN \
  --name REMOTE_MAC_RUNNER \
  --labels self-hosted,macOS,ARM64,ios-release

위 명령의 값은 설명용 자리표시자입니다. 실제 토큰, 저장소 주소, 팀 식별자, 번들 식별자를 공개 저장소나 문서에 넣으면 안 됩니다. 공식 등록 절차는 GitHub의 Runner 추가 문서에서 현재 화면과 명령을 다시 확인해야 합니다.

라벨과 작업 파일

self-hosted, macOS, ARM64는 작업이 실행될 운영체제와 아키텍처를 구분하는 기본 축입니다. 운영 배포용 호스트에는 ios-release처럼 목적이 드러나는 사용자 정의 라벨을 추가합니다. 라벨은 작업의 runs-on 조건과 모두 맞아야 하므로, 라벨 하나라도 다르면 작업은 실행되지 않고 대기 상태에 머뭅니다.

jobs:
  archive-and-upload:
    runs-on: [self-hosted, macOS, ARM64, ios-release]
    steps:
      - uses: actions/checkout@v4
      - name: Select Xcode
        run: sudo xcode-select --switch /Applications/Xcode.app
      - name: Build without signing
        run: xcodebuild -scheme APP_SCHEME -configuration Release build CODE_SIGNING_ALLOWED=NO

작업 파일의 uses 버전은 예시일 뿐입니다. 운영 환경에서는 사용 중인 Action의 공급망과 변경 이력을 검토해야 합니다. 또한 라벨에 맞는 Runner가 없는 상황을 일부러 만들어 작업 대기와 알림이 발생하는지 확인해야 합니다. GitHub의 라벨 라우팅 설명은 기본 라벨과 사용자 정의 라벨의 일치 방식을 설명합니다.

라우팅 선택 적합한 용도 확인할 조건 실패 시 보이는 현상
저장소 Runner 한 앱의 비공개 배포 저장소 접근 범위와 라벨 조건 불일치 작업 대기
조직 Runner 여러 비공개 앱의 공용 빌드 허용 저장소와 라벨 원치 않는 저장소가 호스트 사용
전용 배포 라벨 출시 작업만 분리 ios-release 같은 목적 라벨 라벨 누락으로 작업 대기
범용 macOS 라벨 테스트와 검증 운영 배포와 분리된 호스트 잘못된 Xcode 또는 인증 정보 사용

Xcode 기준과 최소 빌드

원격 Mac에 Xcode가 여러 개 있으면 화면에서 보이는 Xcode와 명령줄이 호출하는 Xcode가 다를 수 있습니다. 작업 시작 단계에서 xcode-select로 활성 개발자 디렉터리를 고정하고, 실제 선택 결과를 로그에 기록해야 합니다.

프로젝트의 의존성 잠금 파일도 빌드 재현성에 영향을 줍니다. Swift Package Manager, CocoaPods 또는 기타 의존성 도구를 사용하는 경우 잠금 파일을 검토한 뒤 동일한 복원 명령을 사용합니다. Scheme은 공유 상태인지 확인하고, 로컬 개발자의 사용자 설정에만 의존하지 않도록 합니다.

처음부터 서명과 TestFlight 업로드를 한 번에 실행하지 않는 편이 안전합니다. 다음 순서로 도구 체인을 좁혀 갑니다.

  1. 저장소를 깨끗한 작업 디렉터리에 받습니다.
  2. 의존성을 잠금 파일 기준으로 복원합니다.
  3. 활성 Xcode와 SDK 정보를 로그에 남깁니다.
  4. 서명 자료 없이 최소 빌드를 실행합니다.
  5. 실제 Scheme으로 Archive를 생성합니다.
  6. 서명된 파일을 내보냅니다.
  7. App Store Connect 전송과 처리 상태를 따로 확인합니다.

이 순서는 컴파일 실패와 인증 실패를 구분하는 데 필요합니다. Apple은 Archive를 배포 가능한 앱 산출물을 준비하는 과정으로 설명하며, Archive 생성과 App Store Connect 업로드를 같은 상태로 취급하지 않습니다. 앱 Archive와 배포 문서를 기준으로 각 결과물을 기록해야 합니다.

서명 자료와 접근 경계

iOS 개발자 인증서의 공개 부분만으로는 배포 작업을 완료할 수 없습니다. 코드 서명 인증서와 개인 키, Provisioning Profile, App Store Connect 업로드 인증 정보는 서로 다른 역할을 가집니다. Apple의 인증서와 Provisioning Profile 개요에서도 이 자료들이 별도 관리 대상임을 확인할 수 있습니다.

운영 작업에서는 임시 키체인을 만들어 필요한 작업 동안만 인증서를 가져오는 방식을 고려합니다. 작업이 끝나면 키체인을 잠그거나 삭제하고, 내보낸 파일과 프로파일의 임시 경로도 정리합니다. 개인 키를 저장소 변수, YAML 파일, 출력 로그에 직접 넣어서는 안 됩니다.

App Store Connect API 키는 업로드 인증에 쓰일 수 있지만 코드 서명 인증서와 개인 키를 대신하지는 않습니다. 둘을 하나의 비밀 값으로 합치면 교체 범위와 사고 대응 범위가 불필요하게 커집니다.

self-hosted runner는 실행 환경을 직접 통제하는 대신, 작업이 호스트에 접근할 수 있다는 위험도 함께 가집니다. GitHub는 자체 호스팅 Runner에서 신뢰하지 않는 코드가 실행될 수 있는 경우의 보안 위험을 별도로 경고합니다. GitHub의 자체 호스팅 Runner 보안 지침에 따라 공개 저장소, 외부 기여 브랜치, 승인되지 않은 작업이 배포 호스트를 사용하지 못하게 제한해야 합니다.

다음과 같이 경계를 나눕니다.

  • 일반 테스트 작업과 출시 작업에 다른 라벨을 사용합니다.
  • 배포 Runner는 비공개 저장소와 허용된 브랜치에서만 호출합니다.
  • 비밀 값은 필요한 작업에만 전달합니다.
  • 작업 로그에서 인증서 내용과 API 응답 비밀을 가립니다.
  • 작업 종료 뒤 임시 키체인, 프로파일, 내보내기 파일을 삭제합니다.
  • 인증 자료를 교체할 때 기존 자료의 폐기와 재등록 기록을 남깁니다.

서비스 실행과 복구 기준

터미널에서 Runner 프로그램을 직접 실행하면 해당 세션이 끝난 뒤 작업을 받을 수 없습니다. 운영용 원격 Mac에서는 Runner를 macOS 서비스로 등록하고, 서비스 상태와 로그 위치를 문서화해야 합니다. GitHub의 Runner 서비스 구성 문서는 서비스 설치와 제거 절차를 제공합니다.

복구 검증은 단순히 재부팅 버튼을 누르는 데서 끝나지 않습니다. 호스트 재시작 뒤 다음 조건을 순서대로 확인합니다.

  • [ ] macOS 로그인 또는 서비스 시작 뒤 Runner 프로세스가 실행됩니다.
  • [ ] GitHub 화면에서 Runner가 온라인으로 표시됩니다.
  • [ ] 운영 라벨이 재시작 뒤에도 유지됩니다.
  • [ ] 작은 검증 작업이 해당 호스트로 라우팅됩니다.
  • [ ] 이전 작업의 임시 인증 자료가 남아 있지 않습니다.
  • [ ] Runner 업데이트 실패 시 다시 등록할 절차가 있습니다.
  • [ ] 접근을 철회해야 할 때 등록 해제와 비밀 교체를 함께 수행합니다.

업데이트, 디스크 증가, 캐시 손상, 작업 중단은 서로 다른 복구 문제입니다. 하나의 재시작 명령으로 모두 해결된다고 가정하지 말고, 각 실패 유형에 대한 로그와 담당자를 정합니다.

단계별 출시 검증

검증 단계 성공으로 볼 조건 남겨야 할 증거 실패 분류
의존성 복원 잠금 파일과 일치하는 복원 완료 복원 로그와 도구 버전 의존성 또는 네트워크
컴파일 지정 Scheme의 빌드 명령 완료 Xcode 선택 결과와 빌드 로그 소스 또는 도구 체인
Archive 기대한 앱 Archive 생성 Archive 경로와 식별 정보 프로젝트 또는 설정
서명 내보내기 배포용 파일과 프로파일 일치 내보내기 로그와 파일 목록 인증서 또는 프로파일
업로드 App Store Connect 전송 완료 업로드 결과와 전송 로그 권한 또는 네트워크
처리 확인 백그라운드 처리가 끝나 빌드가 표시됨 플랫폼 상태와 빌드 기록 서버 처리 또는 메타데이터

명령의 종료 상태가 성공이어도 TestFlight에서 빌드가 바로 사용 가능하다는 뜻은 아닙니다. Archive, 서명된 내보내기, 전송, 플랫폼의 후속 처리를 각각 기록해야 실패 위치를 찾을 수 있습니다.

로그와 산출물에는 보관 기간을 정합니다. 인증 자료가 포함될 수 있는 원본 로그는 장기 보관 대상에서 제외하고, 재현에 필요한 명령과 도구 선택 결과만 남기는 방식이 적절합니다. 디버그 기호는 충돌 분석에 필요한 경우에만 접근 권한을 제한해 보관합니다.

출시 전에는 실제 앱과 분리된 탈취 정보 없는 테스트 프로젝트로 실패 경로를 확인합니다. 그 뒤 실제 프로젝트에서 Archive, 서명 내보내기, TestFlight 전송을 수행합니다. 결론은 통과, 수정 필요, 상시 운영 부적합 중 하나로 기록해야 합니다. 단순한 빌드 성공만으로 상시 배포 호스트라고 판정하지 않습니다.

실행 방식 비교

선택지 통제 범위 운영 부담 적합한 상황 피해야 할 상황
GitHub 호스팅 macOS Runner 호스트 설정 통제 제한 호스트 유지 부담이 낮음 표준화된 임시 빌드 특정 Xcode와 고정 환경이 필수인 앱
원격 Mac self-hosted runner Xcode, 키체인, 의존성 통제 가능 재부팅, 보안, 저장 공간 관리 필요 반복 출시와 네이티브 의존성 공개 코드가 실행될 수 있는 무제한 작업
개인 Mac 상시 Runner 물리 장비 직접 통제 개인 장비와 업무가 결합됨 작은 규모의 내부 테스트 장시간 무인 운영과 접근 분리
별도 Mac 구매 장기 환경 통제 초기 구매와 유지 관리 필요 장기간 안정적인 고정 부하 단기 검증이나 간헐적 출시

기존 개발 환경이 Windows 또는 Linux라면 원격 Mac은 Xcode 접근 문제를 해결하지만, 모든 작업을 자동으로 안전하게 만들어 주지는 않습니다. 네트워크 지연, 원격 화면 의존, 인증 자료 관리, 호스트 복구 책임은 여전히 운영자에게 남습니다.

반대로 소규모 앱이 자주 출시되고 특정 Xcode나 네이티브 도구를 고정해야 한다면, 자체 호스팅 방식이 환경 통제 면에서 더 명확할 수 있습니다. 장기적으로 하루 대부분을 무거운 빌드에 사용하는 경우에는 직접 구매한 Mac과 총비용을 비교해야 하며, 물리 USB 장치나 로컬 디버깅이 필수인 경우에는 원격 임대가 맞지 않을 수 있습니다.

FAQ

FAQ는 본문에서 다룬 설정을 실제 검색 의도에 맞춰 다시 확인하는 부분입니다. 특히 빌드 성공과 배포 완료를 같은 상태로 보지 않는 것이 중요합니다.

마무리 판단

현재의 개인 Mac 또는 공유 개발 PC만으로 출시를 계속하면 장비가 꺼졌을 때 작업이 멈추고, Xcode 버전이 바뀌며, 개인 키체인과 배포 작업이 섞이는 문제가 생깁니다. 공개 작업이 상시 호스트에 닿을 위험과 재부팅 뒤 수동 복구 부담도 남습니다.

따라서 먼저 작업 파일을 작성하고, 단기 원격 Mac에서 실제 프로젝트의 Archive, 서명 내보내기, TestFlight 전송을 실행하는 편이 안전합니다. 재시작 복구와 비밀 자료 격리, 단계별 실패 로그까지 통과한 뒤에만 상시 GitHub Actions Runner로 전환해야 합니다.

장비를 직접 구매할 계획이 없고 물리 장치가 필요하지 않다면 RUVCLOUD의 원격 Mac 이용 방식을 검토할 수 있습니다. 단기간 검증부터 시작하려는 경우에는 한국용 Mac 임대 신청 안내를 통해 필요한 환경을 확인한 뒤, 요금과 이용 조건을 현재 빌드 빈도와 비교하는 방식이 적합합니다.