본문 바로가기

플랫폼별 빌드와 패키징

프로젝트와 콘텐츠를 점검한 뒤 Windows·Android·iOS별 실행 파일로 빌드하고 패키징합니다.

에디터에서 잘 돌아가는 게임을 친구에게 보내려면, 친구의 컴퓨터에는 언리얼 에디터도 프로젝트 폴더도 없다는 사실부터 생각해야 합니다. 게임을 플레이어에게 전달하려면 개발 환경의 프로젝트를 플레이어가 혼자 실행할 수 있는 형태로 만들어야 하고, 이 과정을 배포(deployment)라고 합니다. 코드를 빌드하는 것은 그 일부일 뿐이며, 패키징(packaging)에는 대상 플랫폼에 맞게 콘텐츠를 변환하고 배포할 파일을 모으는 단계도 들어 있습니다.

CPU, GPU 병목 현상 해결까지 성능을 다듬었다면, 이 글에서는 Windows, Android, iOS 같은 플랫폼에 맞춰 게임을 빌드하고 패키징하는 과정을 살펴봅니다. 패키징의 단계를 나눠 이해하고, 패키징 전에 확인할 설정, 플랫폼별로 챙길 것, 자동화와 패키징 뒤의 확인까지 차례로 다룹니다.


패키징은 여러 단계로 이루어진다

코드 빌드·콘텐츠 변환·배포 파일 준비를 구분합니다

선택한 UAT 범위에서 Build 바이너리→Cook 런타임 에셋→Stage 디렉터리→Package 형식→Archive 사본의 산출물 상태가 구체적이다.

UNREAL C++ · CH10-1

코드 빌드·콘텐츠 변환·배포 파일 준비를 구분합니다

Build·Cook·Stage·Package·Archive마다 산출물과 실패 원인이 다릅니다. 선택한 플랫폼과 UAT 옵션에 따라 수행 범위가 달라지므로 로그를 단계별로 읽습니다.

핵심 관계선택한 UAT 단계의 산출물 흐름이다. 코드의 Build 바이너리와 콘텐츠의 Cook 런타임 에셋은 Stage 디렉터리에 모이고 Package가 플랫폼별 배포 형식을 만든다. Archive를 선택하면 완성 산출물의 보관 사본을 만든다. 실행 범위는 UAT 옵션과 플랫폼에 따라 다르다.코드맵·에셋Build: 바이너리Cook: 런타임 에셋Stage: 준비 디렉터리Package: 배포 형식Archive 선택 시보관 사본
핵심 관계선택한 UAT 단계의 산출물 흐름이다. 코드의 Build 바이너리와 콘텐츠의 Cook 런타임 에셋은 Stage 디렉터리에 모이고 Package가 플랫폼별 배포 형식을 만든다. Archive를 선택하면 완성 산출물의 보관 사본을 만든다. 실행 범위는 UAT 옵션과 플랫폼에 따라 다르다.코드맵·에셋Build바이너리Cook런타임 에셋Stage준비 디렉터리Package플랫폼 배포 형식Archive 선택 시보관 사본

선택한 UAT 단계의 산출물 흐름이다. 코드의 Build 바이너리와 콘텐츠의 Cook 런타임 에셋은 Stage 디렉터리에 모이고 Package가 플랫폼별 배포 형식을 만든다. Archive를 선택하면 완성 산출물의 보관 사본을 만든다. 실행 범위는 UAT 옵션과 플랫폼에 따라 다르다.

