UE 5.8 소스 빌드에서 Unreal Engine UBA Executor UBT Error 해결하기
핵심 요약
이 글은 Unreal Engine 5.8 소스 빌드 중 발생하는 UBA Executor UBT Error의 발생 원인과 해결 방법을 기술적 관점에서 다룹니다. 해당 에러는 Unreal Build Accelerator(UBA)가 재귀적 UBT 호출을 지원하지 않아 발생하며, 이를 해결하기 위해 BuildConfiguration.xml 설정을 변경하거나 ExecutorFactory.cs 코드를 패치하는 구체적인 방법을 제시합니다. 아울러 빌드 환경 클린 및 재생성 절차와 컴파일 메모리 부족 방지 등 소스 빌드 최적화를 위한 모범 사례를 함께 공유합니다.
엔진 빌드가 깨지는 것만큼 스튜디오의 개발 동력을 떨어뜨리는 일도 드뭅니다. 특히 Unreal Engine 5.8.0을 소스에서 컴파일하는 도중 Visual Studio에서 원인을 알 수 없는 예외가 발생하며 중단될 때가 그렇습니다. 이 빌드 실패는 UBAExecutor.cs를 직접 가리키는 처리되지 않은 예외(unhandled exception)와 함께 발생합니다. unreal engine uba executor ubt error로 알려진 이 특정 블로커는 컴파일을 완전히 중단시킵니다. 또한 빌드 그래프가 UnrealHeaderTool과 같은 중요한 헬퍼 프로그램의 생성을 완료하지 못하도록 차단합니다. 이 가이드에서는 Unreal Build Accelerator (UBA)가 왜 중첩 호출(nested calls)에서 문제를 겪는지, 기본 구성(default configuration)이 어떻게 실패하는지, 그리고 정상적인 빌드 파이프라인을 복구하기 위해 엔진 소스를 패치하는 방법을 설명합니다.
빌드 시스템 아키텍처의 이해
이 에러가 발생하는 원인을 이해하려면 Unreal Build Tool (UBT)이 컴파일을 어떻게 오케스트레이션하는지 살펴보아야 합니다. Unreal Engine 코드베이스는 수만 개의 소스 파일을 포함할 정도로 매우 거대합니다. 이를 효율적으로 컴파일하기 위해 UBT는 메타 빌드 시스템으로 작동하며, 종속성 그래프를 생성하고 컴파일 액션을 배포합니다.
Unreal Build Accelerator란 무엇인가요?
Epic Games는 기존의 분산 시스템을 대체하기 위해 기본 컴파일 실행기(executor)로 Unreal Build Accelerator (UBA)를 도입했습니다. UBA는 경량 가상 파일 시스템(VFS)을 활용하여 파일 I/O 작업을 가로챔으로써 컴파일 속도를 높이도록 설계되었습니다. 컴파일러 작업을 여러 로컬 코어에 라우팅하거나 Horde 빌드 노드에 분산시킵니다.
표준 64코어 빌드 머신에서 UBA는 클린 엔진 컴파일 시간을 약 90분에서 25분 미만으로 단축할 수 있습니다. 그러나 UBA는 I/O를 인터셉트하기 위해 중앙 집중식 로컬 에이전트에 의존하므로, 컴파일러 프로세스가 생성되는 방식을 엄격하게 제어해야 합니다.
재귀적 UBT 호출(Recursive UBT Calls)의 메커니즘
컴파일을 실행하는 동안 UBT는 메인 엔진 바이너리를 컴파일하기 전에 먼저 빌드해야 하는 타겟을 자주 마주하게 됩니다. 예를 들어 UnrealEditor 실행 파일을 컴파일하기 전에, UBT는 헤더 파일을 파싱하고 리플렉션 메타데이터를 생성하기 위해 UnrealHeaderTool (UHT)을 먼저 컴파일해야 합니다.
이를 위해 메인 UBT 프로세스는 전제 조건 타겟을 빌드하기 위한 중첩된 보조 UBT 프로세스를 생성합니다. 이 중첩된 호출을 재귀적 UBT 호출(recursive UBT call)이라고 합니다. 재귀적 UBT 호출이 활성화되면 UBT는 내부 플래그(UnrealBuildTool.IsRecursive = true)를 설정하고 프로세스가 중첩되었음을 나타내기 위해 환경 변수를 전파합니다.
UBA Executor가 재귀적 호출에서 크래시를 일으키는 이유
크래시가 발생하는 이유는 UBA executor가 중첩된 컴파일 루프를 지원하도록 설계되지 않았기 때문입니다. Visual Studio에서 이 장애가 발생할 때 UBT가 출력하는 전형적인 콜 스택(call stack)을 살펴보겠습니다.
Unhandled exception: Exception: UBA executor is not expected to be invoked from a recursive UBT call.
at UnrealBuildTool.UBAExecutor.Init(IEnumerable`1 targetDescriptors, ILogger logger) in UnrealEngine\Engine\Source\Programs\UnrealBuildTool\Executors\UnrealBuildAccelerator\UBAExecutor.cs:line 315
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at UnrealBuildTool.UBAExecutor.ExecuteActionsAsync(IEnumerable`1 inputActions, ILogger logger) in UnrealEngine\Engine\Source\Programs\UnrealBuildTool\Executors\UnrealBuildAccelerator\UBAExecutor.cs:line 617
at UnrealBuildTool.ActionGraph.InternalExecuteActions(ActionExecutor Executor, List`1 ActionsToExecute, ILogger Logger) in UnrealEngine\Engine\Source\Programs\UnrealBuildTool\Actions\ActionGraph.cs:line 435
UBAExecutor.cs의 안전 검사(Safety Check)
UBAExecutor.cs 파일의 약 315번째 라인에 있는 UBAExecutor.Init 메서드 내부를 보면, 엔진이 명시적으로 재귀 안전 검사를 강제하고 있습니다.
// Engine/Source/Programs/UnrealBuildTool/Executors/UnrealBuildAccelerator/UBAExecutor.cs
public void Init(IEnumerable<TargetDescriptor> TargetDescriptors, ILogger Logger)
{
if (UnrealBuildTool.IsRecursive)
{
throw new Exception("UBA executor is not expected to be invoked from a recursive UBT call.");
}
// Virtualized file system and network initialization follow...
}
이 안전 검사가 존재하는 것에는 아주 중요한 이유가 있습니다. 로컬 UBA 에이전트는 특정 TCP 포트에 바인딩하여 가상화된 컴파일러 헬퍼 프로세스들과 통신합니다.
만약 재귀적 UBT 호출이 UBA executor의 두 번째 인스턴스를 초기화하려고 시도하면, 두 인스턴스가 동일한 네트워크 소켓에 바인딩을 시도하면서 가상 파일 시스템 훅(hook)에 대한 충돌이 일어납니다. 이는 소켓 할당 에러, 파일 시스템 손상 또는 무한 빌드 데드락(deadlock)으로 이어질 수 있습니다. 따라서 이 예외는 이를 방지하는 보호 장벽 역할을 합니다.
근본 원인: Executor 선택 실패
Unreal Engine 5.8.0 소스 빌드의 실제 버그는 이 안전 검사 자체가 아닙니다. 그보다는 executor 팩토리 로직이 재귀적 호출을 올바르게 처리하지 못한다는 점입니다.
UBT가 어떤 executor를 사용할지 결정할 때, ExecutorFactory.cs 클래스를 쿼리합니다. 팩토리는 호스트 머신에서 UBA가 활성화되어 있고 사용 가능한지 확인합니다.
하지만 현재 UBT 프로세스가 재귀적으로 실행 중인지는 검증하지 않습니다. 결과적으로 보조 UBT 프로세스가 UnrealHeaderTool을 컴파일하기 위해 시작될 때, 팩토리는 이 중첩된 빌드에 UBAExecutor를 할당하려고 시도하게 되며, 이로 인해 Init 메서드에서 크래시가 발생합니다.
XML 설정의 함정
많은 개발자들이 글로벌 BuildConfiguration.xml 파일을 수정하여 이 문제를 우회하려고 시도합니다. 오래된 포럼 게시물에서 흔히 추천하는 방법은 다음 태그를 통해 UBA를 비활성화하는 것입니다.
<?xml version="1.0" encoding="utf-8" ?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<BuildConfiguration>
<bAllowUBA>false</bAllowUBA>
</BuildConfiguration>
</Configuration>
UE 5.8에서 bAllowUBA가 무시되는 이유
Unreal Engine 5.8.0 소스 빌드에 위의 XML 설정을 적용해도, 컴파일러는 여전히 UBA를 실행하려고 시도하며 예외를 던집니다. 이는 엔진의 빌드 구성 시스템이 리팩터링되었기 때문입니다.
이전의 bAllowUBA 플래그는 지원 중단(deprecated)되었으며 더 이상 executor 선택 로직에 매핑되지 않습니다. 대신 UBA의 동작은 bAllowUBAExecutor 및 bAllowUBALocalExecutor라는 두 가지 개별 속성으로 제어됩니다.
UBT가 XML에서 bAllowUBAExecutor를 찾지 못하면 기본값인 true로 설정되므로, UBA를 끄려던 시도가 자동으로 무시되는 것입니다.
올바른 UBA 구성용 XML 구조
설정 파일을 통해 UBA를 성공적으로 비활성화하려면 업데이트된 XML 속성 이름을 사용해야 합니다. UBT가 표준 로컬 executor로 폴백(fallback)하도록 강제하는 올바른 구조는 다음과 같습니다.
<?xml version="1.0" encoding="utf-8" ?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<BuildConfiguration>
<bAllowUBAExecutor>false</bAllowUBAExecutor>
<bAllowUBALocalExecutor>false</bAllowUBALocalExecutor>
</BuildConfiguration>
</Configuration>
이 설정을 적용하면 UBA를 성공적으로 우회할 수 있습니다. 그러나 UBA를 완전히 비활성화하면 메인 빌드 작업에서 얻을 수 있었던 상당한 컴파일 속도 이점을 잃게 됩니다.
에러 해결을 위한 단계별 가이드
워크플로우에 따라 XML 구성을 수정하여 글로벌하게 문제를 해결하거나, 재귀적이지 않은 컴파일에 대해서는 빌드 가속을 유지하도록 UBT 소스 코드를 직접 패치할 수 있습니다.
방법 1: BuildConfiguration.xml 오버라이드
엔진 소스 코드를 수정하고 싶지 않다면 UBA를 글로벌하게 비활성화할 수 있습니다. 이 방법은 프로젝트를 컴파일할 수 있게 만드는 가장 빠른 방법이지만, 하드웨어 사양에 따라 클린 빌드 시간이 약 40%에서 60% 정도 늘어날 수 있습니다.
- 글로벌
BuildConfiguration.xml파일의 위치를 찾거나 새로 생성합니다. Windows의 경우 일반적으로%AppData%\Roaming\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml에 위치합니다. Linux의 경우~/.config/Unreal Engine/UnrealBuildTool/BuildConfiguration.xml에 위치합니다. - 텍스트 에디터에서 XML 파일을 엽니다.
- 파일 내용을 아래에 안내된 업데이트된 XML 스키마로 바꿉니다.
- 파일을 저장하고 Visual Studio 빌드를 재시작합니다.
<?xml version="1.0" encoding="utf-8" ?>
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<BuildConfiguration>
<bAllowUBAExecutor>false</bAllowUBAExecutor>
<bAllowUBALocalExecutor>false</bAllowUBALocalExecutor>
</BuildConfiguration>
</Configuration>
방법 2: UBT 소스 코드 패치 (권장)
Unreal Engine 5.8을 소스에서 컴파일하고 있으므로, 권장하는 해결책은 UBT C# 소스 코드를 직접 패치하는 것입니다. 이렇게 하면 기본 컴파일 타겟에서는 UBA를 계속 사용하면서, 재귀적 호출 시에만 표준 ParallelExecutor로 대체하도록 만들 수 있습니다.
- UBT 소스 디렉터리로 이동합니다:
Engine/Source/Programs/UnrealBuildTool/Executors/ - Visual Studio나 텍스트 에디터에서
ExecutorFactory.cs파일을 엽니다. Create메서드를 찾습니다. 이 메서드는 구성을 평가하고 적절한ActionExecutor를 반환하는 역할을 담당합니다.- 재귀적 UBT 실행 여부를 확인하는 로직을 포함하도록 UBA 선택 조건문을 수정합니다.
// Engine/Source/Programs/UnrealBuildTool/Executors/ExecutorFactory.cs
public static ActionExecutor Create(BuildConfiguration BuildConfiguration, List<TargetDescriptor> TargetDescriptors, ILogger Logger)
{
// Check if UBA is allowed, available, and NOT running recursively
- if (BuildConfiguration.bAllowUBAExecutor && UBAExecutor.IsAvailable())
+ if (BuildConfiguration.bAllowUBAExecutor && UBAExecutor.IsAvailable() && !UnrealBuildTool.IsRecursive)
{
return new UBAExecutor(BuildConfiguration, Logger);
}
// Fall back to IncrediBuild if configured
if (BuildConfiguration.bAllowXGE)
{
return new XGEExecutor(BuildConfiguration, Logger);
}
// Fall back to standard ParallelExecutor
return new ParallelExecutor(BuildConfiguration, Logger);
}
이 패치를 적용하면 중첩된 UBT 인스턴스가 UBA executor를 선택하는 것을 방지합니다. 대신 재귀 빌드(예: UnrealHeaderTool 빌드)는 로컬 ParallelExecutor를 사용하여 안전하게 실행되고, 기본 엔진 컴파일은 UBA의 성능을 100% 활용할 수 있게 됩니다.
방법 3: 커맨드 라인 플래그
커스텀 커맨드 라인 스크립트나 CI/CD 파이프라인을 통해 빌드를 실행하는 경우, 빌드 호출 시점에 개별적으로 UBA를 비활성화할 수 있습니다. 이 방법은 로컬 개발자 환경에서는 UBA를 켜두고, 원격 빌드 서버에서만 비활성화하고 싶을 때 유용합니다.
이를 위해 UBT 빌드 명령어에 -NoUBA 플래그를 추가합니다.
# Example command to build the editor without UBA
Engine\Build\BatchFiles\Build.bat UnrealEditor Win64 Development -NoUBA
또는 다음과 같이 -Executor 플래그를 명시하여 UBT가 표준 Parallel executor를 강제로 사용하도록 할 수도 있습니다.
Engine\Build\BatchFiles\Build.bat UnrealEditor Win64 Development -Executor=Parallel
빌드 환경 클린 및 재생성
위의 해결책 중 하나를 적용한 후에도 캐싱된 어셈블리 파일이나 오래된 중간 메타데이터로 인해 UBT 컴파일이 실패할 수 있습니다. 수정 사항이 완전히 반영되도록 하려면 생성된 빌드 툴 바이너리를 삭제하고 프로젝트 파일을 재생성해야 합니다.
Windows 환경의 경우
Unreal Engine 소스 디렉터리를 가리키는 명령 프롬프트나 PowerShell에서 다음 명령어를 실행합니다.
:: Delete the cached UBT assembly and build binaries
rd /s /q Engine\Intermediate\Build\UnrealBuildTool
rd /s /q Engine\Binaries\DotNET\UnrealBuildTool
:: Regenerate the project files
GenerateProjectFiles.bat
Linux 및 macOS 환경의 경우
터미널에서 다음 명령어를 실행합니다.
# Remove cached build tool data
rm -rf Engine/Intermediate/Build/UnrealBuildTool
rm -rf Engine/Binaries/DotNET/UnrealBuildTool
# Regenerate project files
./GenerateProjectFiles.sh
환경이 정리되면 생성된 솔루션 파일(UE5.sln)을 Visual Studio에서 열고 타겟을 다시 빌드합니다. 이제 executor 예외를 던지지 않고 재귀 컴파일 단계를 무사히 통과할 것입니다.
Unreal Engine 소스 빌드를 위한 실용적인 모범 사례(Best Practices)
소스에서 커스텀 엔진 포크(fork)를 빌드할 때는 다양한 컴파일, 최적화 및 패키징 관련 과제가 수반됩니다. 다음 모범 사례들을 적용하면 안정적이고 고도로 최적화된 개발 파이프라인을 유지하는 데 도움이 됩니다.
1. 동시성 최적화를 통한 메모리 부족 방지
최신 C++ 컴파일러는 컴파일 스레드당 상당한 RAM 공간을 요구합니다. 대형 엔진 모듈을 빌드할 때 UBT가 시스템 RAM이 지원할 수 있는 것보다 더 많은 병렬 작업을 생성하면, 페이지 파일 쓰레싱(page-file thrashing)이나 컴파일러 크래시가 발생할 수 있습니다.
BuildConfiguration.xml 파일에서 ParallelExecutor 설정을 구성하여 최대 프로세서 개수를 제한할 수 있습니다.
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<ParallelExecutor>
<MaxProcessorCount>16</MaxProcessorCount>
<ProcessorCountMultiplier>1.0</ProcessorCountMultiplier>
</ParallelExecutor>
</Configuration>
스레드당 약 2GB의 RAM이 확보되도록 개수를 조정하는 것이 좋습니다. 예를 들어, 32GB RAM이 탑재된 머신은 MaxProcessorCount를 16으로 제한해야 합니다.
2. Dedicated Server Asset Stripping 숙지하기
클라우드에 게임을 배포할 때는 전용 서버 바이너리에 UI 텍스처, 오디오, 메시 같은 불필요한 클라이언트 에셋이 컴파일되지 않도록 해야 합니다. 이를 통해 서버 시작 시간을 단축하고 메모리 사용량을 줄일 수 있으며, 이는 플릿(fleet)을 효율적으로 확장하는 데 있어 매우 중요합니다.
이러한 에셋을 제외하도록 빌드 스크립트를 구성하는 방법을 알아보려면 Unreal Engine Dedicated Server Asset Stripping 가이드를 확인해보세요.
3. 블루프린트 및 패키지 무결성의 사전 검증
엔진 빌드가 성공했다고 해서 프로젝트 패키징 과정까지 에러 없이 완료된다는 보장은 없습니다. 오래된 블루프린트나 직렬화(serialization) 에러는 종종 최종 패키징 단계에서 크래시를 유발합니다.
이러한 문제가 릴리스 파이프라인을 막는 것을 방지하기 위해, Resolving the Unreal Package HasValidBlueprint Ensure Crash 가이드를 읽고 자동화된 유효성 검사를 구축해 보세요.
4. UBA VFS Caching의 올바른 설정
C# 패치를 통해 UBA를 활성화 상태로 유지하기로 결정했다면, 컴파일된 오브젝트 파일이 로컬에 저장되도록 가상 파일 시스템 캐시를 구성해야 합니다. 이를 통해 브랜치를 전환할 때 변경되지 않은 소스 파일의 재컴파일을 방지할 수 있습니다.
캐싱을 활성화하려면 XML 파일에 다음 UnrealBuildAccelerator 구성 블록을 추가합니다.
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
<UnrealBuildAccelerator>
<WriteCache>true</WriteCache>
<Cache>127.0.0.1</Cache>
</UnrealBuildAccelerator>
</Configuration>
방화벽이 캐시 동기화 루프를 관리하는 데 사용되는 UBA의 로컬 네트워크 통신 포트를 차단하지 않도록 설정해야 합니다.
Backend 아키텍처 한 단계 끌어올리기
unreal engine uba executor ubt error 같은 빌드 문제를 해결하는 것은 원활한 개발 워크플로우를 유지하는 데 필수적입니다. 그러나 로컬 컴파일 환경을 구성하는 것은 현대적인 Multiplayer 게임을 빌드하기 위한 첫 단계에 불과합니다.
커스텀 엔진 컴파일을 마치고 Dedicated Server를 준비했다면, 이제 Backend 인프라의 복잡한 과제들을 해결해야 합니다. 서버 오케스트레이션, Matchmaking 알고리즘, 영속성 데이터베이스(persistence database)를 처음부터 직접 작성하려면 수주의 개발 시간이 소요됩니다.
Load Balancing 구성, 데이터베이스 샤딩(database sharding) 및 SSL 인증서 관리를 셋업하는 것만으로도 최소 4~6주의 집중적인 작업이 필요할 수 있습니다. 하지만 horizOn을 활용하면 이러한 Backend 서비스들이 미리 구성된 상태로 즉시 제공되어 간편하게 확장할 수 있습니다.
인프라 코드를 직접 작성하는 대신, 몇 번의 간단한 API 호출만으로 플레이어 데이터, 실시간 로비, 서버 스케일링 등을 원활하게 연동해 보세요. 이를 통해 유지 관리 부담 없이 Dedicated Server 빌드를 배포하고 플레이어 상태를 관리할 수 있습니다. 개발 팀이 게임플레이 메카닉과 엔진 기능 개발에만 몰입할 수 있도록 horizOn이 클라우드 인프라를 대신 관리합니다.
다음 단계
이 빌드 에러를 해결하려면 빠른 XML 오버라이드를 적용하거나 ExecutorFactory.cs에 C# 소스 패치를 반영하세요. 빌드 파이프라인이 정상적으로 돌아가기 시작하면 배포 워크플로우를 최적화할 방법을 탐색해 보시기 바랍니다. 커스텀 서버 호스팅에 대한 부담 없이 Multiplayer 게임을 확장하고 싶다면 horizOn을 무료로 사용해 보거나 API 문서를 참조하여 더 자세한 내용을 알아보세요.