Version: 2023.2
언어: 한국어
관리되는 코드 스트리핑
Unity 에디터에서 코드 재로드

Unity linker

Unity 빌드 프로세스는 Unity linker라는 툴을 사용하여 관리되는 코드를 스트리핑합니다. Unity linker는 Unity에서 작동하도록 커스터마이즈된 IL Linker 버전입니다. Unity linker의 커스텀 Unity 엔진에 한정된 부분은 공개적으로 사용할 수 없습니다.

Unity linker는 관리되는 코드 스트리핑과 엔진 코드 스트리핑 프로세스의 일부에 책임이 있으며 이는 미사용 엔진 코드를 제거하는 IL2CPP 스크립트 백엔드를 통해 사용할 수 있는 별도의 프로세스입니다. 자세한 내용은 PlayerSettings.StripEngineCode]을 참조하십시오.

Unity linker 동작 방식

Unity linker는 프로젝트의 모든 어셈블리를 분석합니다. 먼저 루트 타입과 메서드, 프로퍼티, 필드를 마킹합니다. 예를 들어 씬의 게임 오브젝트에 추가한 MonoBehaviour에서 파생된 클래스는 루트 타입입니다. 다음으로 Unity linker는 식별하기 위해 마킹한 루트를 분석하여 이러한 루트가 의존하는 모든 관리되는 코드를 마킹합니다. 이와 같은 정적 분석이 끝나면 마킹되지 않은 모든 잔류 코드는 애플리케이션 코드의 실행 경로를 통해 도달할 수 없는 것으로 파악되어 어셈블리에서 삭제됩니다.

Unity linker가 어셈블리를 스트리핑하는 방법

Unity 에디터는 Unity 프로젝트의 모든 씬에서 사용되는 타입이 포함된 어셈블리 리스트를 생성하여 이 리스트를 Unity linker에 전달합니다. 그런 다음 Unity linker는 해당 어셈블리, 해당 어셈블리의 모든 레퍼런스, link.xml 파일에 선언된 모든 어셈블리, AlwaysLinkAssembly 속성을 가진 모든 어셈블리를 처리합니다. 일반적으로 Unity linker는 이러한 카테고리 중 하나에 해당하지 않는 프로젝트 내 어셈블리를 처리하지 않으며 플레이어 빌드에서 이를 제외합니다.

Unity linker가 어셈블리를 처리할 때마다 어셈블리 분류, 어셈블리가 씬에서 사용되는 타입을 포함하는지 여부 및 사용자가 해당 빌드에 대해 선택한 Managed Stripping Level에 따라 일련의 규칙을 준수합니다.

이러한 규칙에서 어셈블리는 다음과 같이 분류됩니다.

  • .NET Class Library 어셈블리 — mscorlib.dll과 System.dll과 같은 Mono 클래스 라이브러리 및 netstandard.dll과 같은 .NET 클래스 라이브러리 파사드 어셈블리를 모두 포함합니다.
  • Platform SDK 어셈블리 — 플랫폼 SDK의 관리되는 어셈블리를 포함합니다(예: 유니버설 Windows 플랫폼 SDK의 일부인 windows.winmd 어셈블리).
  • Unity Engine Module 어셈블리 — UnityEngine.Core.dll 등 Unity 엔진을 구성하는 관리되는 어셈블리를 포함합니다.
  • Project 어셈블리 — 프로젝트 어셈블리를 포함합니다. 예:

마킹 규칙

Unity에서 프로젝트를 빌드하면 빌드 프로세스는 C# 코드를 CIL(Common Intermediate Language)이라는 .NET 바이트코드 포맷으로 컴파일합니다. Unity는 이러한 CIL 바이트코드를 어셈블리라고 부르는 파일로 패키징합니다. 프로젝트에서 사용하는 .NET 프레임워크 라이브러리와 플러그인의 모든 C# 라이브러리는 또한 CIL 바이트코드의 어셈블리로 미리 패키징됩니다.

Unity linker는 정적 분석을 수행하면 일련의 규칙을 따라 Unity linker가 빌드에 필요하다고 마킹한 CIL 바이트코드의 일부를 결정합니다. 루트 마킹 규칙은 Unity linker가 빌드에서 최상위 레벨의 어셈블리를 식별하고 보존하는 방법을 결정합니다. 종속성 마킹 규칙은 Unity linker가 루트 어셈블리가 의존하는 코드를 식별하고 보존하는 방법을 결정합니다.

Managed Stripping Level 프로퍼티는 Unity linker가 사용하는 규칙 세트를 변경합니다. 다음 섹션에서는 Managed Stripping Level 프로퍼티용으로 가능한 각 설정에 대한 마킹 규칙을 설명합니다.

