Unity가 직면한 기술적 문제들

UPDATE: 이 글에 대한 피드백을 정리한 Unity가 직면한 기술적 문제들 2를 작성했습니다.

자체 게임 엔진을 개발할 여력이 안 되는 중소 모바일 게임 개발사가 선택할 수 있는 게임 엔진은 크게 Unity3DUnreal Engine으로 양분되는 것 같습니다. 제가 맡았던 두 팀은 이미 Unity로 게임을 제작하고 있었고, 팀원들도 모두 Unity 에디터가 손에 익었기 때문에 별다른 고민 없이 Unity를 사용해 프로젝트를 계속 진행하게 되었습니다.

Unity가 모바일 게임 개발에 기여한 바는 이론의 여지가 없을 정도로 큽니다. .NET을 이용한 크로스 플랫폼 개발 환경 제공, C#과 같은 고수준 프로그래밍 언어의 사용, 편리한 에디터 등 Unity의 장점은 매력적이었습니다. C, C++, J2ME로 피처폰 게임을 개발하던 방식과 비교하면 격세지감을 느낄 수 있는 수준입니다.

하지만 시간이 지나면 혁신의 속도도 늦어지는 법인지, 제가 지난 1년 2개월간 경험한 Unity는 다음과 같이 몇 가지 기술적인 문제점을 노출하고 있었습니다.

  • 정체된 .NET 버전

Unity는 .NET을 오픈소스로 구현한 Mono 엔진을 사용하고 있습니다. 문제는 굉장히 오래된 Mono 엔진을 사용하고 있다는 점입니다. Unity 4.5.1f3의 Mono 버전은 2.0.50727.1433인데, Mono 2.0이 2008년에 릴리즈되었다는 점을 생각하면 골동품 수준의 엔진을 사용하고 있습니다. 최근에 릴리즈된 Unity 5에서도 기대와는 달리 Mono 버전의 업그레이드는 없었습니다.

Mono 버전이 오래되었다는 건 최신의 .NET 기술을 전혀 사용할 수 없다는 뜻입니다. 일례로, Unity는 비동기 프로그래밍에 상당한 발전을 가져온 .NET의 Task Parallel Library나 C#의 async, await 키워드 등을 사용할 수 없습니다.

Unity가 사용하는 Mono는 .NET 버전 기준으로 3.5 + 일부 4.0 API가 포함되어 있는 수준입니다. 요즘 대부분의 .NET 개발자들은 .NET 최소 버전을 4.0으로 잡고 있기 때문에 최신의 .NET 라이브러리는 수정 없이 Unity에서 사용하기가 어려워지고 있습니다. 어떤 라이브러리를 Unity에 사용하려면 별도의 수정 작업이 필요하다는 건, 더 이상 Unity를 .NET 개발 환경이라고 부르기 힘들어졌다는 뜻입니다. 일례로, Reactive Programming의 유행을 가져온 Rx는 Unity에서 지원되지 않습니다. 대신 비슷한 개념을 Unity에서 사용할 수 있게 새로 만든 UniRx라는 별도의 라이브러리가 나와 있습니다.

  • 안정성과 성능

Unity가 직면한 또 다른 문제점은 안정성과 성능입니다. 특히, iOS 빌드에 사용하는 AOT 컴파일러는 심각한 수준의 문제가 있습니다. 일례로, LINQ를 사용한 코드는 iOS 빌드 시에 크래시를 일으킵니다. LINQ가 아에 안 되는 것도 아니고 LINQ의 특정한 조합에서만 발생하는 문제이기 때문에 디버깅도 쉽지 않습니다. 이 문제를 피하기 위해 아에 LINQ 사용을 금지하는 것이 가장 현실적인 조언이 될 정도입니다.

이 문제도 노후한 Mono 엔진과 연결이 되어 있습니다. iOS 빌드 시 발생하는 크래시 문제도 Mono 2.0에 포함된 AOT 컴파일러의 안정성에 문제가 있기 때문에 발생합니다.

