GitHub Actions와 Jenkins로 구축하는 멀티플랫폼 게임 자동 빌드 파이프라인

GitHub Actions의 검증·오케스트레이션과 Jenkins의 플랫폼별 빌드를 연동해 Windows, Android, iOS 게임 빌드를 자동화한다. 캐시, 아티팩트, 버전, 실패 알림까지 안정적으로 설계하는 방법을 정리한다.

✓ 복사 완료!
챕터빌드 자동화 & CI/CD
난이도중급
상태● 검토 완료
대상 엔진
Engine Agnostic
최종 업데이트
GitHub Actions와 Jenkins로 구축하는 멀티플랫폼 게임 자동 빌드 파이프라인

게임의 멀티플랫폼 자동 빌드는 GitHub Actions가 푸시·풀 리퀘스트 검증과 배포 흐름을 제어하고 Jenkins가 Windows·macOS 같은 전용 빌드 머신에서 실제 빌드를 수행하도록 분리하면 운영하기 쉽다. 핵심은 플랫폼별 실행 환경을 격리하고 동일한 커밋 SHA·버전 번호·빌드 산출물을 한 실행 단위로 추적하는 것이다.

왜 GitHub Actions와 Jenkins를 함께 사용할까?

GitHub Actions만으로도 CI를 구성할 수 있지만 iOS 서명 환경이나 고사양 Windows 빌드 머신처럼 사내 장비와 라이선스가 필요한 게임 빌드는 별도 에이전트가 더 적합한 경우가 많다. Jenkins는 이처럼 고정된 빌드 노드, 사내 네트워크, 플랫폼별 도구 체인을 제어하는 데 유리하다.

역할GitHub ActionsJenkins
실행 시작push, pull_request, 수동 실행GitHub Actions 요청 수신
빠른 검증포맷 검사, 단위 테스트, 메타데이터 검사선택 사항
플랫폼 빌드가벼운 빌드 또는 결과 수집Windows, Android, iOS 실제 빌드
비밀 관리GitHub SecretsJenkins Credentials
결과 노출Checks, PR 상태, 릴리스콘솔 로그, 아티팩트, 노드 상태
flowchart LR
  A[Git push 또는 PR] --> B[GitHub Actions]
  B --> C[정적 검사 및 테스트]
  C -->|성공| D[Jenkins 빌드 요청]
  D --> E[Windows 노드]
  D --> F[Android 노드]
  D --> G[macOS iOS 노드]
  E --> H[아티팩트 저장소]
  F --> H
  G --> H
  H --> I[GitHub 배포 상태 및 알림]

멀티플랫폼 빌드 파이프라인은 어떻게 구성할까?

1. 빌드 입력을 하나의 계약으로 고정한다

플랫폼마다 다른 브랜치나 임의의 버전을 쓰면 재현이 어려워진다. Jenkins에 전달하는 값은 최소한 다음으로 제한한다.

  • GIT_SHA: 빌드할 정확한 커밋 SHA
  • BUILD_VERSION: 플레이어에게 보이는 버전. 예: 1.4.0
  • BUILD_NUMBER: CI 실행 번호
  • TARGET: Windows64, Android, iOS
  • CHANNEL: dev, qa, release

Unity 2022.3 LTS 기준이라면 에디터 실행 시 -batchmode, -nographics, -quit, -executeMethod를 사용해 사람이 에디터를 조작하지 않아도 빌드되게 만든다.

// Assets/Editor/BuildGame.cs
using UnityEditor;

public static class BuildGame
{
    public static void BuildWindows()
    {
        BuildPipeline.BuildPlayer(
            new[] { "Assets/Scenes/Boot.unity" },
            "Builds/Windows/Game.exe",
            BuildTarget.StandaloneWindows64,
            BuildOptions.None);
    }
}

2. GitHub Actions에서 검증 후 Jenkins를 호출한다

Jenkins 토큰은 저장소에 쓰지 말고 GitHub Secrets에 보관한다. Jenkins의 buildWithParameters 엔드포인트로 커밋 SHA와 빌드 정보를 전달하면 Jenkins는 브랜치의 최신 상태가 아니라 지정된 SHA를 체크아웃할 수 있다.

name: Build Request

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  request-jenkins:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run validation
        run: ./ci/validate.ps1
        shell: pwsh

      - name: Request Jenkins build
        env:
          JENKINS_URL: ${{ secrets.JENKINS_URL }}
          JENKINS_USER: ${{ secrets.JENKINS_USER }}
          JENKINS_TOKEN: ${{ secrets.JENKINS_TOKEN }}
        run: |
          curl --fail --user "$JENKINS_USER:$JENKINS_TOKEN" \
            --data-urlencode "GIT_SHA=${{ github.sha }}" \
            --data-urlencode "BUILD_VERSION=1.4.0" \
            --data-urlencode "BUILD_NUMBER=${{ github.run_number }}" \
            "$JENKINS_URL/job/game-build/buildWithParameters"