관계도 해설과 예제 원자료
패키징 단계별로 만드는 결과
단계만드는 결과실패 시 확인
Build타깃 플랫폼·구성의 코드 바이너리컴파일러, Target·Build.cs와 모듈·플러그인 의존성
Cook해당 플랫폼 런타임용 맵·에셋쿠킹 대상, 동적 로드 경로, 에셋 참조와 지원 형식
Stage실행 파일·콘텐츠·설정 등을 모은 디렉터리필수 파일, 플러그인 런타임 파일과 포함 경로
Package플랫폼별 배포 형식과 콘텐츠 컨테이너플랫폼 도구, 압축·컨테이너 및 서명 설정
Archive선택한 보관 경로의 산출물 사본같은 빌드 번호의 파일·로그·심볼 보존 정책
Build
만드는 결과: 타깃 플랫폼·구성의 코드 바이너리
실패 시 확인: 컴파일러, Target·Build.cs와 모듈·플러그인 의존성
Cook
만드는 결과: 해당 플랫폼 런타임용 맵·에셋
실패 시 확인: 쿠킹 대상, 동적 로드 경로, 에셋 참조와 지원 형식
Stage
만드는 결과: 실행 파일·콘텐츠·설정 등을 모은 디렉터리
실패 시 확인: 필수 파일, 플러그인 런타임 파일과 포함 경로
Package
만드는 결과: 플랫폼별 배포 형식과 콘텐츠 컨테이너
실패 시 확인: 플랫폼 도구, 압축·컨테이너 및 서명 설정
Archive
만드는 결과: 선택한 보관 경로의 산출물 사본
실패 시 확인: 같은 빌드 번호의 파일·로그·심볼 보존 정책

단계 완료가 실제 기기 실행·스토어 심사 통과를 뜻하지 않습니다. 시작 맵·동적 로드 콘텐츠·플러그인·런타임 의존성을 깨끗한 대상 환경에서 확인하고, 동일 빌드의 파일·로그·심볼을 보관합니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
Build·Cook

