빌드 프로세스 중에 Unity는 관리되는 코드 스트리핑이라는 프로세스를 통해 사용되지 않거나 도달할 수 없는 코드를 제거합니다. 이렇게 하면 애플리케이션의 최종 크기가 크게 줄어듭니다. 관리되는 코드 스트리핑은 프로젝트의 C# 스크립트에서 빌드된 어셈블리, 패키지 및 플러그인의 일부인 어셈블리, .NET Framework의 어셈블리 등 관리되는 어셈블리 코드를 제거합니다.
Unity는 Unity 링커라는 툴을 사용하여 프로젝트 어셈블리의 코드를 정적으로 분석합니다. 정적 분석은 실행 중에 도달할 수 없는 클래스, 클래스의 일부, 함수 또는 함수의 일부를 식별합니다. Unity가 정적 분석을 수행할 때 런타임에서 생성된 코드가 존재하지 않기 때문에 이 분석에는 빌드 시점에 존재하는 코드만 포함됩니다.
관리되는 스트리핑 레벨 설정을 사용하여 Unity가 프로젝트에 수행하는 코드 스트리핑 레벨을 설정할 수 있습니다. Unity가 코드의 특정 부분을 제거하지 않도록 하려면 주석을 사용하여 Unity 링커가 보존해야 하는 코드베이스 부분을 나타냅니다. 자세한 내용은 Unity 링커를 참조하십시오.
Managed Stripping Level 프로퍼티에 따라 애플리케이션 코드를 분석하고 스트리핑할 때 Unity 링커가 따르는 규칙 집합이 결정됩니다. 설정을 Minimal에서 High로 늘리면 규칙을 통해 링커가 더 많은 어셈블리를 통해 도달할 수 없는 코드를 검색할 수 있습니다. Unity 링커는 높은 설정에서 더 많은 코드를 제거하여 빌드의 최종 크기를 줄이지만, 검색이 확장되면 각 빌드를 생성하는 데 더 오래 걸립니다.
Managed Stripping Level 프로퍼티를 변경하려면 다음 단계를 따르십시오.
| 프로퍼티: | 기능: |
|---|---|
| Disabled | Unity가 코드를 제거하지 않습니다. 이 설정은 Mono 스크립팅 백엔드를 사용하는 경우에만 표시되며 기본 설정입니다. |
| Minimal | Unity가 UnityEngine 및 .NET 클래스 라이브러리에서 사용되지 않는 코드를 검색합니다. Unity는 사용자가 작성한 코드를 제거하지 않습니다. 이 설정은 예기치 않은 런타임 동작을 일으킬 가능성이 가장 낮습니다. 이 설정은 빌드 크기보다 가용성이 우선시되는 프로젝트에 유용합니다. IL2CPP 스크립팅 백엔드를 사용하는 경우 기본 설정입니다. |
| Low | Unity가 사용자가 작성한 일부 어셈블리와 모든 UnityEngine 및 .NET 클래스 라이브러리에서 사용되지 않는 코드를 검색합니다. 이 설정은 일부 사용하지 않는 코드를 제거하지만 반사를 사용하는 런타임 코드의 동작 변경 같은 의도하지 않은 결과가 발생할 가능성을 최소화하는 규칙 집합을 적용합니다. |
| Medium | Unity가 모든 어셈블리를 부분적으로 검색하여 도달할 수 없는 코드를 찾습니다. 이 설정은 빌드 크기를 줄이기 위해 더 많은 유형의 코드 패턴을 스트리핑하는 일련의 규칙을 적용합니다. Unity는 도달할 수 없는 모든 코드를 스트리핑하지는 않지만, 이 설정은 원치 않는 행동 또는 예기치 않은 행동 변경의 위험을 증가시킵니다. |
| High | Unity가 모든 어셈블리를 광범위하게 검색하여 도달할 수 없는 코드를 찾습니다. 이 설정에서 Unity는 크기 줄이기를 코드 안정성보다 더 우선시하고 가능한 한 많은 코드를 제거합니다. 이 검색은 낮은 스트리핑 레벨보다 훨씬 더 오래 걸릴 수 있습니다. 컴팩트한 빌드 크기가 매우 중요한 프로젝트에는 이 설정을 사용합니다. 애플리케이션을 철저하게 테스트하고 [Preserve] 속성과 link.xml 파일을 주의 깊게 사용하여 Unity 링커가 중요한 코드를 스트리핑하지 않도록 합니다. |
주석을 사용하여 Unity 링커가 코드의 특정 섹션을 스트리핑하지 못하도록 방지할 수 있습니다. 이는 애플리케이션이 Unity가 반사를 통해 정적 분석을 수행할 때 존재하지 않는 런타임 코드를 생성하는 경우 유용합니다. 주석은 Unity 링커에 어떤 코드 패턴을 스트리핑하지 않아야 하는지에 대한 일반적인 지침을 제공하거나, 정의된 특정 코드 섹션을 스트리핑하지 않도록 지시합니다.
관리되는 코드 스트리핑 프로세스에서 코드를 보존하기 위해 코드에 주석을 추가할 수 있는 다음 두 가지 광범위한 접근 방식이 있습니다.
이러한 각 기법은 Unity 링커가 높은 스트리핑 레벨에서 스트리핑하는 코드의 양을 더 세부적으로 제어하므로 중요한 코드가 스트리핑될 확률을 줄일 수 있습니다. 주석은 반사를 통해 다른 코드를 참조할 때 특히 유용합니다. Unity 링커는 반사의 사용을 항상 감지할 수 없기 때문입니다.
런타임 시 반사를 사용하거나 다른 코드를 생성하는 코드를 보존하여 애플리케이션이 실행될 때 예기치 않은 동작의 가능성을 크게 줄입니다. Unity 링커가 인식할 수 있는 반사 패턴의 예는 Unity 중간 언어 링커 반사 테스트 제품군을 참조하십시오.
루트 주석은 Unity 링커가 코드 요소를 코드 스트리핑 프로세스에서 제거되지 않는 루트로 취급하도록 합니다. 생성자나 어셈블리로 개별 유형을 보존해야 하는지에 따라 두 가지 유형의 루트 주석을 사용할 수 있습니다.
Preserve 속성을 사용하여 Unity 링커의 정적 분석에서 코드의 특정 섹션을 개별적으로 제외합니다. 이 속성을 사용하여 코드 조각에 주석을 붙이려면 보존하려는 코드의 첫 번째 부분 바로 앞에 [Preserve]를 추가합니다. 다음 목록에는 [Preserve] 속성을 사용하여 다른 코드 요소에 주석을 붙일 때 Unity 링커가 보존하는 엔티티가 나와 있습니다.
[Preserve] 속성을 할당하려면 어셈블리에 포함된 임의의 C# 파일에서 네임스페이스 선언 앞에 속성 선언을 배치합니다.[add] 접근자, [remove] 접근자를 보존합니다.유형과 기본 생성자를 모두 보존하려면 [Preserve] 속성을 사용하십시오. 둘 중 하나만 유지하거나 둘 다 유지하지 않으려면 link.xml 파일을 사용하십시오.
임의의 어셈블리와 네임스페이스에서 [Preserve] 속성을 정의할 수 있습니다. UnityEngine.Scripting.PreserveAttribute 클래스를 사용하거나, UnityEngine.Scripting.PreserveAttribute의 서브 클래스를 생성하거나, 자체 PreserveAttribute 클래스를 생성할 수 있습니다. 예시:
class Foo
{
[Preserve]
public void PreservedMethod(){}
}
프로젝트에 link.xml이라는 .xml 파일을 포함하여 특정 어셈블리 또는 어셈블리의 일부 목록을 보존할 수 있습니다. link.xml 파일은 프로젝트의 Assets 폴더 또는 Assets 폴더 하위 디렉토리에 있어야 하며 파일에 <linker> 태그를 포함해야 합니다. Unity 링커는 link.xml 파일에 보존된 어셈블리, 유형 또는 멤버를 루트 유형으로 취급합니다.
프로젝트에서 link.xml 파일을 원하는 수만큼 사용할 수 있습니다. 따라서 각 플러그인에 별도의 보존 선언을 제공할 수 있습니다. 패키지에 link.xml 파일을 포함할 수는 없지만, 패키지가 아닌 link.xml 파일에서 패키지 어셈블리를 참조할 수 있습니다.
다음 예시는 link.xml 파일을 사용하여 프로젝트 어셈블리의 루트 유형을 선언하는 다양한 방법을 나타냅니다.
<linker>
<!--Preserve types and members in an assembly-->
<assembly fullname="AssemblyName">
<!--Preserve an entire type-->
<type fullname="AssemblyName.MethodName" preserve="all"/>
<!--No "preserve" attribute and no members specified means preserve all members-->
<type fullname="AssemblyName.MethodName"/>
<!--Preserve all fields on a type-->
<type fullname="AssemblyName.MethodName" preserve="fields"/>
<!--Preserve all fields on a type-->
<type fullname="AssemblyName.MethodName" preserve="methods"/>
<!--Preserve the type only-->
<type fullname="AssemblyName.MethodName" preserve="nothing"/>
<!--Preserving only specific members of a type-->
<type fullname="AssemblyName.MethodName">
<!--Fields-->
<field signature="System.Int32 FieldName" />
<!--Preserve a field by name rather than signature-->
<field name="FieldName" />
<!--Methods-->
<method signature="System.Void MethodName()" />
<!--Preserve a method with parameters-->
<method signature="System.Void MethodName(System.Int32,System.String)" />
<!--Preserve a method by name rather than signature-->
<method name="MethodName" />
<!--Properties-->
<!--Preserve a property, its backing field (if present),
getter, and setter methods-->
<property signature="System.Int32 PropertyName" />
<property signature="System.Int32 PropertyName" accessors="all" />
<!--Preserve a property, its backing field (if present), and getter method-->
<property signature="System.Int32 PropertyName" accessors="get" />
<!--Preserve a property, its backing field (if present), and setter method-->
<property signature="System.Int32 PropertyName" accessors="set" />
<!--Preserve a property by name rather than signature-->
<property name="PropertyName" />
<!--Events-->
<!--Preserve an event, its backing field (if present), add, and remove methods-->
<event signature="System.EventHandler EventName" />
<!--Preserve an event by name rather than signature-->
<event name="EventName" />
</type>
</assembly>
</linker>
다음 예시는 전체 어셈블리를 선언하는 방법을 나타냅니다.
<!--Preserve an entire assembly-->
<assembly fullname="AssemblyName" preserve="all"/>
<!--No "preserve" attribute and no types specified means preserve all-->
<assembly fullname="AssemblyName"/>
<!--Fully qualified assembly name-->
<assembly fullname="AssemblyName, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="AssemblyName.Foo" preserve="all"/>
</assembly>
<!--Force an assembly to be processed for roots but don’t explicitly preserve anything in particular. Useful when the assembly isn't referenced.-->
<assembly fullname="AssemblyName" preserve="nothing"/>
이 예시는 중첩 유형 또는 일반 유형을 보존하는 방법을 나타냅니다.
<!--Examples with generics-->
<type fullname="AssemblyName.G`1">
<!--Preserve a field with generics in the signature-->
<field signature="System.Collections.Generic.List`1<System.Int32> FieldName" />
<field signature="System.Collections.Generic.List`1<T> FieldName" />
<!--Preserve a method with generics in the signature-->
<method signature="System.Void MethodName(System.Collections.Generic.List`1<System.Int32>)" />
<!--Preserve an event with generics in the signature-->
<event signature="System.EventHandler`1<System.EventArgs> EventName" />
</type>
<!--Preserve a nested type-->
<type fullname="AssemblyName.H/Nested" preserve="all"/>
<!--Preserve all fields of a type if the type is used. If the type isn't used, it will be removed-->
<type fullname="AssemblyName.I" preserve="fields" required="0"/>
<!--Preserve all methods of a type if the type is used. If the type isn't used, it will be removed-->
<type fullname="AssemblyName.J" preserve="methods" required="0"/>
<!--Preserve all types in a namespace-->
<type fullname="AssemblyName.SomeNamespace*" />
<!--Preserve all types with a common prefix in their name-->
<type fullname="Prefix*" />
link.xml 파일의 <assembly> 요소에는 주석을 통한 더 많은 제어를 위해 활성화할 수 있는 특수한 목적을 지닌 3가지 속성이 있습니다.
ignoreIfMissing: 모든 플레이어 빌드 동안 존재하지 않는 어셈블리에 보존을 선언해야 하는 경우 이 속성을 사용합니다.<linker>
<assembly fullname="Foo" ignoreIfMissing="1">
<type name="TypeName"/>
</assembly>
</linker>
ignoreIfUnreferenced: 경우에 따라 다른 어셈블리가 해당 어셈블리를 참조하는 경우에만 어셈블리의 엔티티를 보존하려고 할 수 있습니다. 이 속성을 사용하면 어셈블리에서 하나 이상의 유형이 참조되는 경우에만 어셈블리의 엔티티를 보존할 수 있습니다.<linker>
<assembly fullname="Bar" ignoreIfUnreferenced="1">
<type name="TypeName"/>
</assembly>
</linker>
windowsruntime: Windows 런타임 메타데이터(.winmd) 어셈블리에 보존을 정의하는 경우 link.xml 파일의 <assembly> 요소에 windowsruntime 속성을 추가해야 합니다.<linker>
<assembly fullname="Windows" windowsruntime="true">
<type name="TypeName"/>
</assembly>
</linker>
종속성 주석은 다양한 코드 요소 간의 종속성을 정의합니다. 이 주석은 반사와 같이 Unity 링커가 정적으로 분석할 수 없는 코드 패턴을 보존하는 데 유용합니다. 또한 이러한 주석은 루트 요소가 사용하지 않을 때 이러한 코드 요소가 잘못 보존되지 않도록 합니다. 다음 두 가지 메서드를 사용하여 Unity 링커가 코드 요소를 처리하는 방식을 변경할 수 있습니다.
[Preserve] 속성은 API가 항상 필요할 때 유용합니다. 다른 속성은 더 일반적인 보존에 유용할 수 있습니다. 예를 들어, RequireImplementorsAttribute를 사용하여 인터페이스에 주석을 붙여 특정 인터페이스를 구현하는 모든 유형을 보존할 수 있습니다.
특정 코딩 패턴을 주석에 추가하려면 다음 속성 중 하나 이상을 사용합니다.
Unity 링커가 코드를 보존하는 방법을 더욱 정확하게 제어하기 위해 이러한 속성을 다양한 방식으로 결합할 수 있습니다.
[assembly: UnityEngine.Scripting.AlwaysLinkAssembly] 속성은 빌드에 포함된 다른 어셈블리가 어셈블리를 참조하는지와 관계없이 Unity 링커가 어셈블리를 검색하도록 합니다. 어셈블리에만 AlwaysLinkAssembly 속성을 적용할 수 있습니다.
이 속성은 어셈블리 내에서 코드를 직접 보존하지 않습니다. 대신 이 속성은 어셈블리에 루트 마킹 규칙을 적용하도록 Unity 링커에 지시합니다. 어셈블리의 루트 마킹 규칙과 일치하는 코드 요소가 없는 경우에도 Unity 링커는 빌드에서 어셈블리를 제거합니다.
[RuntimeInitializeOnLoadMethod] 속성이 포함된 메서드를 하나 이상 포함하되 프로젝트의 씬에 직접적으로나 간접적으로 사용된 유형을 포함하지 않을 수 있는 사전 컴파일된 어셈블리 또는 패키지 어셈블리에 이 속성을 사용하십시오.
어셈블리가 [assembly: AlwaysLinkAssembly]를 정의하며 빌드에 포함된 다른 어셈블리에 의해 참조되는 경우 이 속성은 결과에 영향을 미치지 않습니다.