3. Jenkins에서 플랫폼별 노드를 분리한다

Windows 실행 파일, Android APK/AAB, iOS IPA는 요구 도구와 서명 방식이 다르다. 하나의 노드에서 모두 처리하기보다 Jenkins label로 분리하고 플랫폼별 stage에 해당 label을 지정한다.

pipeline {
  agent none

  parameters {
    string(name: 'GIT_SHA')
    string(name: 'BUILD_VERSION')
    string(name: 'BUILD_NUMBER')
  }

  stages {
    stage('Windows') {
      agent { label 'windows-unity-2022' }
      steps {
        checkout([$class: 'GitSCM', branches: [[name: params.GIT_SHA]]])
        bat 'Unity.exe -batchmode -quit -nographics -executeMethod BuildGame.BuildWindows'
        archiveArtifacts artifacts: 'Builds/Windows/**', fingerprint: true
      }
    }

    stage('Android') {
      agent { label 'android-unity-2022' }
      steps {
        checkout([$class: 'GitSCM', branches: [[name: params.GIT_SHA]]])
        bat 'Unity.exe -batchmode -quit -nographics -executeMethod BuildGame.BuildAndroid'
        archiveArtifacts artifacts: 'Builds/Android/**', fingerprint: true
      }
    }
  }
}

자동 빌드가 느리거나 불안정한 이유는 무엇인가?

가장 흔한 원인은 캐시 오염, 공유 작업 공간, 서명 파일의 수동 관리다. 빌드가 실패했을 때 재시도만 늘리기보다 실패 원인을 분류해야 한다.

증상주된 원인대응 방법
같은 SHA가 가끔 실패공유 워크스페이스 잔여 파일작업마다 깨끗한 워크스페이스 사용
빌드 시간이 급증Library 또는 의존성 캐시 미사용Unity 버전·플랫폼·의존성 해시를 캐시 키에 포함
iOS만 실패인증서·프로비저닝 프로파일 만료Jenkins Credentials와 만료 알림으로 관리
결과를 찾기 어려움파일명과 버전 규칙 부재게임명-플랫폼-버전-빌드번호-SHA 규칙 적용

캐시 키는 서로 다른 엔진 버전이나 플랫폼이 섞이지 않게 설계해야 한다. 예를 들어 Unity 캐시는 unity-2022.3.XXf1-android-패키지해시처럼 구성한다. 캐시 적중률보다 잘못된 캐시로 인한 비결정적 빌드를 막는 것이 우선이다.

빌드 결과와 실패를 어떻게 추적할까?

빌드 산출물에는 커밋 SHA, 버전, 빌드 번호를 메타데이터로 남긴다. Jenkins의 archiveArtifacts fingerprint와 별도 아티팩트 저장소를 함께 사용하면 어떤 QA 빌드가 어떤 소스에서 나왔는지 역추적할 수 있다.

실패 알림에는 단순히 “빌드 실패”만 보내지 말고 다음 정보를 포함한다.

  1. 플랫폼과 빌드 노드 이름
  2. 대상 커밋 SHA와 실행 URL
  3. 실패한 stage와 로그의 첫 오류 행
  4. 재시도 가능 여부와 담당 팀

PR 단계에서는 Windows와 Android의 전체 패키징 대신 컴파일·테스트만 수행하고 main 병합 또는 릴리스 태그에서 전체 빌드를 실행하면 비용과 대기 시간을 줄일 수 있다.

자주 묻는 질문 (FAQ)

GitHub Actions만으로 멀티플랫폼 게임 빌드를 운영할 수 있나?

가능하다. 다만 iOS 서명, 사내 라이선스 서버, 고성능 GPU 또는 대용량 캐시처럼 고정 장비가 필요한 환경이라면 Jenkins 에이전트나 self-hosted runner가 더 관리하기 편하다.

Jenkins가 최신 브랜치를 빌드하면 안 되는 이유는 무엇인가?

빌드 요청 이후 새 커밋이 푸시될 수 있기 때문이다. 요청 시점의 GIT_SHA를 전달하고 그 SHA를 체크아웃해야 QA 결과와 배포 파일을 정확히 재현할 수 있다.

플랫폼별 빌드를 병렬 실행해도 안전한가?

노드와 작업 공간, 출력 경로, 캐시 경로가 분리되어 있다면 안전하다. 같은 디렉터리나 동일한 서명 파일을 동시에 수정하는 구조는 피해야 한다.

정리

멀티플랫폼 자동 빌드의 목표는 단순히 버튼을 없애는 일이 아니다. 특정 커밋을 기준으로 같은 결과를 만들고 실패 원인을 빠르게 찾으며 QA와 라이브 운영이 같은 산출물을 신뢰하게 만드는 일이다. GitHub Actions는 변경 검증과 흐름 제어를 맡기고 Jenkins는 플랫폼 전용 빌드 노드를 담당하게 하면 이 책임 분리가 명확해진다.