루트 마킹 규칙

다음 표에서는 Unity linker가 어셈블리의 상위 레벨 타입을 식별하는 방식을 설명합니다.

어셈블리 타입: 마킹 규칙:
Minimal 낮음 중간 높음
.NET 클래스 & 플랫폼 SDK 및 UnityEngine 어셈블리 예방적 보존과 모든 link.xml에 정의된 모든 보존을 적용합니다. 예방적 보존과 모든 link.xml에 정의된 모든 보존을 적용합니다. 모든 link.xml 파일에 정의된 모든 보존을 적용합니다. 모든 link.xml 파일에 정의된 모든 보존을 적용합니다.
씬에서 참조된 타입을 포함한 어셈블리 어셈블리의 모든 타입과 멤버 마킹. 어셈블리의 모든 타입과 멤버 마킹. 다음을 마킹합니다.
[RuntimeInitializeOnLoadMethod] 또는 [Preserve] 속성을 가진 모든 메서드
•모든 link.xml 파일에 정의된 보존
•미리 컴파일된 패키지, Unity 스크립트 또는 어셈블리 정의 어셈블리의 MonoBehaviour와 ScriptableObject에서 파생된 모든 타입을 마킹
다음을 마킹합니다.
[RuntimeInitializeOnLoadMethod] 또는 [Preserve] 속성을 가진 모든 메서드
•모든 link.xml 파일에 정의된 보존
•미리 컴파일된 패키지, Unity 스크립트 또는 어셈블리 정의 어셈블리의 MonoBehaviour와 ScriptableObject에서 파생된 모든 타입을 마킹
기타 어셈블리의 모든 타입과 멤버 마킹. 다음을 마킹합니다.
•모든 공용 타입과 해당 타입의 공용 멤버
[RuntimeInitializeOnLoadMethod] 또는 [Preserve] 속성을 가진 모든 메서드
• 모든 link.xml 파일에 정의된 보존
•미리 컴파일된 패키지, Unity 스크립트 또는 어셈블리 정의 어셈블리의 MonoBehaviour와 ScriptableObject에서 파생된 모든 타입
다음을 마킹합니다.
•모든 공용 타입과 해당 타입의 공용 멤버
[RuntimeInitializeOnLoadMethod] 또는 [Preserve] 속성을 가진 모든 메서드
• 모든 link.xml 파일에 정의된 보존
•미리 컴파일된 패키지, Unity 스크립트 또는 어셈블리 정의 어셈블리의 MonoBehaviour와 ScriptableObject에서 파생된 모든 타입
다음 사항을 마킹합니다.
[RuntimeInitializeOnLoadMethod] 또는 [Preserve] 속성을 가진 모든 메서드
•모든 link.xml 파일에 정의된 보존
•미리 컴파일된 패키지, Unity 스크립트 또는 어셈블리 정의 어셈블리의 MonoBehaviour와 ScriptableObject에서 파생된 모든 타입
테스트 [UnityTest] 속성이 있는 모든 메서드와 NUnit.Framework에서 정의한 속성으로 표시된 모든 메서드를 마킹합니다.

종속성 마킹 규칙

Unity linker는 어셈블리에서 루트를 식별한 후 해당 루트가 의존하는 모든 코드를 식별해야 합니다. 다음 표에서는 Unity linker가 어셈블리의 루트 타입에 대한 의존성을 식별하는 방식을 설명합니다.

규칙 타겟 각 스트리핑 레벨의 행동
Minimal 낮음 중간 높음
MonoBehaviour Unity linker는 타입을 마킹할 때 MonoBehavior 타입의 모든 멤버를 마킹합니다.
ScriptableObject Unity linker는 타입을 마킹할 때 ScriptableObject 타입의 모든 멤버를 마킹합니다.
Attributes Unity linker가 어셈블리, 타입이나 다른 코드 구조체를 마킹할 때 Unity linker는 또한 해당 구조체의 모든 속성을 마킹합니다. Unity linker가 어셈블리, 타입이나 다른 코드 구조체를 마킹할 때 Unity linker는 또한 해당 구조체의 모든 속성을 마킹합니다. Unity linker가 어셈블리, 타입이나 다른 코드 구조체를 마킹할 때 속성 타입도 마킹되는 경우 Unity linker는 해당 구조체의 속성만을 마킹합니다. Unity linker가 어셈블리, 타입이나 다른 코드 구조체를 마킹할 때 속성 타입도 마킹되는 경우 Unity linker는 해당 구조체의 속성만을 마킹합니다.
Debugging Attributes 스크립트 디버깅을 활성화하면 Unity linker는 해당 멤버를 사용하는 코드 경로가 없어도 [DebuggerDisplay] 속성이 정의된 모든 멤버를 마킹합니다. 스크립트 디버깅을 활성화하면 Unity linker는 해당 멤버를 사용하는 코드 경로가 없어도 [DebuggerDisplay] 속성이 정의된 모든 멤버를 마킹합니다. Unity linker는 DebuggerDisplayAttribute 및 DebuggerTypeProxyAttribute와 같은 디버깅 속성을 항상 제거합니다. Unity linker는 DebuggerDisplayAttribute 및 DebuggerTypeProxyAttribute와 같은 디버깅 속성을 항상 제거합니다.
.NET Facade Class Library 파사드 어셈블리는 런타임 시 필요하지 않기 때문에 제거합니다.

