개발이 알고싶다

젠킨스로 다른 로컬 컴퓨터 원격으로 빌드 본문

카테고리 없음

젠킨스로 다른 로컬 컴퓨터 원격으로 빌드

Aron0513 2026. 6. 30. 11:06

Windows에서 Jenkins로 iOS 자동 빌드/배포 파이프라인 구축하기

Mac 없이 개발하는 팀이 iOS 배포 자동화를 구축한 과정을 정리한 글입니다.
Windows에서 Jenkins를 운영하며 Mac을 원격 빌드 머신으로 활용하는 구조를 다룹니다.



1. 들어가며

iOS 앱을 배포하려면 반드시 macOS + Xcode 환경이 필요합니다.
문제는 팀의 주 개발 환경이 Windows일 때입니다.

이 글에서는 다음 상황을 전제로 합니다.

  • 평소 개발은 Windows에서 한다
  • iOS 빌드를 위한 Mac은 별도로 존재하지만, 개발자가 상시 사용 중이다
  • 배포 자동화를 위해 Jenkins를 Windows에 설치했다
  • Mac은 SSH로 원격 제어하여 빌드 머신으로만 활용한다

결론부터 말하면 동작은 하지만 구조가 복잡해집니다.
그 이유와 과정을 단계별로 정리했습니다.


2. 전체 아키텍처

2-1. 실제 구성 환경

역할 환경
Jenkins 서버 Windows 11
빌드 머신 macOS (상시 켜둠)
연결 방식 SSH (Windows → Mac)
게임 엔진 Unity → Xcode 프로젝트 자동 생성
배포 대상 TestFlight (베타그룹 자동 배포)

2-2. 전체 흐름 다이어그램

[Windows]                    [Mac]                        [Apple]
Jenkins 서버
    │
    ├─ Git Pull ──────────────────────────────────────►│
    │                                                   │
    ├─ SSH 명령 전송 ────────────────────────────────►│
    │                          Unity 빌드               │
    │                              │                    │
    │                              └─ Xcode 프로젝트    │
    │                                  생성             │
    │                                  │                │
    │                                  └─ Archive       │
    │                                      & Export     │
    │                                      │            │
    │                                      └─ .ipa 생성 │
    │                                          │        │
    │                                          └ ─ ─ ─►│ App Store Connect
    │                                                   │
    │◄─ 빌드 결과 리포트 ────────────────────────────── │ TestFlight 배포

핵심은 Jenkins가 명령만 내리고, 실제 빌드는 전부 Mac에서 실행된다는 점입니다.


3. 사전 준비물

Windows (Jenkins 서버)

Jenkins 설치 — jenkins.io LTS 버전 권장

필요한 Jenkins 플러그인

플러그인 역할
SSH Agent Plugin Mac에 SSH 명령 전송
Credentials Plugin 인증 정보 암호화 저장
Pipeline Plugin Jenkinsfile 기반 파이프라인

Mac (빌드 머신)

Xcode — App Store에서 설치, Command Line Tools 필수

xcode-select --install

Unity — 프로젝트와 동일한 버전 (iOS Build Support 모듈 포함)

SSH 서버 활성화

시스템 설정 → 일반 → 공유 → 원격 로그인 ON

Apple Developer 계정

유료 Apple Developer Program 가입 필수 ($99/년)
TestFlight 배포 및 인증서 발급에 사용됩니다.

SSH 키 페어 생성

Windows에서 생성 후 Mac에 공개키를 등록합니다.

# Windows PowerShell
ssh-keygen -t ed25519 -C "jenkins-build-key"

생성된 공개키(~/.ssh/id_ed25519.pub)를 Mac의 ~/.ssh/authorized_keys 에 추가합니다.

# Mac
echo "공개키_내용" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

4. Apple 인증 체계 이해하기

iOS 빌드에서 가장 헷갈리는 부분입니다.
개념을 먼저 잡아야 파이프라인을 이해할 수 있습니다.

4-1. Certificate (인증서)

Apple이 발급하는 "이 개발자를 신뢰한다" 는 서명입니다.

종류 용도
Apple Development 개발/테스트용 빌드
Apple Distribution App Store / TestFlight 배포용 빌드

인증서는 공개키 + 개인키 쌍으로 구성됩니다.
개인키는 절대 외부에 노출되면 안 됩니다.

4-2. Provisioning Profile (프로비저닝 프로파일)

"어떤 앱을, 어떤 기기에, 어떤 권한으로 실행할 수 있는가" 를 정의하는 파일입니다.

Provisioning Profile = 앱 ID + 인증서 + 기기 목록 + 권한(Entitlements)

배포용 파이프라인에서는 App Store Distribution 타입 프로파일을 사용합니다.

4-3. Keychain이란?

