Skip to content

Unity 6 스크립팅 백엔드와 가비지 컬렉션#

들어가며#

Unity의 스크립팅 백엔드와 가비지 컬렉션은 모두 성능에 영향을 주지만, 해결하는 문제는 다르다.

  • 스크립팅 백엔드는 C# 코드를 어떤 방식으로 플레이어에 포함하고 실행할지 결정한다.
  • 가비지 컬렉션은 실행 중 관리되는 힙에 쌓인 더 이상 참조되지 않는 객체를 정리한다.

두 개념을 한 설정으로 생각하면 빌드 시간, 앱 크기, 프레임 스파이크 문제를 잘못 진단하기 쉽다. 이 문서는 Unity 6를 기준으로 Mono, IL2CPP, 코드 스트리핑, 점진적 GC의 역할과 선택 절차를 정리한다.

먼저 기억할 점

IL2CPP로 전환한다고 관리되는 할당이 사라지지는 않는다. GC 스파이크는 할당량과 객체 그래프를 먼저 측정해 해결하고, 스크립팅 백엔드는 대상 플랫폼과 빌드 요구사항을 기준으로 선택한다.

스크립팅 백엔드는 무엇을 바꾸는가#

Unity 프로젝트의 C# 코드는 먼저 중간 언어로 컴파일된다. 이후 선택한 스크립팅 백엔드가 대상 플랫폼에서 실행할 수 있는 형태로 변환한다.

C# 소스 코드
    ↓
관리되는 중간 언어와 어셈블리
    ├─ Mono: 런타임 JIT 컴파일
    └─ IL2CPP: C++ 변환 → 대상 플랫폼 네이티브 바이너리(AOT)

IL2CPP는 중간 언어를 C++로 변환한 뒤 대상 플랫폼용 네이티브 바이너리를 생성하는 AOT 백엔드다. Mono는 런타임 JIT 방식을 사용한다. 지원 여부와 기본값은 대상 플랫폼과 Unity 버전에 따라 달라지므로, 실제 배포 대상의 Player Settings를 기준으로 확인해야 한다.

관점 Mono IL2CPP
코드 실행 방식 관리되는 코드를 런타임에 JIT 컴파일 C++ 변환 후 네이티브 코드로 AOT 컴파일
반복 빌드 일반적으로 변환·네이티브 컴파일 단계가 적다 C++ 변환과 네이티브 컴파일로 빌드 시간이 늘 수 있다
최종 산출물 관리되는 런타임과 어셈블리 중심 대상 플랫폼 네이티브 코드 포함
대상 플랫폼 플랫폼별 지원 여부를 확인해야 한다 JIT를 허용하지 않는 플랫폼을 포함해 플랫폼별 지원 여부를 확인해야 한다
전환 시 검증 에디터와 대상 기기 동작 네이티브 플러그인, 리플렉션, 코드 스트리핑, 실제 기기 빌드

성능 보장은 아니다

IL2CPP는 네이티브 코드를 생성하지만, 모든 프로젝트에서 자동으로 프레임 성능을 높여 주지는 않는다. 빌드 시간·앱 크기·디버깅·플러그인 호환성까지 포함해 대상 기기에서 측정해야 한다.

스크립팅 백엔드 선택 절차#

Unity 6에서는 대상 플랫폼을 선택한 뒤 Player Settings의 Configuration 영역에서 스크립팅 백엔드를 확인한다.

  1. 대상 플랫폼의 스토어·보안·JIT 제한 사항을 확인한다.
  2. 해당 플랫폼에서 지원하는 백엔드 중 배포 요구사항에 맞는 것을 선택한다.
  3. IL2CPP를 선택했다면 CI에서 전체 네이티브 빌드를 수행한다.
  4. 리플렉션, 동적 타입 검색, 직렬화, 네이티브 플러그인을 사용하는 기능을 실제 기기에서 검증한다.
  5. 릴리스 후보와 같은 설정으로 빌드 시간, 앱 크기, 시작 시간, 프레임 시간을 비교한다.

개발과 배포를 분리해 판단

에디터 반복 작업이 빠르다는 이유만으로 배포 백엔드를 정하지 않는다. 반대로 배포 백엔드가 IL2CPP라는 이유만으로 모든 개발 빌드를 무겁게 만들 필요도 없다. 팀의 CI 시간과 대상 플랫폼 요구사항을 함께 기준으로 삼는다.

코드 스트리핑은 런타임 메모리 최적화와 다르다#