Mono 엔진 자체는 2.0 버전 이후 이후 안정성과 성능에 상당한 개선이 있었습니다. 또한, 최근 마이크로소프트가 coreclrcorefx라는 이름으로 .NET 코드를 오픈소스로 공개하였는데, Mono 4.0부터는 필요한 부분에 대해서는 마이크로소프트 코드를 일부 차용하기 시작하여 안정성이나 호환성이 크게 개선되기 시작하였습니다.

문제는 Unity는 이런 Mono 엔진의 성능 및 안정성 개선 혜택을 전혀 누리지 못하고 있다는 점입니다. Unity 엔진은 계속해서 Mono 2.0에 머무르고 있고, 엔진 업그레이드를 못 하고 있는 이유가 단순히 인력 부족은 아닌 것으로 보입니다. 들리는 루머에 따르면 Mono 버전 업그레이드와 관련하여 Mono의 개발사인 Xamarin과 라이선스 협상에 문제가 있었고, 그 결과 Unity는 단순히 Mono 버전을 업그레이드하는 대신 이 문제를 전혀 다른 방식으로 문제를 풀기로 결정합니다.

  • IL2CPP 개발

Unity가 노후화된 Mono 엔진의 성능 및 안정성 문제를 한 번에 해결하기 위해 들고 나온 해결책은 IL2CPP라는 기술입니다. 이 기술을 간단히 말해 C#을 컴파일해서 나온 중간 언어인 IL을 컴파일해 C++를 출력한 다음 다시 각 플랫폼이 제공하는 C++ 컴파일러를 이용해 네이티브 코드를 만드는 기술입니다.

Unity가 IL2CPP를 통해 목표하는 바는 제가 앞서 제기한 Unity의 문제점과 정확히 일치합니다. 노후화된 Mono 런타임의 성능 및 안정성 문제를 해결할 뿐만 아니라, .NET 버전도 업그레이드하겠다는 것입니다. 하지만 IL2CPP 자체는 특별한 게 없습니다. Mono의 AOT 컴파일러를 대체하는 또 다른 AOT 컴파일러이기 때문입니다. 다만, 그동안은 Mono를 주력으로 사용하고, iOS 등 라이선스 정책 때문에 어쩔 수 없이 AOT 컴파일을 해야하는 상황에서만 AOT 컴파일러를 사용한 것과 달리 될 수 있으면 모든 플랫폼에 AOT를 적용하겠다는 게 달라진 부분입니다. 물론, 이는 성능이나 안정성 측면에서 IL2CPP가 제공하는 AOT 컴파일러 및 런타임이 Mono 런타임을 뛰어넘었을 경우에만 가능한 옵션이기도 합니다.

  • 노출된 문제점

IL2CPP은 크게 AOT 컴파일러와 VM으로 구성되어 있습니다. 바꿔 말해 Mono를 완전히 대체하는 대신 .NET API 구현 등 기존 Mono 인프라는 최대한 살려서 이용하고 성능 및 안정성, 이식성 등의 핵심이 되는 엔진만 교체하겠다는 구상입니다. 이 계획 자체는 상당히 합리적이라고 생각하지만, 문제는 시간입니다. .NET이라는 게 작은 플랫폼이 아니고, 다양한 플랫폼에서 새로운 엔진의 성능을 개선하고 안정성을 확보하는 것도 상당한 노력이 들어가는 작업입니다.

이 과정에서 가장 큰 변수는 애플의 64비트 지원 강제 정책입니다. iOS에 신규로 등록되는 모든 앱에 대해서 64비트 지원을 강제하고, 약간의 유예 기간이 있지만 기존 앱에 대해서도 64비트 지원을 요구하면서 Unity는 발등에 불이 떨어지게 됩니다. 왜냐하면, iOS 빌드에 사용해온 기존 Mono AOT 컴파일러가 64비트 빌드를 지원하지 않고 있기 때문에, 64비트 빌드를 지원하려면 반드시 IL2CPP를 사용해야 하는 상황이 되었기 때문입니다.