macOS의 암호화된 자격증명 저장소입니다.
인증서와 개인키가 여기에 저장됩니다.

Xcode가 빌드 시 인증서를 사용할 때 Keychain에서 꺼내 씁니다.
문제는 SSH 원격 세션에서는 Keychain이 기본적으로 잠겨 있다는 것입니다.

그래서 파이프라인에서 아래 명령으로 명시적으로 잠금을 해제해야 합니다.

security unlock-keychain -p "비밀번호" ~/Library/Keychains/login.keychain-db

Jenkins에서 Mac을 원격 제어할 때 빌드 실패의 상당수가
이 Keychain 잠금 문제에서 비롯됩니다.

4-4. App Store Connect API Key

Apple의 자동화 API에 접근하기 위한 키입니다.
TestFlight 업로드 자동화에 필수입니다.

발급 경로: App Store Connect → 사용자 및 액세스 → 통합 → App Store Connect API

발급 시 세 가지 정보가 생성됩니다.

항목 설명
Issuer ID API 발급자 식별자
Key ID API 키 식별자
.p8 파일 실제 인증에 사용하는 개인키 파일 (단 1회 다운로드 가능)

권한(Role) 설정

역할 권한
Admin 모든 권한 (권장하지 않음)
App Manager 앱 관리, TestFlight 배포 가능
Developer 빌드 업로드만 가능, 배포 불가

자동화 배포까지 하려면 App Manager 이상이 필요합니다.


5. Jenkins Credentials 관리

파이프라인에서 사용하는 민감 정보는 코드에 직접 쓰지 않고,
Jenkins Credentials에 등록해서 사용합니다.

대시보드 → Manage Jenkins → Credentials → System → Global credentials

필요한 Credentials 종류

ID (예시) 타입 내용
mac-ssh-key SSH Username with private key Mac 원격 접속용 SSH 개인키
appstore-api-key Secret file App Store Connect .p8 파일
appstore-issuer-id Secret text Issuer ID
appstore-key-id Secret text Key ID
keychain-password Secret text Mac Keychain 비밀번호

Jenkinsfile에서 사용하는 방법

pipeline {
    environment {
        KEYCHAIN_PASS = credentials('keychain-password')
    }
    stages {
        stage('Unlock Keychain') {
            steps {
                sshagent(['mac-ssh-key']) {
                    sh """
                        ssh user@mac-ip 'security unlock-keychain -p "${KEYCHAIN_PASS}" ~/Library/Keychains/login.keychain-db'
                    """
                }
            }
        }
    }
}

credentials() 함수로 꺼낸 값은 빌드 로그에서 자동으로 마스킹 처리됩니다.


6. 파이프라인 단계별 설명

전체 파이프라인은 크게 준비 → 빌드 → 배포 세 구간으로 나뉩니다.

Stage 1:  소스코드 체크아웃 (Git Pull)
Stage 2:  Mac SSH 연결 확인
Stage 3:  Keychain 잠금 해제
Stage 4:  Unity 빌드 실행 → Xcode 프로젝트 생성
Stage 5:  Provisioning Profile 설치
Stage 6:  Xcode Archive (앱 아카이브 생성)
Stage 7:  IPA Export (배포용 .ipa 파일 추출)
Stage 8:  App Store Connect에 업로드
Stage 9:  TestFlight 베타그룹 배포
Stage 10: 결과 알림 (슬랙/메일 등)

주요 명령어

Unity 헤드리스 빌드 (Mac에서 실행)

/Applications/Unity/Hub/Editor/{버전}/Unity.app/Contents/MacOS/Unity \
  -quit \
  -batchmode \
  -projectPath "/path/to/project" \
  -executeMethod BuildScript.BuildiOS \
  -logFile "/tmp/unity_build.log"

Xcode Archive

xcodebuild archive \
  -project "Unity-iPhone.xcodeproj" \
  -scheme "Unity-iPhone" \
  -configuration Release \
  -archivePath "/tmp/build.xcarchive"

IPA Export

xcodebuild -exportArchive \
  -archivePath "/tmp/build.xcarchive" \
  -exportPath "/tmp/export" \
  -exportOptionsPlist "ExportOptions.plist"

ExportOptions.plist 에 배포 방식(app-store), 팀 ID, 프로비저닝 프로파일 정보가 들어갑니다.

TestFlight 업로드

xcrun altool --upload-app \
  --type ios \
  --file "/tmp/export/앱이름.ipa" \
  --apiKey "KEY_ID" \
  --apiIssuer "ISSUER_ID"

7. TestFlight란?

Apple이 제공하는 공식 베타 테스트 배포 플랫폼입니다.
App Store에 출시하기 전 테스터에게 앱을 배포할 수 있습니다.

내부 테스터 vs 외부 테스터

