| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- 그래프
- 게임개발
- unity
- ㅘ
- 언리얼5 Unreal5
- Enhanced Input
- 언리얼 Animation
- 언리얼 c++
- .
- 언리얼5
- Unreal5
- 언리얼 C++ 페이드
- 게임 포트폴리오
- 알고리즘
- 디펜스
- 샌드박스 #게임기획 #게임이론
- Today
- Total
개발이 알고싶다
젠킨스로 다른 로컬 컴퓨터 원격으로 빌드 본문
Windows에서 Jenkins로 iOS 자동 빌드/배포 파이프라인 구축하기
Mac 없이 개발하는 팀이 iOS 배포 자동화를 구축한 과정을 정리한 글입니다.
Windows에서 Jenkins를 운영하며 Mac을 원격 빌드 머신으로 활용하는 구조를 다룹니다.
목차
1. 들어가며 · 2. 전체 아키텍처 · 3. 사전 준비물
4. Apple 인증 체계 이해하기 · 5. Jenkins Credentials 관리
6. 파이프라인 단계별 설명 · 7. TestFlight란?
8. 삽질 포인트 / 주의사항 · 9. 마치며
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 관리까지
확실히 이해하고 나면 유지보수는 충분히 가능합니다.
이 글이 그 과정에 조금이나마 도움이 됐으면 합니다.