결국 Unity는 울며겨자 먹기 식으로 아직 안정성이 확보되지 않은 IL2CPP를 Unity 4와 5에 탑재하게 됩니다. UNITY 4.6.2 IOS 64-BIT SUPPORT 릴리즈 노트를 보면 BeginInvoke/EndInvoke등 .NET의 기본적인 API도 동작 안 하는 상태에서 iOS 64 비트 지원을 발표합니다. 이후 Unity는 거의 매주 업데이트 및 패치를 진행하면서 IL2CPP 버그에 대응하고 있는 상황입니다.

정리하면, 지금 Unity가 직면한 기술적인 문제들은 Unity의 미래를 흔들 수도 있을 만큼 심각합니다. IL2CPP가 이 문제에 대한 궁극적인 해결책이 되기도 어렵습니다. 단순한 안정성이나 성능 확보에도 어려움을 겪고 있는 상황에서 빠른 속도로 발전하는 .NET 기술을 따라가는 것이 게임 엔진 개발사의 Unity 입장에서 쉬운 일이 아니기 때문입니다.

결국 .NET 기술을 주도하고 있는 마이크로소프트와 어떤 식으로는 협력을 모델을 끝어내지 않으면 이런 한계를 넘어서기는 어려워 보입니다. 더군다나 마이크로소프트가 .NET 기술을 오픈소소화하면서 여러 가지 변화를 꾀하고 있는 상황이라 Unity가 직면한 불확싱성이 어느 때보다 커보입니다. 게임 개발사 입장에서 Unity는 여전히 좋은 도구이지만, 앞으로 계속 투자할 수 있는 기술인지에 대해서는 의문이 남는 대목입니다.

Advertisements