구분 내부 테스터 외부 테스터
대상 App Store Connect 팀원 이메일 초대된 외부인
최대 인원 25명 10,000명
심사 불필요 첫 빌드만 심사 필요
사용 목적 팀 내부 QA 외부 베타 테스트

베타그룹

테스터를 그룹으로 묶어 관리할 수 있습니다.
"개발팀 QA 그룹", "외부 베타 테스터 그룹" 처럼 구분하면
빌드를 선택적으로 배포할 수 있습니다.

App Store Connect API를 통해 특정 그룹에 자동 배포도 가능합니다.

# 업로드 후 그룹 자동 배포 (App Store Connect API 활용)
curl -X POST "https://api.appstoreconnect.apple.com/v1/betaGroups/{groupId}/relationships/builds" \
  -H "Authorization: Bearer {JWT_TOKEN}" \
  -d '{"data": [{"type": "builds", "id": "빌드ID"}]}'

TestFlight 배포 흐름

Jenkins 파이프라인
    └─ .ipa 업로드
        └─ Apple 처리 대기 (5~15분)
            └─ 베타 그룹 자동 배포
                └─ 테스터 기기에서 TestFlight 앱으로 설치

업로드 직후 바로 설치되는 것이 아니라, Apple 처리 시간이 있습니다.


8. 삽질 포인트 / 주의사항

Keychain 원격 잠금 문제

SSH 세션에서는 Keychain이 잠긴 상태입니다.
security unlock-keychain 명령을 빌드 직전에 반드시 실행해야 하며,
Mac이 재시작되거나 절전 모드에서 깨어나면 다시 잠깁니다.

.p8 파일 단 1회 다운로드

App Store Connect API Key의 .p8 파일은 발급 시 딱 한 번만 다운로드 가능합니다.
분실하면 새로 발급해야 합니다.
Jenkins Credentials에 등록한 뒤 원본도 안전한 곳에 백업해두세요.

SSH 환경변수 누락

Mac 로컬 터미널과 달리 SSH 세션은 ~/.zshrc, ~/.bash_profile 을 자동으로 로드하지 않습니다.
PATH 에 Unity, Xcode 경로가 없어서 명령어를 못 찾는 경우가 많습니다.

# SSH 명령에 경로를 직접 지정하거나
export PATH="/usr/local/bin:/usr/bin:/Applications/Unity/...":$PATH

# 또는 ~/.ssh/environment 파일에 환경변수 등록

인증서 만료 주기

인증서 종류 유효기간
Apple Development 1년
Apple Distribution 1년
Provisioning Profile 최대 1년

만료 전에 갱신하지 않으면 파이프라인이 갑자기 실패합니다.
만료일을 캘린더에 등록해두는 것을 권장합니다.

Unity 헤드리스 빌드 로그 확인

빌드 실패 원인이 Unity인지 Xcode인지 구분하기 어렵습니다.
-logFile 옵션으로 로그 파일을 저장하고,
파이프라인 실패 시 Jenkins가 해당 로그를 아카이브하도록 설정하면 디버깅이 쉬워집니다.

post {
    failure {
        sshagent(['mac-ssh-key']) {
            sh "scp user@mac-ip:/tmp/unity_build.log unity_build.log"
        }
        archiveArtifacts artifacts: 'unity_build.log'
    }
}

9. 마치며

이 구조의 장단점

Windows에 Jenkins를 둔 이유

우리회사 Mac은 개발자의 주 작업 머신이라 항상 켜져 있다고 보장하기 어려웠고,
Windows PC를 상시 운영 서버로 활용했습니다.
그 결과 SSH 브릿지가 추가되며 구조가 복잡해졌습니다.

  Mac에 Jenkins Windows에 Jenkins (현재)
iOS 빌드 Xcode 직접 접근, 단순 SSH 경유, 복잡
Android 빌드 Android SDK 설치 시 동일 머신에서 가능 Mac 경유 또는 별도 처리
Keychain 접근 로컬 세션, 안정적 원격 세션, 잠금 문제 발생
환경변수 로컬 셸 그대로 사용 SSH 세션 별도 설정 필요
단점 Jenkins가 Mac 리소스 점유 설정 복잡도 증가

macOS는 Xcode뿐 아니라 Android SDK도 설치 가능하기 때문에,
Mac 한 대로 iOS와 Android 빌드를 모두 처리하는 구조가 실제로는 더 단순합니다.

처음 환경을 구성할 때 Jenkins 서버 위치를 신중하게 결정하는 것이
나중의 복잡도를 크게 줄여줍니다.

그럼에도 이 구조를 선택한 상황이라면 —
SSH 키, Keychain, App Store Connect API, Credentials 관리까지
확실히 이해하고 나면 유지보수는 충분히 가능합니다.

이 글이 그 과정에 조금이나마 도움이 됐으면 합니다.