.NET MAUI 앱 자르기
앱을 빌드할 때 .NET 다중 플랫폼 앱 UI(.NET MAUI)는 트리밍이라는 기술로 앱의 전체 크기를 줄이기 위해 호출된 ILLink 링커를 사용할 수 있습니다. ILLink 는 컴파일러에서 생성된 중간 코드를 분석하여 크기를 줄입니다. 사용되지 않는 메서드, 속성, 필드, 이벤트, 구조체 및 클래스를 제거하여 앱을 실행하는 데 필요한 코드 및 어셈블리 종속성만 포함하는 앱을 생성합니다.
앱을 트리밍할 때의 동작 변경을 방지하기 위해 .NET은 트리밍 경고를 통해 트리밍 호환성에 대한 정적 분석을 제공합니다. 트리머는 트리밍과 호환되지 않을 수 있는 코드를 발견하면 자르기 경고를 생성합니다. 트리밍 경고가 있는 경우 수정해야 하며, 동작 변경이 없는지 확인하기 위해 트리밍 후 앱을 철저히 테스트해야 합니다. 자세한 내용은 트리밍 경고 소개를 참조 하세요.
트리밍 동작
빌드 속성을 다음 중 하나 partial
또는 full
다음으로 설정 $(TrimMode)
하여 트리밍 동작을 제어할 수 있습니다.
<PropertyGroup>
<TrimMode>full</TrimMode>
</PropertyGroup>
Important
빌드 속성은 $(TrimMode)
빌드 구성에 의해 조건화되어서는 안 됩니다. 이는 빌드 속성의 $(TrimMode)
값에 따라 기능 스위치를 사용하거나 사용하지 않도록 설정하고, 코드가 동일하게 작동하도록 모든 빌드 구성에서 동일한 기능을 사용하거나 사용하지 않도록 설정해야 했기 때문입니다.
트리밍 모드는 full
앱에서 사용되지 않는 코드를 제거합니다. 트리밍 모드는 partial
BCL(기본 클래스 라이브러리), 기본 플랫폼에 대한 어셈블리(예: Mono.Android.dll 및 Microsoft.iOS.dll) 및 빌드 항목으로 트리밍을 선택한 다른 어셈블리를 트리밍 $(TrimmableAsssembly)
합니다.
<ItemGroup>
<TrimmableAssembly Include="MyAssembly" />
</ItemGroup>
이는 어셈블리를 빌드할 때 [AssemblyMetadata("IsTrimmable", "True")]
를 설정하는 것과 같습니다.
참고 항목
기본적으로 설정되므로 빌드 속성을 true
앱의 프로젝트 파일로 설정할 $(PublishTrimmed)
필요는 없습니다.
더 많은 트리밍 옵션은 트리밍 옵션을 참조하세요.
트리밍 기본값
기본적으로 Android 및 Mac Catalyst 빌드는 빌드 구성이 릴리스 빌드로 설정된 경우 부분 트리밍을 사용합니다. iOS는 빌드 구성에 관계없이 모든 디바이스 빌드에 부분 트리밍을 사용하며 시뮬레이터 빌드에 트리밍을 사용하지 않습니다.
비호환성 트리밍
다음 .NET MAUI 기능은 전체 트리밍과 호환되지 않으며 트리머에 의해 제거됩니다.
- 바인딩 경로가 문자열로 설정된 바인딩 식입니다. 대신 컴파일된 바인딩을 사용합니다. 자세한 내용은 컴파일된 바인딩을 참조 하세요.
- XAML의 속성에 호환되지 않는 형식의 값을 할당하거나 서로 다른 형식의 두 속성이 데이터 바인딩을 사용하는 경우 암시적 변환 연산자입니다. 대신, 형식에 대한 a를 TypeConverter 정의하고 TypeConverterAttribute. 자세한 내용은 암시적 변환 연산자를 대체할 TypeConverter 정의를 참조 하세요.
- 확장 메서드를 사용하여 런타임에 XAML을 로드합니다 LoadFromXaml . 이 XAML은 런타임
DynamicallyAccessedMembers
에 로드할 수 있는 모든 형식에 특성 또는DynamicDependency
특성에 주석을 추가하여 트리밍을 안전하게 만들 수 있습니다. 그러나 이는 매우 오류가 발생하기 쉬울 수 있으며 권장되지 않습니다. - 를 사용하여 QueryPropertyAttribute탐색 데이터 수신 대신 쿼리 매개 변수를 IQueryAttributable 수락해야 하는 형식에 대한 인터페이스를 구현해야 합니다. 자세한 내용은 단일 메서드를 사용하여 탐색 데이터 처리를 참조하세요.
SearchHandler.DisplayMemberName
속성 대신 결과의 모양을 SearchHandler 정의하는 방법을 제공해야 ItemTemplate 합니다. 자세한 내용은 검색 결과 항목 모양 정의를 참조 하세요.- HybridWebView 동적
System.Text.Json
serialization 기능의 사용으로 인해 컨트롤입니다.
또는 트리머가 이러한 기능에 대한 코드를 유지하게 기능 스위치를 사용할 수 있습니다. 자세한 내용은 트리밍 기능 스위치를 참조 하세요.
.NET 트리밍 비호환성은 알려진 트리밍 비호환성을 참조 하세요.
암시적 변환 연산자를 대체할 TypeConverter 정의
호환되지 않는 형식의 값을 XAML의 속성에 할당하거나 다른 형식의 두 속성이 데이터 바인딩을 사용하는 경우 전체 트리밍을 사용하는 경우 암시적 변환 연산자를 사용할 수 없습니다. 이는 C# 코드에서 사용되지 않는 경우 트리머에서 암시적 연산자 메서드를 제거할 수 있기 때문입니다. 암시적 변환 연산자에 대한 자세한 내용은 사용자 정의 명시적 및 암시적 변환 연산자를 참조 하세요.
예를 들어 암시적 변환 연산 SizeRequest
자를 정의하는 다음 형식을 고려해 보세요.Size
namespace MyMauiApp;
public struct SizeRequest : IEquatable<SizeRequest>
{
public Size Request { get; set; }
public Size Minimum { get; set; }
public SizeRequest(Size request, Size minimum)
{
Request = request;
Minimum = minimum;
}
public SizeRequest(Size request)
{
Request = request;
Minimum = request;
}
public override string ToString()
{
return string.Format("{{Request={0} Minimum={1}}}", Request, Minimum);
}
public bool Equals(SizeRequest other) => Request.Equals(other.Request) && Minimum.Equals(other.Minimum);
public static implicit operator SizeRequest(Size size) => new SizeRequest(size);
public static implicit operator Size(SizeRequest size) => size.Request;
public override bool Equals(object? obj) => obj is SizeRequest other && Equals(other);
public override int GetHashCode() => Request.GetHashCode() ^ Minimum.GetHashCode();
public static bool operator ==(SizeRequest left, SizeRequest right) => left.Equals(right);
public static bool operator !=(SizeRequest left, SizeRequest right) => !(left == right);
}
전체 트리밍을 사용하도록 설정하면 C# 코드에서 사용되지 않는 경우 트리머 간에 SizeRequest
Size
암시적 변환 연산자를 제거할 수 있습니다.
대신 다음을 사용하여 TypeConverterAttribute형식에 대한 a를 TypeConverter 정의하고 형식에 연결해야 합니다.
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
namespace MyMauiApp;
[TypeConverter(typeof(SizeRequestTypeConverter))]
public struct SizeRequest : IEquatable<SizeRequest>
{
public Size Request { get; set; }
public Size Minimum { get; set; }
public SizeRequest(Size request, Size minimum)
{
Request = request;
Minimum = minimum;
}
public SizeRequest(Size request)
{
Request = request;
Minimum = request;
}
public override string ToString()
{
return string.Format("{{Request={0} Minimum={1}}}", Request, Minimum);
}
public bool Equals(SizeRequest other) => Request.Equals(other.Request) && Minimum.Equals(other.Minimum);
public static implicit operator SizeRequest(Size size) => new SizeRequest(size);
public static implicit operator Size(SizeRequest size) => size.Request;
public override bool Equals(object? obj) => obj is SizeRequest other && Equals(other);
public override int GetHashCode() => Request.GetHashCode() ^ Minimum.GetHashCode();
public static bool operator ==(SizeRequest left, SizeRequest right) => left.Equals(right);
public static bool operator !=(SizeRequest left, SizeRequest right) => !(left == right);
private sealed class SizeRequestTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
=> sourceType == typeof(Size);
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
=> value switch
{
Size size => (SizeRequest)size,
_ => throw new NotSupportedException()
};
public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType)
=> destinationType == typeof(Size);
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
if (value is SizeRequest sizeRequest)
{
if (destinationType == typeof(Size))
return (Size)sizeRequest;
}
throw new NotSupportedException();
}
}
}
트리밍 기능 스위치
.NET MAUI에는 기능 스위치라고 하는 트리머 지시문이 있어 트리밍이 안전하지 않은 기능에 대한 코드를 유지할 수 있습니다. 이러한 트리머 지시문은 네이티브 AOT뿐만 아니라 빌드 속성이 설정된 full
경우 $(TrimMode)
사용할 수 있습니다.
MSBuild 속성 | 설명 |
---|---|
MauiEnableVisualAssemblyScanning |
로 설정 true 하면 .NET MAUI는 어셈블리에서 구현하는 IVisual 형식 및 특성에 대해 [assembly:Visual(...)] 검색하고 이러한 형식을 등록합니다. 기본적으로 이 빌드 속성은 전체 트리밍을 false 사용하는 경우로 설정됩니다. |
MauiShellSearchResultsRendererDisplayMemberNameSupported |
로 false 설정하면 값 SearchHandler.DisplayMemberName 이 무시됩니다. 대신 결과의 모양을 SearchHandler 정의하는 방법을 제공해야 ItemTemplate 합니다. 기본적으로 이 빌드 속성은 전체 트리밍 또는 네이티브 AOT를 사용할 때로 설정 false 됩니다. |
MauiQueryPropertyAttributeSupport |
로 false [QueryProperty(...)] 설정하면 탐색할 때 특성이 속성 값을 설정하는 데 사용되지 않습니다. 대신 쿼리 매개 변수를 IQueryAttributable 허용하는 인터페이스를 구현해야 합니다. 기본적으로 이 빌드 속성은 전체 트리밍 또는 네이티브 AOT를 사용할 때로 설정 false 됩니다. |
MauiImplicitCastOperatorsUsageViaReflectionSupport |
로 설정 false 하면 값을 한 형식에서 다른 형식으로 변환할 때 .NET MAUI에서 암시적 변환 연산자를 찾을 수 없습니다. 이는 형식이 다른 속성 간의 바인딩과 다른 형식의 값으로 바인딩 가능한 개체의 속성 값을 설정하는 데 영향을 줄 수 있습니다. 대신, 형식에 TypeConverter 대해 정의하고 특성을 사용하여 형식에 TypeConverterAttribute 연결해야 합니다. 기본적으로 이 빌드 속성은 전체 트리밍 또는 네이티브 AOT를 사용할 때로 설정 false 됩니다. |
_MauiBindingInterceptorsSupport |
로 설정 false 하면 .NET MAUI는 메서드에 대한 호출을 SetBinding 가로채지 않으며 컴파일을 시도하지 않습니다. 기본적으로 이 빌드 속성은 .로 설정됩니다 true . |
MauiEnableXamlCBindingWithSourceCompilation |
로 true 설정하면 .NET MAUI는 속성이 사용되는 바인딩을 포함하여 모든 바인딩을 Source 컴파일합니다. 이 기능을 사용하도록 설정하면 모든 바인딩이 컴파일되도록 올바른 x:DataType 바인딩이 있는지 확인하거나 바인딩을 컴파일하지 않아야 하는 경우 데이터 형식 x:Data={x:Null}} 을 지웁니다. 기본적으로 이 빌드 속성은 전체 트리밍 또는 네이티브 AOT를 사용할 때로 설정 true 됩니다. |
MauiHybridWebViewSupported |
설정 false 하면 컨트롤을 HybridWebView 사용할 수 없습니다. 기본적으로 이 빌드 속성은 전체 트리밍 또는 네이티브 AOT를 사용할 때로 설정 false 됩니다. |
이러한 MSBuild 속성에는 동일한 AppContext 스위치도 있습니다.
MauiEnableVisualAssemblyScanning
MSBuild 속성에는 이름이 같은 AppContext 스위치가 있습니다Microsoft.Maui.RuntimeFeature.IsIVisualAssemblyScanningEnabled
.MauiShellSearchResultsRendererDisplayMemberNameSupported
MSBuild 속성에는 이름이 같은 AppContext 스위치가 있습니다Microsoft.Maui.RuntimeFeature.IsShellSearchResultsRendererDisplayMemberNameSupported
.MauiQueryPropertyAttributeSupport
MSBuild 속성에는 이름이 같은 AppContext 스위치가 있습니다Microsoft.Maui.RuntimeFeature.IsQueryPropertyAttributeSupported
.MauiImplicitCastOperatorsUsageViaReflectionSupport
MSBuild 속성에는 이름이 같은 AppContext 스위치가 있습니다Microsoft.Maui.RuntimeFeature.IsImplicitCastOperatorsUsageViaReflectionSupported
._MauiBindingInterceptorsSupport
MSBuild 속성에는 이름이 같은 AppContext 스위치가 있습니다Microsoft.Maui.RuntimeFeature.AreBindingInterceptorsSupported
.MauiEnableXamlCBindingWithSourceCompilation
MSBuild 속성에는 이름이 같은 AppContext 스위치가 있습니다Microsoft.Maui.RuntimeFeature.MauiEnableXamlCBindingWithSourceCompilationEnabled
.MauiHybridWebViewSupported
MSBuild 속성에는 이름이 같은 AppContext 스위치가 있습니다Microsoft.Maui.RuntimeFeature.IsHybridWebViewSupported
.
기능 스위치를 사용하는 가장 쉬운 방법은 해당 MSBuild 속성을 앱의 프로젝트 파일(*.csproj)에 배치하여 관련 코드를 .NET MAUI 어셈블리에서 트리밍하는 것입니다.
코드 유지
트리머를 사용하면 간접적으로도 동적으로 호출했을 수 있는 코드가 제거되는 경우가 있습니다. 특성에 주석을 추가하여 멤버를 유지하도록 트리머에 DynamicDependency
지시할 수 있습니다. 이 특성은 멤버의 형식 및 하위 집합 또는 특정 멤버에 대한 종속성을 표현하는 데 사용할 수 있습니다.
Important
앱에서 사용하도록 정적으로 확인할 수 없는 BCL의 모든 멤버는 제거될 수 있습니다.
DynamicDependency
생성자, 필드 및 메서드에 특성을 적용할 수 있습니다.
[DynamicDependency("Helper", "MyType", "MyAssembly")]
static void RunHelper()
{
var helper = Assembly.Load("MyAssembly").GetType("MyType").GetMethod("Helper");
helper.Invoke(null, null);
}
이 예제에서는 DynamicDependency
메서드가 유지되는지 확인합니다 Helper
. 특성이 없으면 트리밍이 Helper
MyAssembly
제거되거나 다른 곳에서 참조되지 않는 경우 완전히 제거 MyAssembly
됩니다.
특성은 특성을 통해 또는 특성을 통해 유지할 멤버를 string
DynamicallyAccessedMembers
지정합니다. 형식과 어셈블리는 특성 컨텍스트에서 암시적이거나 특성에서 명시적으로 지정됩니다(형식 및 어셈블리 이름에 대해 Type
또는 string
을 사용).
형식 및 멤버 문자열은 멤버 접두사 없이 C# 설명서 설명 ID 문자열 형식의 변형을 사용합니다. 멤버 문자열은 선언 형식의 이름을 포함하지 않아야 하며 지정된 이름의 모든 멤버를 유지하기 위해 매개 변수를 생략할 수 있습니다. 다음 예제에서는 유효한 용도를 보여 줍니다.
[DynamicDependency("Method()")]
[DynamicDependency("Method(System,Boolean,System.String)")]
[DynamicDependency("MethodOnDifferentType()", typeof(ContainingType))]
[DynamicDependency("MemberName")]
[DynamicDependency("MemberOnUnreferencedAssembly", "ContainingType", "UnreferencedAssembly")]
[DynamicDependency("MemberName", "Namespace.ContainingType.NestedType", "Assembly")]
// generics
[DynamicDependency("GenericMethodName``1")]
[DynamicDependency("GenericMethod``2(``0,``1)")]
[DynamicDependency("MethodWithGenericParameterTypes(System.Collections.Generic.List{System.String})")]
[DynamicDependency("MethodOnGenericType(`0)", "GenericType`1", "UnreferencedAssembly")]
[DynamicDependency("MethodOnGenericType(`0)", typeof(GenericType<>))]
어셈블리 유지
트리밍 프로세스에서 제외해야 하는 어셈블리를 지정하는 동시에 다른 어셈블리를 트리밍할 수 있습니다. 이 방법은 특성을 쉽게 사용할 DynamicDependency
수 없거나 트리밍되는 코드를 제어하지 않는 경우에 유용할 수 있습니다.
모든 어셈블리를 트리밍할 때 프로젝트 파일에서 MSBuild 항목을 설정 TrimmerRootAssembly
하여 어셈블리를 건너뛰도록 트리머에게 지시할 수 있습니다.
<ItemGroup>
<TrimmerRootAssembly Include="MyAssembly" />
</ItemGroup>
참고 항목
.dll
MSBuild 속성을 설정할 때 확장이 TrimmerRootAssembly
필요하지 않습니다.
트리머가 어셈블리를 건너뛰면 루트로 간주됩니다. 즉, 해당 어셈블리와 정적으로 이해된 모든 종속성이 유지됩니다. MSBuild 속성을 더 추가하여 TrimmerRootAssembly
추가 어셈블리를 <ItemGroup>
건너뛸 수 있습니다.
어셈블리, 형식 및 멤버 유지
보존해야 하는 어셈블리, 형식 및 멤버를 지정하는 XML 설명 파일을 트리머에 전달할 수 있습니다.
모든 어셈블리를 트리밍할 때 트리밍 프로세스에서 멤버를 제외하려면 프로젝트 파일의 TrimmerRootDescriptor
MSBuild 항목을 제외할 멤버를 정의하는 XML 파일로 설정합니다.
<ItemGroup>
<TrimmerRootDescriptor Include="MyRoots.xml" />
</ItemGroup>
그런 다음 XML 파일은 트리머 설명자 형식 을 사용하여 제외할 멤버를 정의합니다.
<linker>
<assembly fullname="MyAssembly">
<type fullname="MyAssembly.MyClass">
<method name="DynamicallyAccessedMethod" />
</type>
</assembly>
</linker>
이 예제에서 XML 파일은 트리밍에서 제외되는 앱에서 동적으로 액세스하는 메서드를 지정합니다.
어셈블리, 형식 또는 멤버가 XML에 나열되면 기본 동작은 유지입니다. 즉, 트리머가 사용한다고 생각하는지 여부에 관계없이 출력에 유지됩니다.
참고 항목
보존 태그는 모호하게 포괄합니다. 다음 수준의 세부 정보를 제공하지 않으면 모든 자식이 포함됩니다. 어셈블리가 형식 없이 나열되면 어셈블리의 모든 형식과 멤버가 유지됩니다.
어셈블리를 트리밍 안전으로 표시
프로젝트에 라이브러리가 있거나 재사용 가능한 라이브러리의 개발자이고 트리머가 어셈블리를 트리밍 가능한 것으로 처리하려는 경우 어셈블리의 프로젝트 파일에 MSBuild 속성을 추가하여 IsTrimmable
어셈블리를 트리밍 안전으로 표시할 수 있습니다.
<PropertyGroup>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
이렇게 하면 어셈블리가 "트리밍 가능"으로 표시되고 해당 프로젝트에 대한 트리밍 경고가 활성화됩니다. "트리밍 가능"이면 어셈블리가 트리밍과 호환되는 것으로 간주되며 어셈블리가 빌드될 때 트리밍 경고가 없어야 합니다. 트리밍된 앱에서 사용되는 경우 어셈블리의 사용되지 않은 멤버는 최종 출력에서 제거됩니다.
IsTrimmable
MSBuild 속성을 true
프로젝트 파일로 설정하면 어셈블리에 특성이 AssemblyMetadata
삽입됩니다.
[assembly: AssemblyMetadata("IsTrimmable", "True")]
또는 MSBuild 속성을 어셈블리의 AssemblyMetadata
프로젝트 파일에 추가하지 않고 어셈블리에 특성을 추가할 IsTrimmable
수 있습니다.
참고 항목
어셈블리에 IsTrimmable
대해 MSBuild 속성이 설정된 경우 특성을 재정의 AssemblyMetadata("IsTrimmable", "True")
합니다. 이렇게 하면 특성이 없는 경우에도 어셈블리를 트리밍으로 선택하거나 특성이 있는 어셈블리의 트리밍을 사용하지 않도록 설정할 수 있습니다.
분석 경고 표시 안 함
트리머를 사용하도록 설정하면 정적으로 연결할 수 없는 IL이 제거됩니다. 리플렉션 또는 동적 종속성을 만드는 다른 패턴을 사용하는 앱은 결과적으로 손상될 수 있습니다. 이러한 패턴에 대해 경고하려면 어셈블리를 트리밍 안전으로 표시할 때 라이브러리 작성자는 MSBuild 속성을 다음으로 false
설정 SuppressTrimAnalysisWarnings
해야 합니다.
<PropertyGroup>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>
트리밍 분석 경고를 표시하지 않으면 사용자 고유의 코드, 라이브러리 코드 및 SDK 코드를 포함하여 전체 앱에 대한 경고가 포함됩니다.
자세한 경고 표시
트리밍 분석은 어셈블리의 내부가 트리밍과 호환되지 않음을 나타내는 각 어셈블리에 PackageReference
대해 최대 하나의 경고를 생성합니다. 라이브러리 작성자로서 어셈블리를 트리밍 안전으로 표시할 때 MSBuild 속성을 false
다음으로 설정하여 모든 어셈블리에 대해 개별 경고를 사용하도록 설정 TrimmerSingleWarn
해야 합니다.
<PropertyGroup>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>
이 설정은 어셈블리당 단일 경고로 축소하는 대신 모든 자세한 경고를 표시합니다.
참고 항목
.NET MAUI