관리되는 코드 스트리핑은 빌드 시 도달할 수 없다고 판단한 코드와 어셈블리 일부를 제거해 최종 앱 크기를 줄이는 과정이다. IL2CPP에서는 변환·컴파일할 코드량에도 영향을 줄 수 있다.

그러나 스트리핑 수준을 높이는 것은 런타임 메모리 문제를 해결하는 일반 해법이 아니다. 리플렉션이나 문자열 기반 타입 탐색처럼 정적 분석으로 사용 여부를 알기 어려운 코드가 제거될 위험이 있다.

상황 권장 대응
리플렉션으로 타입이나 멤버를 찾음 필요한 타입을 Preserve 특성 또는 link.xml로 보존한다
플러그인이 런타임에 타입을 조회함 플러그인 문서의 보존 지침을 적용하고 실제 기기에서 검증한다
앱 크기를 줄여야 함 낮은 스트리핑 수준부터 릴리스 후보 테스트를 통과시킨 뒤 단계적으로 높인다
실행 중 메모리가 증가함 Memory Profiler와 Profiler로 원인을 분류한다

스트리핑 변경은 회귀 테스트가 필요하다

Medium 또는 High 수준에서는 리플렉션 기반 코드가 누락될 수 있다. 설정을 변경한 빌드는 로그인, 직렬화, 네트워크 모델, 플러그인 초기화 등 동적 코드 경로를 포함해 반드시 검사한다.

가비지 컬렉션의 역할#

Unity의 가비지 컬렉터는 관리되는 힙에서 더 이상 참조되지 않는 C# 객체를 정리한다. Mono와 IL2CPP 모두에서 관리되는 할당은 발생할 수 있으므로, 백엔드 변경만으로 GC 문제를 해결할 수는 없다.

Unity 6의 점진적 GC는 컬렉션 작업을 여러 프레임에 나눠 긴 중단을 짧은 작업으로 분산한다. 전체 GC 작업량을 줄이지는 않지만, GC 관련 프레임 스파이크를 완화할 수 있다.

항목 의미
점진적 GC 컬렉션 작업을 여러 프레임으로 분산해 프레임 일관성을 높인다
비점진적 GC 컬렉션 시점에 긴 중단이 발생할 수 있다
수동 GC 제어 앱 전체의 정책으로만 사용하며, 서드파티 라이브러리가 임의로 바꾸지 않게 한다
지원 제약 웹 플랫폼은 점진적 GC를 지원하지 않으므로 대상별 동작을 확인한다

점진적 GC는 기본적으로 활성화돼 있지만, 참조 변경이 매우 많은 워크로드에서는 효과가 제한되거나 전체 컬렉션으로 폴백할 수 있다. Player Settings의 Use Incremental GC를 바꾸기 전에 Profiler에서 GC.Alloc, 프레임 시간, 컬렉션 빈도를 측정한다.

GC.Collect를 일반적인 해결책으로 쓰지 않는다

System.GC.Collect는 힌트를 전달하는 API가 아니라 전체 컬렉션을 직접 실행할 수 있다. 명확한 중단 구간과 측정 결과가 있는 경우에만 앱 수준 정책으로 검토한다.

진단과 개선 순서#

  1. 실제 대상 기기에서 재현 가능한 시나리오를 만든다.
  2. Profiler에서 프레임 시간, GC.Alloc, 관리 힙 증가를 확인한다.
  3. 같은 시나리오 전후로 Memory Profiler 스냅샷을 비교한다.
  4. 문제를 일시적 관리 할당, 오래 유지되는 관리 객체, 네이티브 객체·에셋, 그래픽 메모리로 분류한다.
  5. 할당을 줄이거나 참조 수명을 바로잡은 뒤 동일 시나리오로 재측정한다.

관리되는 할당과 리소스 수명 관리는 Unity 6 메모리 관리와 프로파일링에서 다룬다.

배포 전 체크리스트#

  • 대상 플랫폼에서 지원하는 스크립팅 백엔드를 사용하고 있는가
  • IL2CPP 빌드를 실제 CI와 대상 기기에서 검증했는가
  • 스트리핑 변경 후 리플렉션·직렬화·플러그인 기능을 테스트했는가
  • GC.Alloc과 프레임 스파이크를 대상 기기에서 측정했는가
  • 점진적 GC 설정 변경 전후의 프레임 시간과 메모리를 비교했는가

참고 자료#

Comments