.NET 생태계는 다양한 방면으로 계속 진화를 거듭하고 있으며, 유니티는 새로운 개선 사항을 최대한 신속하게 전달하고자 노력하고 있습니다. 유니티 내부의 .NET Tech Group에서는 .NET 통합을 지속적으로 개선하고 있으며, 여기에는 최신 C# 기능 및 .NET Standard 2.1도 포함됩니다. 최근에는 사용자 피드백을 바탕으로 전체적인 개발자 경험을 대폭 개선하고 있습니다.
이번 블로그 포스팅에서는 유니티가 현재 개발 중인 기능을 소개합니다. GDC 2022의 Unity 개발자 서밋(Unity Dev Summit)에서도 이 주제에 대해 논의한 바 있습니다. 여기에서 전체 세션을 보실 수 있습니다.
.NET과 Unity의 진화
이 이야기는 지금으로부터 17년 전, 유니티의 CTO가 C#과 Mono .NET 런타임을 동시에 활용하면서 시작됩니다. 유니티는 C# 코드를 상대적으로 효율성 높은 네이티브 코드로 변환하는 JIT(Just-In-Time) 컴파일러와 언어의 간결한 매력 때문에 C#을 선호했습니다. Unity 엔진에서 훨씬 더 큰 비중을 차지하는 나머지 부분은 성능의 적절한 균형과 제어를 위해 C++를 사용하여 개발되었습니다.
수년간 Unity는 Mono .NET 런타임 및 C# 언어(2.0)를 기반으로 실행되었고, 그 기간 동안 다른 플랫폼에 대한 지원도 추가되었습니다. 유니티는 iOS 및 일부 콘솔 플랫폼을 타겟팅할 수 있도록 자체 컴파일러이자 런타임인 IL2CPP도 개발했습니다.
그동안 전체 Microsoft .NET 생태계도 진화하여 새로운 라이선싱과 비 Windows 플랫폼 지원이 생겼습니다. 이러한 진화 덕분에 유니티는 2018년에 Unity .NET Mono 런타임을 업그레이드하고 더 현대적인 C# 언어 버전(7.0+)을 도입할 수 있었습니다. 같은 해에 Burst 컴파일러의 최초 버전도 릴리스하여 일부 C# 언어로 최적화된 네이티브 코드를 생성할 수 있게 되었습니다. 유니티는 당시 획기적인 발전을 거듭하며 C++가 아닌 C#을 사용하여 엔진의 다른 주요 기능을 개발할 수 있다는 가능성을 보게 되었고, 이는 DOTS 런타임 개발로 이어졌습니다.
Unity 2020 LTS와 Unity 2021 LTS에서는 새로운 버전의 C#과 .NET API(예: Span<T>)가 사용되었습니다. 이와 동시에 .NET 생태계에서는 엄청난 성능 개선이 이루어졌고, SDK 스타일의 csproj의 도입과 NuGet 생태계의 성장을 통해 보다 친근한 개발 환경이 구축되었습니다.
크리에이터 피드백
Unity 플랫폼은 오랜 진화의 결과로 Mono .NET 런타임에서 상속한 구체적 가정을 사용하여 .NET 오브젝트와 직접 상호 작용하는 대규모 C++ 코드베이스를 포함하게 되었습니다. 이는 .NET (Core) 런타임에는 더 이상 유효하지 않으며 효율이 떨어집니다.
또한 Unity 에디터와 함께 사용하는 복잡한 컴파일 파이프라인은 MSBuild를 사용하지 않기 때문에 제공되는 모든 기본 기능을 활용할 수 없게 됩니다.
유니티는 지난 몇 년간 인터뷰와 Unity 포럼을 통해 많은 크리에이터와 소통하며 성공적인 게임을 개발하는 데 있어 어떤 측면을 개선할 수 있을지 파악했습니다. 그 결과 최신 C# 언어, .NET 런타임 기술, NuGet의 타사 C# 코드를 사용하고 싶다는 피드백을 받았습니다. Unity 플랫폼 사용에 관해서는 고품질 C# 테스트, 디버깅, 프로파일링 툴, 그리고 기본 .NET API와 Unity API 간의 적절한 통합을 통해 타겟 하드웨어를 최대한 활용하고 싶다는 의견이 많았습니다. C# Unity 프로그래머들은 업계 최고의 런타임 성능을 달성할 수 있도록 다른 툴박스와 원활히 연동되어 빠른 반복을 실현하는 Unity 툴을 원합니다.
이러한 요청을 모두 현실화하려면 수 년이 걸릴 것으로 예상합니다. 유니티는 개발 과정에서 직면하는 기술적인 문제에 관해 블로그와 포럼을 수시로 업데이트하여 상황을 계속 공유할 예정입니다.
.NET 이니셔티브
이번 이니셔티브의 첫 단계에서는 C# 및 .NET에 열의를 가진 내부 직원을 모두 한 데 모아 C#/.NET Tech Group을 구성하였습니다.
커스텀 솔루션이 아닌 .NET 생태계를 기반으로 빌드하고자 했으며, 최신 .NET SDK/Runtime 및 MSBuild의 성능과 생산성 개선을 활용할 수 있도록 Mono .NET 런타임에서 현대적인 .NET (Core) 런타임인 CoreCLR로 마이그레이션할 계획을 세웠습니다.
또한 이 이니셔티브는 C# 스크립트에서 더 빠른 .NET 반복 작업이 가능하도록 기존의 .NET 생태계를 넘어서는 혁신을 제공합니다. 유니티는 JIT 및 AOT(Ahead-Of-Time) 솔루션, 즉 IL2CPP 및 Burst의 융합을 통해 컴파일 소요 시간과 코드 생성 품질 간에 최적의 균형을 제공하려고 합니다.
외부적으로는 Unity 크리에이터가 최신 .NET 기술을 사용할 수 있도록 Microsoft, JetBrains 등의 업계 파트너와 협력하면서 오픈 소스 커뮤니티에서도 참여도를 높일 예정입니다. 이 모든 활동은 여러 세분화된 단계로 진행됩니다. 그럼 향후 계획을 살펴보겠습니다.
2022년 유니티의 계획
유니티 팀은 올해에 다음 사항에 집중할 예정입니다.
C# 개발 워크플로
최대한 시간을 활용할 수 있도록 반복 작업 시간 단축을 여전히 최고 우선 순위로 고려합니다. 다음은 반복 작업 시간을 개선하기 위한 방안의 일부입니다.
MSBuild로 마이그레이션하기 위한 첫 단계는 Unity 에디터에서 컴파일 파이프라인을 분리하여 별도 프로세스로 옮기는 것이었습니다. 몇 년간 축적된 수천 줄의 C++, C# 레거시 코드를 분리해내는 동시에 하위 호환성도 유지해야 하기 때문에 이는 복잡한 작업입니다. 외부적인 변화는 없지만 이 작업을 통해 MSBuild로 이전하기 위한 기반을 마련하고 유지 관리를 간소화할 수 있습니다.
또한 Burst에서 C# IDE 디버깅 경험도 개선할 예정입니다. Burst를 통해 실행되는 코드 경로에 중단점이 설정되어 있을 때 디버거가 관리되는 디버깅으로 자동 전환하는 모드가 도입됩니다. 이렇게 하면 디버깅되는 코드 경로에서 [BurstCompile] 속성을 수동으로 제거하지 않아도 됩니다.
.NET 런타임 현대화
.NET CoreCLR 런타임으로 마이그레이션하기 위한 작업은 이미 시작되었으며 이는 아주 까다로운 과제입니다. 이 마이그레이션을 완수하기 위해 유니티는 문제를 점진적으로 해결하면서 기존 Unity 프로젝트의 안정성을 유지하는 방향으로 결과물을 전달하려 합니다.
다음과 같이 여러 단계로 이 마이그레이션을 완수할 계획입니다.
Unity 런타임 현대화
Unity 2021 LTS의 .NET Standard 2.1 지원을 통해 다양한 방식으로 Unity 런타임을 현대화할 수 있게 되었습니다. 유니티는 현재 아래와 같은 두 가지 측면을 개선하고 있습니다.
async/await 프로그래밍 모델을 개선합니다. async/await는 기초적인 프로그래밍 접근 방식으로, 엔진의 메인루프 차단 없이 비동기 연산이 완료될 때까지 대기해야 하는 게임플레이 코드를 작성합니다.
2011년, async/await가 .NET에서 대중화되기 전에 유니티는 반복자(interator) 기반의 코루틴을 통해 비동기 연산을 도입했으나 이 접근 방식은 async/await와 호환되지 않으며 효율성이 저하될 가능성이 있습니다. 그동안 .NET Standard 2.1은 ValueTask를 통해 더 효율적인 async/await 연산 처리를 도입하고, AsyncMethodBuilder를 통해 작업 기반의 자체 시스템을 허용함으로써 C# 및 .NET에서 async/await 지원을 개선했습니다.
이제 이러한 개선 사항을 활용할 수 있으며 Unity의 기존 비동기 연산을 통해 async/await 연산을 사용할 수 있도록 수정하고 있습니다(다음 프레임 대기 또는 UnityWebRequest 완료 대기 등). 첫 단계에서는 MonoBehavior가 제거되고 있거나 취소 토큰을 사용하여 플레이 모드를 종료할 때 대기 중인 비동기 작업을 취소할 수 있도록 개선합니다. 유니티는 UniTask의 작성자와 같이 커뮤니티에 적극적으로 기여하는 사용자들과 긴밀히 협력하여 이와 같은 신기능을 활용할 수 있도록 지원하고 있습니다.
Span<T>를 활용하여 메모리 할당과 사본을 줄입니다. Unity는 C# 스크립팅 레이어가 있는 C++ 엔진이기 때문에 둘 사이에 많은 데이터가 교환됩니다. 데이터를 상호 복사하거나, 관리되는 오브젝트를 새로 할당해야 하는 경우가 종종 있으므로 이는 비효율적일 수 있습니다.
Span<T>는 이 과정을 개선하기 위해 C# 7.2에 도입되었으며 .NET Standard 2.1에서 기본적으로 사용 가능합니다. 최근 몇 년간 Span<T> 덕분에 .NET 런타임의 성능이 여러 면에서 크게 개선되었다는 소식을 아마 접하셨을 겁니다(.NET Core 2.1, .NET Core 3.0, .NET 5, .NET 6의 자세한 개선 사항 참조). Unity에서는 Span<T>를 활용하여 할당과 가비지 컬렉션의 멈춤 현상을 줄이고 API 다수의 전반적인 성능을 개선할 계획입니다.
유니티의 여정에 동참하세요
이번 포스팅에서 소개한 변경 사항과 기능에 큰 기대를 가져주셨으면 합니다.
유니티의 계획에 대한 의견은 포럼에서 공유해 주세요. Unity 플랫폼 로드맵의 엔지니어링 섹션도 정기적으로 업데이트됩니다. 로드맵을 통해 새로운 기능을 요청하고 프로젝트 우선 순위를 제안하세요.
Is this article helpful for you?
Thank you for your feedback!