선택한 UAT 범위에서 코드와 런타임 에셋을 만든다.

    • 두 산출물의 역할과 선택한 실행 범위를 구분한다.
    Stage·Package

    배포 디렉터리를 준비해 패키지 형식으로 묶는다.

    • Package: 패키지 형식
    • Stage는 소스를 컴파일하는 동작이 아니다.
    Archive

    선택한 보관 위치로 산출물을 복사한다.

    • Archive: 보관 사본
    • 입력: 완성 산출물
    • 보관 사본 생성과 서비스에 배포하는 작업은 다르다.

    코드 빌드·콘텐츠 변환·배포 파일 준비를 구분합니다

    선택한 UAT 범위에서 Build 바이너리→Cook 런타임 에셋→Stage 디렉터리→Package 형식→Archive 사본의 산출물 상태가 구체적이다.

    UNREAL C++ · CH10-1

    코드 빌드·콘텐츠 변환·배포 파일 준비를 구분합니다

    Build·Cook·Stage·Package·Archive마다 산출물과 실패 원인이 다릅니다. 선택한 플랫폼과 UAT 옵션에 따라 수행 범위가 달라지므로 로그를 단계별로 읽습니다.

    핵심 관계선택한 UAT 단계의 산출물 흐름이다. 코드의 Build 바이너리와 콘텐츠의 Cook 런타임 에셋은 Stage 디렉터리에 모이고 Package가 플랫폼별 배포 형식을 만든다. Archive를 선택하면 완성 산출물의 보관 사본을 만든다. 실행 범위는 UAT 옵션과 플랫폼에 따라 다르다.코드맵·에셋Build: 바이너리Cook: 런타임 에셋Stage: 준비 디렉터리Package: 배포 형식Archive 선택 시보관 사본
    핵심 관계선택한 UAT 단계의 산출물 흐름이다. 코드의 Build 바이너리와 콘텐츠의 Cook 런타임 에셋은 Stage 디렉터리에 모이고 Package가 플랫폼별 배포 형식을 만든다. Archive를 선택하면 완성 산출물의 보관 사본을 만든다. 실행 범위는 UAT 옵션과 플랫폼에 따라 다르다.코드맵·에셋Build바이너리Cook런타임 에셋Stage준비 디렉터리Package플랫폼 배포 형식Archive 선택 시보관 사본

    선택한 UAT 단계의 산출물 흐름이다. 코드의 Build 바이너리와 콘텐츠의 Cook 런타임 에셋은 Stage 디렉터리에 모이고 Package가 플랫폼별 배포 형식을 만든다. Archive를 선택하면 완성 산출물의 보관 사본을 만든다. 실행 범위는 UAT 옵션과 플랫폼에 따라 다르다.

    관계도 해설과 예제 원자료
    패키징 단계별로 만드는 결과
    단계만드는 결과실패 시 확인
    Build타깃 플랫폼·구성의 코드 바이너리컴파일러, Target·Build.cs와 모듈·플러그인 의존성
    Cook해당 플랫폼 런타임용 맵·에셋쿠킹 대상, 동적 로드 경로, 에셋 참조와 지원 형식
    Stage실행 파일·콘텐츠·설정 등을 모은 디렉터리필수 파일, 플러그인 런타임 파일과 포함 경로
    Package플랫폼별 배포 형식과 콘텐츠 컨테이너플랫폼 도구, 압축·컨테이너 및 서명 설정
    Archive선택한 보관 경로의 산출물 사본같은 빌드 번호의 파일·로그·심볼 보존 정책
    Build
    만드는 결과: 타깃 플랫폼·구성의 코드 바이너리
    실패 시 확인: 컴파일러, Target·Build.cs와 모듈·플러그인 의존성
    Cook
    만드는 결과: 해당 플랫폼 런타임용 맵·에셋
    실패 시 확인: 쿠킹 대상, 동적 로드 경로, 에셋 참조와 지원 형식
    Stage
    만드는 결과: 실행 파일·콘텐츠·설정 등을 모은 디렉터리
    실패 시 확인: 필수 파일, 플러그인 런타임 파일과 포함 경로
    Package
    만드는 결과: 플랫폼별 배포 형식과 콘텐츠 컨테이너
    실패 시 확인: 플랫폼 도구, 압축·컨테이너 및 서명 설정
    Archive
    만드는 결과: 선택한 보관 경로의 산출물 사본
    실패 시 확인: 같은 빌드 번호의 파일·로그·심볼 보존 정책

    단계 완료가 실제 기기 실행·스토어 심사 통과를 뜻하지 않습니다. 시작 맵·동적 로드 콘텐츠·플러그인·런타임 의존성을 깨끗한 대상 환경에서 확인하고, 동일 빌드의 파일·로그·심볼을 보관합니다.

    이 예의 조건

    • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
    • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
    예의 값과 변화
    관찰·상황값·보충 정보변화·이유
    Build·Cook

    선택한 UAT 범위에서 코드와 런타임 에셋을 만든다.

      • 두 산출물의 역할과 선택한 실행 범위를 구분한다.
      Stage·Package

      배포 디렉터리를 준비해 패키지 형식으로 묶는다.

      • Package: 패키지 형식
      • Stage는 소스를 컴파일하는 동작이 아니다.
      Archive

      선택한 보관 위치로 산출물을 복사한다.

      • Archive: 보관 사본
      • 입력: 완성 산출물
      • 보관 사본 생성과 서비스에 배포하는 작업은 다르다.

      위 그림은 패키징이 만드는 산출물의 흐름을 단계별로 보여 줍니다. 코드는 빌드(Build) 단계에서 바이너리가 되고, 맵과 에셋은 쿡(Cook) 단계에서 해당 플랫폼의 런타임용 에셋으로 바뀝니다. 두 결과물은 스테이지(Stage) 단계에서 하나의 준비 디렉터리에 모이고, 패키지(Package) 단계가 그것을 플랫폼별 배포 형식으로 묶습니다. 아카이브(Archive)를 고르면 완성된 산출물의 보관 사본도 만듭니다. 실제로 어느 단계까지 실행되는지는 선택한 플랫폼과 UAT 옵션에 따라 다르므로 로그를 단계별로 읽어야 합니다.

      그림의 표는 단계마다 만드는 결과와 실패했을 때 확인할 곳을 정리합니다. 빌드는 대상 플랫폼과 구성의 코드 바이너리를 만들며, 실패하면 컴파일러, Target 파일과 Build.cs, 모듈과 플러그인의 의존성을 봅니다. 쿡은 해당 플랫폼의 런타임용 맵과 에셋을 만들고, 실패하면 쿠킹 대상, 동적으로 불러오는 경로, 에셋 참조와 지원 형식을 봅니다. 스테이지는 실행 파일, 콘텐츠, 설정 같은 것을 모은 디렉터리를 만들며, 실패하면 필수 파일, 플러그인의 런타임 파일, 포함 경로를 확인합니다. 패키지는 플랫폼별 배포 형식과 콘텐츠 컨테이너를 만들고, 플랫폼 도구, 압축과 컨테이너, 서명 설정을 확인합니다. 아카이브는 선택한 보관 경로에 사본을 만들며, 같은 빌드 번호의 파일, 로그, 심볼을 보존하는 정책을 확인합니다.

      단계 그림도 빌드와 쿡, 스테이지와 패키지, 아카이브를 차례로 따라가며, 스테이지는 소스를 컴파일하는 동작이 아니고 보관 사본을 만드는 것은 서비스에 배포하는 일과 다르다는 점을 짚습니다. 그림 아래의 설명처럼 단계가 완료되었다고 실제 기기에서 실행되거나 스토어 심사를 통과한다는 뜻은 아니므로, 시작 맵, 동적으로 불러오는 콘텐츠, 플러그인, 런타임 의존성을 깨끗한 대상 환경에서 확인하고, 같은 빌드의 파일, 로그, 심볼을 함께 보관합니다. 이렇게 만든 패키지 게임은 언리얼 에디터가 설치되지 않은 환경에서도 혼자 실행할 수 있습니다.


      패키징 전에 확인할 설정

      패키징이 제대로 되려면 몇 가지 설정을 먼저 해 둬야 합니다. 에디터에서 플레이할 때는 지금 연 맵과 켜진 플러그인으로 바로 실행되지만, 패키지는 프로젝트 설정에 적힌 시작 맵과 빌드 구성, 그리고 쿠킹된 콘텐츠만으로 실행되기 때문입니다. 아래에서는 프로젝트 설정의 맵과 패키징 항목을 먼저 보고, 이어서 패키지에 함께 들어갈 플러그인과 콘텐츠를 점검합니다.

      프로젝트 설정

      프로젝트 설정의 Maps & Modes에서 기본 맵을 정합니다. Editor Startup Map은 에디터를 시작할 때 열릴 맵이고, Game Default Map은 게임을 시작할 때 불러올 기본 맵으로, 패키지 게임이 무엇으로 시작할지를 정하므로 가장 중요합니다. Transition Map은 심리스 트래블(seamless travel)로 맵을 바꿀 때 거쳐 가는 작은 중간 맵이며, 일반적인 로딩 화면을 만들어 주는 설정은 아닙니다.

      Packaging 항목에서는 패키징 방식을 정합니다. Build Configuration에서 Development, Shipping, DebugGame 같은 구성 가운데 목적에 맞는 것을 고르는데, 최종 배포에는 보통 Shipping을 씁니다. Shipping은 배포용 최적화를 적용하고 일반적인 콘솔과 통계 기능을 제한합니다. 디버그 심볼을 만들고 보관할지는 별도의 설정이며, 실제 성능은 대상 기기에서 확인해야 합니다. Full Rebuild를 켜면 패키징할 때마다 UAT에 -clean이 붙어, 코드를 처음부터 다시 빌드하고 그 플랫폼의 이전 쿡 결과(Saved/Cooked/[플랫폼])도 지운 뒤 다시 쿡합니다. 저장 게임 같은 사용자 데이터까지 지우는 기능은 아닙니다. For Distribution은 스토어에 배포할 때 체크하는 옵션으로, 플랫폼별 서명 같은 추가 요구 사항을 켭니다. 패키지 압축 관련 옵션을 켜면 최종 빌드의 크기를 줄일 수 있습니다.

      그 밖에 Supported Platforms에서 프로젝트가 지원할 플랫폼을 켜고 끄고, Target Hardware에서 데스크톱과 모바일 가운데 대상 기기의 종류를 정해 렌더링 품질과 기능 제한에 반영합니다. Description에는 게임 이름, 버전, 저작권 같은 기본 정보를 넣습니다. iOS, Android, Linux 같은 플랫폼마다 SDK 경로, 서명 정보, 앱 아이콘, 스플래시 화면 같은 고유한 설정도 각 플랫폼 항목에서 따로 합니다.

      더 깊이: 빌드 구성마다 빠지는 것

      Build Configurations 문서는 다섯 가지 구성을 이렇게 나눕니다. Debug는 엔진과 게임 코드를 모두 최적화 없이 빌드하고, DebugGame은 게임 코드만 최적화 없이 빌드합니다. Development는 시간이 가장 많이 드는 최적화를 뺀 나머지 엔진과 게임 코드 최적화를 모두 켭니다. Shipping은 최적의 성능을 위한 배포용 구성으로, 콘솔 명령과 통계, 프로파일링 도구를 빼고 빌드합니다. Test는 Shipping 구성에 일부 콘솔 명령과 통계, 프로파일링 도구를 다시 켠 구성입니다.

      이 차이 때문에 성능을 재는 구성을 고를 때 생각할 점이 생깁니다. Development는 시간이 많이 드는 최적화 일부가 빠져 있어 Shipping과 같은 코드가 아니고, Shipping에는 측정 도구가 없습니다. 출시할 빌드와 가까운 조건에서 통계와 프로파일러를 써야 한다면 Test 구성을 검토합니다. 또 구성과 별개로 빌드 대상(target)도 있어서, Client나 Server 대상은 프로젝트에 <GAME_NAME>Client.Target.cs나 <GAME_NAME>Server.Target.cs 파일이 있을 때만 고를 수 있습니다.

      플러그인과 콘텐츠

      쓰지 않는 플러그인은 꺼서 빌드 크기와 빌드 시간을 줄입니다. 서드파티 플러그인은 대상 플랫폼을 지원하는지 확인해야 합니다.

      9장의 메모리 관리와 최적화 기법과 렌더링 최적화에서 다룬 LOD, 텍스처 압축, 머티리얼 최적화 같은 기법은 모두 패키지 게임에 직접 영향을 줍니다. 에디터와 패키지는 콘텐츠, 품질, 진단 기능의 부담이 서로 다르므로, 최종 성능은 대상 패키지에서 측정해야 합니다.


      에디터에서 패키징하기

      에디터에서 패키징하는 방법은 간단합니다. 언리얼 엔진 5의 메인 툴바에서 플랫폼(Platforms) 메뉴를 열고, 대상 플랫폼과 빌드 구성, 타깃을 확인한 뒤, 그 플랫폼의 Package Project를 실행하고 결과를 저장할 출력 디렉터리를 고릅니다. 같은 메뉴에서 콘텐츠만 쿠킹하는 항목도 고를 수 있으며, 메뉴 이름과 지원 항목은 설치한 엔진과 SDK에 따라 다릅니다.

      패키징은 프로젝트가 크거나 처음 빌드할 때 특히 오래 걸릴 수 있습니다. 진행하는 동안 출력 로그를 보며 오류와 경고를 확인합니다.

      빌드가 끝났다고 패키지가 완성된 것은 아니다

      Windows의 Development Game 타깃을 시험한다고 해 봅시다. 프로젝트 경로와 출력 경로는 뒤에 나올 UAT 명령의 대괄호 부분처럼 실제 경로로 바꾸고, 쓸 수 있는 새 출력 디렉터리를 고릅니다. Game Default Map은 플레이용 맵 A로, Editor Startup Map은 작업용 맵 B로 정했다면, 패키지가 처음 여는 맵은 A여야 합니다. 에디터가 B로 열렸다는 사실은 패키지의 시작 설정을 확인한 것이 아닙니다. 또 Game 타깃의 시작 맵과 서버(Server) 타깃이 쓰는 시작 맵도 구별해야 합니다.

      로그를 보니 빌드는 끝났지만 쿡이 에셋 X의 지원 형식 오류로 멈췄다면, 코드 바이너리가 만들어진 것과 전달할 수 있는 게임 패키지가 완성된 것은 다릅니다. X의 플랫폼 형식과 쿠킹 대상, 참조를 조사해 고친 뒤, 이어지는 단계와 새 산출물을 확인합니다. 실패하기 전에 남아 있던 출력 파일을 성공한 최신 빌드로 착각해 전달하면 안 됩니다. Epic의 Packaging Projects 문서도 빌드, 쿡, 스테이지, 패키지의 단계와 Game Default Map의 역할을 구분해 설명합니다.


      플랫폼마다 챙길 것

      플랫폼마다 개발 환경, 서명, 실제 배포 산출물이 다르므로 각각 따로 확인합니다. 같은 프로젝트라도 Windows에서는 실행 파일이 든 폴더가, Android에서는 서명한 APK나 AAB가, iOS에서는 Mac과 Xcode로 빌드한 앱이 결과물입니다. 그래서 한 플랫폼의 패키징이 성공했다고 다른 플랫폼도 준비되었다고 볼 수 없으며, 아래 항목은 플랫폼마다 먼저 갖출 도구와 설정, 시험 방법을 정리합니다.

      Windows

      Windows는 가장 흔한 대상이고 패키징 과정도 비교적 간단합니다. 결과물로 실행 파일(.exe)과 데이터 폴더가 들어 있는 디렉터리가 만들어집니다. 사용자 컴퓨터에는 게임이 쓰는 그래픽 API의 런타임(DirectX나 Vulkan)과, 게임을 빌드한 Visual Studio 버전에 맞는 Visual C++ 재배포 패키지가 필요합니다. 이런 필수 구성 요소는 패키징 설정에서 설치 프로그램을 함께 넣도록 하거나, 게임 설치 과정에서 함께 설치하게 합니다. Steam이나 Epic Games Store 같은 스토어에 낸다면 그 스토어의 SDK를 통합하고 배포 도구를 씁니다.

      Android

      Android는 설정할 것이 많습니다. 먼저 쓰는 엔진 버전이 요구하는 Android SDK, NDK, JDK의 조합을 설치하고, 프로젝트 설정의 Platforms 아래 Android SDK 항목에서 경로를 지정합니다. 최신 도구라고 해서 쓰는 엔진 버전과 호환된다고 가정하지 않습니다.

      배포 형식도 구분합니다. 기기에 직접 설치할 때는 APK를, 스토어에 제출할 때는 AAB를 씁니다. 키스토어(keystore)를 만들어 잘 보관하고, Android 설정의 배포 서명(Distribution Signing)에 연결합니다. 제출 형식과 서명 요건은 대상 스토어의 현재 요구 사항을 확인합니다.

      앱이 카메라, 저장 공간, 인터넷 같은 기능을 쓰려면 AndroidManifest.xml에 권한을 선언해야 합니다. 엔진이 일부 필수 권한을 자동으로 넣지만, 직접 추가해야 하는 권한도 있을 수 있습니다. 텍스처 압축은 ASTC(Adaptive Scalable Texture Compression)를 지원하는 기기라면, 한 가지 형식만 쓰는 Android ASTC 패키징과 여러 형식을 담는 Android(Multi)의 Include ASTC Textures, 형식 우선순위 설정을 구별합니다. 메뉴의 위치와 이름은 쓰는 엔진 버전을 따릅니다. 모바일 앱은 다운로드 크기 제한이 있는 경우가 많으므로 필요 없는 콘텐츠를 빼고 압축을 활용해 크기를 줄이며, 개발하는 동안에는 기기의 개발자 옵션에서 USB 디버깅을 켜고 앱을 직접 설치해 시험합니다.

      iOS

      iOS는 Apple의 생태계 때문에 정해진 요구 사항이 있습니다. 이 교재의 C++ 프로젝트는 호환되는 Mac과 Xcode 환경에서 iOS 바이너리를 빌드해야 하며, Windows에서 개발한다면 원격 Mac 빌드 구성을 검토합니다. Xcode와 iOS SDK는 엔진 버전의 개발 요구 사항과 배포 대상이 요구하는 조합을 씁니다.

      앱에 서명하려면 Apple Developer Program에 가입해 앱 ID, 개발 인증서, 프로비저닝 프로파일(provisioning profile)을 만들고, 프로젝트 설정의 Platforms 아래 iOS 항목에서 번들 식별자(Bundle Identifier), 서명 인증서, 프로비저닝 프로파일을 지정합니다. 만든 .ipa 파일은 실제 iOS 기기에 설치해 시험하고, TestFlight로 베타 테스트를 거쳐 App Store Connect로 최종 제출합니다.

      콘솔

      콘솔 플랫폼은 개발 키트와 비밀 유지 협약(NDA)이 필요하고, 더 엄격한 지침과 인증 과정을 거쳐야 합니다. 각 콘솔 제조사의 개발자 프로그램에 정식으로 등록하고, 개발과 시험에 쓸 전용 개발 키트 하드웨어를 갖추고, 플랫폼별 SDK를 프로젝트에 통합합니다. 메모리 사용량, 로딩 시간, 프레임 속도, UI 지침처럼 각 콘솔이 요구하는 기준을 맞춰야 하며, 최종 게임은 제조사의 인증 시험을 통과해야 배포할 수 있습니다.


      빌드 자동화와 CI/CD

      규모가 크거나 업데이트가 잦은 프로젝트에서는 빌드와 패키징을 자동화해야 합니다. 언리얼 엔진은 명령 프롬프트에서 RunUAT.bat 스크립트로 패키징을 자동화할 수 있게 해 주며, 이 스크립트를 언리얼 자동화 도구(Unreal Automation Tool, UAT)라고 부릅니다. Windows Game 타깃을 시작하는 예는 다음과 같습니다.

      RunUAT.bat BuildCookRun -project="[ProjectPath].uproject" -noP4 -platform=Win64 -clientconfig=Development -build -cook -stage -pak -archive -archivedirectory="[OutputPath]"

      -build, -cook, -stage, -archive는 앞 그림의 같은 이름 단계에 대응하고, -pak은 패키지 단계 가운데 콘텐츠를 pak 파일로 묶는 일을 맡습니다. 플랫폼별 배포 형식으로 묶는 일은 UAT의 별도 인자 -package가 맡으므로, 대상 플랫폼에 그 형식이 필요하면 이 인자를 더합니다. 실제로는 프로젝트의 맵, 타깃, 플러그인과 플랫폼의 요구 사항에 맞춰 인자를 정하며, 서버 타깃과 스토어 서명 옵션을 무조건 한 명령에 합치지 않습니다.

      Jenkins, TeamCity, GitHub Actions 같은 CI/CD(지속적 통합과 지속적 배포) 도구와 연동하면, 코드가 바뀔 때마다 자동으로 빌드하고, 시험하고, 패키징하고, 배포하는 시스템을 만들 수 있습니다. GitHub Actions로 검증, 빌드, 배포를 하나의 파이프라인으로 잇는 예는 NestJS 교재의 CI/CD 파이프라인 구축에서 볼 수 있습니다. 개발 효율이 높아지고 오류를 일찍 발견할 수 있습니다. 자동화된 패키징 로그는 한 덩어리로 보지 말고 빌드, 쿡, 스테이지, 아카이브 단계별로 실패 원인을 나눠 읽어야 빨리 고칠 수 있습니다.


      패키징 뒤에 확인하기

      패키지 게임은 에디터에서 실행할 때와 다르게 동작할 수 있습니다. 그래서 각 대상 플랫폼의 실제 하드웨어에서 패키지 게임을 꼼꼼히 시험해 예상하지 못한 버그나 성능 문제를 찾고, QA 팀이나 테스터에게 빌드를 나눠 넓게 시험합니다.

      측정과 출시 확인도 나눕니다. 자세한 계측은 도구가 들어 있는 Development나 Test 구성에서 하고, 최종 Shipping 산출물의 실행과 성능은 따로 확인합니다. 기본 Shipping에서 개발용 콘솔과 stat 명령이 그대로 동작한다고 가정하면 안 됩니다. 빌드 번호, 플랫폼, 서명 상태, 심볼을 산출물과 함께 보관하고, 같은 산출물이 깨끗한 대상 환경에서 시작 맵과 필수 콘텐츠를 불러오는지 확인합니다.


      정리

      • 패키징은 빌드, 쿡, 스테이지, 패키지, 아카이브로 나뉘며, 단계마다 산출물과 실패 원인이 다르므로 로그를 단계별로 읽습니다.
      • 패키지 게임의 시작 맵은 Game Default Map이 정하며, 에디터의 시작 맵과 다릅니다.
      • Shipping은 배포용 구성으로 콘솔과 통계 기능을 제한하며, 디버그 심볼 보관과 성능 측정은 따로 다룹니다.
      • 에디터에서는 플랫폼 메뉴의 Package Project로, 자동화에서는 RunUAT.bat BuildCookRun으로 패키징합니다.
      • Windows는 런타임 구성 요소, Android는 SDK, NDK, JDK 조합과 APK, AAB, 서명, 권한, iOS는 Mac과 Xcode, 프로비저닝, 콘솔은 개발 키트와 인증을 챙깁니다.
      • 빌드 성공이나 보관 경로의 파일만으로 실행 성공을 판단하지 말고, 깨끗한 대상 환경에서 같은 산출물을 실행해 확인합니다.

      연습 문제

      1. 파일은 있는데 실행되지 않는다

      빌드, 쿡, 스테이지가 끝났고 아카이브 경로에 파일도 있지만, 깨끗한 대상 PC에서는 필요한 런타임이 없어 게임이 시작되지 않습니다. 어디까지 확인된 것이고, 무엇이 남았을까요?

      해설 보기

      선택한 단계의 산출물이 만들어지고 보관되었다는 것까지 확인되었습니다. 남은 것은 대상 환경에서의 실행입니다. 설치 정책에 맞춰 필요한 런타임 의존성을 준비하고, 같은 산출물로 시작 맵, 필수 콘텐츠의 로드, 실행을 다시 확인해야 합니다.

      보관 경로에 파일이 있다고 대상 기기에서 실행되거나 스토어 제출에 성공한다는 증명이 되지는 않습니다. 이 예시는 단계별로 판단하는 연습이며, 실제로 패키징하고 기기에서 실행한 기록은 아닙니다.

      2. 디버그 파일을 포함하면

      패키징 설정에서 디버그 파일을 포함하는 옵션을 켰습니다. 이것으로 Shipping 빌드에서도 콘솔과 stat 명령을 쓸 수 있게 되었다고 말할 수 있을까요?

      해설 보기

      말할 수 없습니다. 디버그 파일을 포함하는 것과 콘솔 기능을 켜는 것은 서로 다른 설정입니다. 디버그 파일은 크래시 분석 같은 데 쓰는 심볼을 함께 담을 뿐이며, Shipping에서 제한된 콘솔과 통계 기능이 그것으로 다시 켜지지는 않습니다. 자세한 계측이 필요하면 Development나 Test 구성을 씁니다.

      3. 에디터에서는 시작 맵이 맞는데

      에디터를 열면 늘 메인 메뉴 맵이 열리는데, 패키지 게임을 실행하니 빈 테스트 맵으로 시작합니다. 무엇을 확인해야 할까요?

      해설 보기

      Maps & Modes의 Game Default Map을 확인합니다. 에디터가 여는 맵은 Editor Startup Map이 정하고, 패키지 게임의 시작 맵은 Game Default Map이 정하므로, 에디터에서 메인 메뉴가 열렸다는 사실은 패키지의 시작 설정을 확인한 것이 아닙니다. Game Default Map을 메인 메뉴 맵으로 바꾸고, 그 맵이 쿠킹 대상에 들어가는지도 확인한 뒤 다시 패키징합니다.

      다음 글 디버깅과 로그 관리에서는 패키지 게임과 개발 빌드에서 문제를 찾는 디버깅 방법과, 로그를 남기고 관리하는 방법을 살펴봅니다.