Link XML 기능 태그 제외

Link.xml 파일은 자주 사용되지 않는 “features” XML 속성을 지원합니다. 예를 들어 mscorlib.dll에 내장된 mscorlib.xml 파일이 이 속성을 사용하지만, 적합한 경우 모든 link.xml 파일에서 사용할 수 있습니다.

High 스트리핑 레벨을 사용할 때 Unity linker는 현재 빌드에 대한 설정에 기반하여 지원되지 않는 기능의 보존을 제외합니다.

  1. remoting — IL2CPP 스크립팅 백엔드를 타겟팅하는 경우 제외됩니다.
  2. sre — IL2CPP 스크립팅 백엔드를 타겟팅하는 경우 제외됩니다.
  3. com — COM을 지원하지 않는 플랫폼을 타겟팅하는 경우 제외됩니다.

예를 들어 다음의 link.xml 파일은 COM을 지원하는 플랫폼에 포함된 타입의 메서드 1가지 및 모든 플랫폼의 메서드 1가지를 지원합니다.

<linker>

    <assembly fullname="Foo">

        <type fullname="Type1">

            <!--Preserve FeatureOne on platforms that support COM-->

            <method signature="System.Void FeatureOne()" feature="com"/>

            <!--Preserve FeatureTwo on all platforms-->

            <method signature="System.Void FeatureTwo()"/>

        </type>

    </assembly>

</linker>

메서드 바디 편집

High 스트리핑 레벨을 사용하면 Unity linker는 코드 크기를 추가로 축소하기 위해 메서드 바디를 편집합니다. 이 섹션에서는 Unity linker가 메서드 바디에 수행하는 주요 편집 몇 가지를 요약하여 설명합니다.

Unity linker가 .NET 클래스 라이브러리 어셈블리의 메서드 바디만 편집합니다. 메서드 바디 편집 후에는 어셈블리의 소스 코드가 더 이상 어셈블리의 컴파일된 코드와 일치하지 않으므로 디버깅이 더 어려워질 수 있습니다.

다음 리스트에서는 Unity linker가 메서드 바디를 편집하기 위해 수행할 수 있는 행동을 설명합니다.

  • 도달할 수 없는 브랜치 제거 - Unity linker는 System.Environment.OSVersion.Platform을 확인하고 현재 타겟인 플랫폼에 도달할 수 없는 If문 블록을 제거합니다.
  • 필드만 액세스하는 인라인 메서드 - Unity linker는 호출을 필드에 바로 액세스하는 필드를 가져오거나 설정하는 메서드로 교체합니다. 이 방법을 사용하여 종종 메서드를 완전히 스트리핑할 수 있습니다. Mono backend를 사용할 때 필드의 가시성에 기반하여 메서드의 호출자가 필드에 직접 액세스하도록 허용할 때 Unity linker는 이렇게 변경합니다. IL2CPP의 경우 가시성 규칙은 적용되지 않으므로 Unity linker는 적절한 경우 이렇게 변경합니다.
  • const 값을 반환하는 인라인 메서드 - const 값만을 반환하는 메서드 호출을 인라이닝합니다.
  • 비어있는 비 반환 호출 제거 - Unity linker는 비어있고 유효한 반환 타입이 있는 메서드에 대한 호출을 제거합니다.
  • 빈 범위 제거 - Unity linker는 최종 블록이 비어 있는 경우 Try/Finally 블록을 제거합니다. 빈 호출을 제거하면 빈 Finally 블록이 생성될 수 있습니다. 메서드 편집 중 이러한 현상이 발생하면 Unity linker는 전체 Try/Finally 블록을 제거합니다. 예를 들어 컴파일러가 Dispose()를 호출하기 위해 Foreach 루프의 일부로 Try/Finally 블록을 생성하는 경우 이러한 현상이 발생할 수 있습니다.
관리되는 코드 스트리핑
Unity 에디터에서 코드 재로드