9 thoughts on “Unity가 직면한 기술적 문제들

  1. 게임 엔진 구조에 대해 무지한체 c#랭귀지의 장점만 골라서 쓰고 싶다는 글처럼 느껴지네요… 어차피 유니티 코어는 네이티브 렝귀지로 짜여있고 c#은 그냥 게임로직 스크립팅 랭귀지 정도로 쓰는걸텐데..
    .net async나 await같은 task palleralism같은거 지원하려면 코어 엔진 구조자체를 바꿔야되겠죠. 어차피 보통 게임엔진들은 렌더링이나 삐직을 위해서 지들 나름대로 네이티브로 구현한 잡시스템이 있을꺼고… 뭐 네이티브 메모리풀이랑 가비지콜렉터혼형해서 쓰는거보면 뭐 만들면될것 같기도하지만 오히려 더 골치아파질거 구지 구현할것 같지는 안네요.
    그리고 어차피 그러한 async,await같은 i/o관련 작업할때 중요한거니.. 게임 로직에서는 쓰지도 않을꺼고 뭐.. 네트워크 요청같은거 할때 좀 불편함이 있을것같긴하네요..
    뭐 reactive니 뭐시기니도 사실 그냥 event 시스템 조금 편리하게쓰려고 있는거고 솔찍히 그게 기술적인 문제라고 보긴 힘들죠. 아마도 유니티가 그거 외에 다른 존나 많은 편의기능이 있으니까 시장에서 많이 쓰는거자나요.
    모노 버전과 64비트 때문에 aot컴파일러 쓰는게 안정성이니 뭐니 하는것도 좀 어불설성이 아닌가 싶네여.. 그런걸로 미래가 흔들릴리가 없음… 어차피 모든 업데이트는 안정성 희생을 수반하지 않나요? 어차피 시장에 나가면 사고터지고 그거 고치면 안정되고.. 당연히 새플랫폼 지원하려고 고치면 원래 좀 안정되기까지 기간이 필요한거 아닙니까.. 그래도 시장에 요구에 맞출라고 그런거 감수하는거고..
    그냥 정리하자면 게임엔진에서 쓰는 c#이면 풀스택 .net 기술 자체가 필요없어요.

    ps. 유니티 5년전에 2시간정도 훑어본 수준이라 제가 한 모든소리가 개소리일 수도 있습니다.

    Liked by 1명

    • 한 기술에 대해서 개개인이 서로 다른 견해를 가지게 마련인데 예의 없이 댓글 쓰시는 게 참 안타깝고 불편하네요. 2시간정도 훑어보셔서 틀린 이야기를 할 수 있다는 분이 어투가 저렇게 나올 수 있다는 게 놀랍고요. 서로 견해 다를 수 있는 건 당연하고 이에 대해 얘기해서 더 좋은 생각이나 판단을 내릴 수 있는데 왜안써저님과는 건설적인 디스커션을 할 수 없는 상대네요. 블로거 주인장님, 무시하세요.

      Liked by 3 people

    • 5년 전이면 유니티 초창기 아닌가요? 단점이 없어 보였을 수 있는 시점일 수도 있죠.

      근 몇년 간 유니티가 엔진 시장에서 거의 독주하고 있었다고 생각하면

      1. 유니티의 성장에 따라 변화에 유연하게 대응할 수 없도록 조직이 비대화되었음.
      (또는 유니티가 안일해졌음)
      2. 타 경쟁사들의 견제
      3. 타 플랫폼 업체들의 견제 (1의 경쟁사들과 서드파티 형태의 협력을 갖추고 있다면 더더욱)

      위와 같은 환경이 원인이 되어 5년 사이 기술적 한계들이 드러나기 시작했을 수 있다고 생각합니다.
      저는 5년전 라이브 서비스중인 유니티 프로젝트에 참여했고, 지금도 유니티로 개발중인 프로젝트에 참여중입니다.
      기술에 대해서는 잘 모르지만, 원글과 댓글의 의견차를 개진하기에 시점 차이가 너무 큰 것 같네요.

      Liked by 2 people

  2. 좋은 글 읽고 갑니다.
    IL2CPP 이후 그 엄청난 버그(?)(사용하면 크래쉬 나는 것들)을 겪어본 사람으로써 굉장히 공감이 가는 글이네요.

    앞으로도 좋은 글 써주시길 .. 🙂

    좋아요

  3. 또하나의 문제점이 있다면 Garbage Collect 문제도 들 수 있겠죠. 이것 또한 Mono의 오래된 Version 문제와도 연관 되는것이기는 하지만, Mono 2.6은 Boehm garbage collector를 사용하고 있고, 이 GC 기술은 성능이 좋지 않은걸로 알고 있습니다. 그래서 최신 Mono에서는 Generational GC로 바뀌어져 있는데 현재 Unity는 Generational GC를 지원하지 못하죠. 이건 IL2CPP에서도 마찬가지 입니다. 또한 Mobile Device와 같이 Memory 자원이 한정된 장치에서는 Memory 관리가 필수인데, .NET 환경은 Embedded system을 염두해서 개발된 것이 아니기에 LINQ 기능과 같은 행사코드를 줄여주는 기능들은 필연적으로 GC의 부담을 늘어주기에 Unity Manual에도 사용을 자체(이건 윗글에서처럼 Reflect 일부 기능의 의해 LINQ 일부 기능에서 문제가 발생 한것과 또 다른 문제) 하도록 권하고 있는 내용이기도 합니다.

    Liked by 1명

    • 네. 공감합니다. 그리고 IL2CPP는 가비지 콜렉터를 교체할 수 있는 구조로 만들었다고 하는데, 아직은 Boehm GC만 지원하네요. 아직 할 일이 많이 보입니다.

      Liked by 1명

  4. 토론을 하고 싶으신건지, 시비를 걸고 싶은지 모를 태클이 있네요
    쥔장님 저런글은 그냥 무시하시는게….

    iOS에서 LINQ를 쓰면 생기는 버그때문에 고생한 사람으로써는 격하게 공감하고 갑니다..
    유니티도 이 버그를 알고있을텐데, 왜 수정하려 들지 않을까요

    좋아요

댓글이 닫혀있습니다.