# CoreBus와 Native AOT: 크로스플랫폼 터미널 컴파일 실전 경험
.NET 애플리케이션을 Native AOT로 컴파일하면 시작 시간이 빨라지고 배포 크기가 작아지는 장점이 있지만, 실제로는 여러 기술적 제약이 따릅니다. 이 글에서는 COM 포트와 Modbus 프로토콜 작업을 위한 크로스플랫폼 터미널인 CoreBus에 Native AOT를 통합한 실제 사례를 살펴봅니다.
Native AOT를 사용하는 이유?
전환의 주요 동기는 저사양 PC에서의 느린 콜드 스타트와 큰 설치 패키지 크기였습니다. WPF에서 Avalonia UI로 마이그레이션한 후 앱 크기가 ~60 MB 줄었지만, 10 MB 목표에는 여전히 미치지 못했습니다. 자원이 제한된 산업 장비와 임베디드 시스템을 타깃으로 하는 앱이므로 Native AOT가 논리적인 다음 단계처럼 보였습니다.
하지만 빌드 단계부터 Native AOT 지원을 위해 바인딩 아키텍처, 직렬화, 배포 전략을 대대적으로 개편해야 한다는 사실이 드러났습니다.
문제 1: Avalonia의 컴파일된 바인딩
Avalonia는 기본적으로 리플렉션을 통한 동적 바인딩을 사용하므로 Native AOT와 호환되지 않습니다. 해결책은 사전 컴파일된 바인딩을 활성화하는 것입니다:
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
이로 인해 모든 XAML 파일의 루트 요소(Window, UserControl)와 DataTemplate 내부에서 x:DataType 속성을 사용해 데이터 타입을 명시적으로 지정해야 합니다. 이 속성을 생략하면 컴파일 오류(Dynamic code generation is not supported on this platform) 또는 실행 직후 크래시가 발생합니다.
CoreBus 프로젝트에서는 모든 XAML 마크업을 업데이트해야 했으며, 상당한 시간이 소요되었지만 런타임 예외는 완전히 사라졌습니다.
문제 2: 리플렉션 없는 직렬화
CoreBus의 설정과 프리셋은 System.Text.Json을 사용해 JSON 파일에 저장됩니다. 표준 직렬화 메서드는 RequiresUnreferencedCode와 RequiresDynamicCode 속성을 가지며, Native AOT 빌드 시 IL2026 및 IL3050 경고를 발생시킵니다.
해결책은 JsonSerializerContext를 통한 소스 생성 JSON 직렬화기를 사용하는 것입니다. 올바른 구현 예시는 다음과 같습니다:
var options = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
TypeInfoResolver = SerializerContext.Default
};
using var stream = new FileStream(correctFilePath, FileMode.Open);
var jsonTypeInfo = options.TypeInfoResolver.GetTypeInfo(typeof(T), options);
if (jsonTypeInfo == null)
{
throw new Exception($"Not succeeded nayti type {typeof(T)} in obyavlenii konteksta serializatsii.");
}
JsonSerializer.Serialize(stream, data, jsonTypeInfo);
역직렬화도 유사한 방식으로 수행합니다:
var data = (T?)JsonSerializer.Deserialize(stream, typeof(T), SerializerContext.Default);
이 방법은 리플렉션을 완전히 피하고 트리밍 및 AOT와의 호환성을 보장합니다.
문제 3: 크로스플랫폼 빌드
Native AOT는 대상 OS와 아키텍처에 대한 네이티브 컴파일을 요구합니다. Linux 커널 버전과 glibc가 호환성에 핵심적입니다:
- Ubuntu 24.10(커널 6.11) 빌드는 Astra Linux CE(커널 5.15)에서 실행되지 않았습니다.
- Ubuntu 18.04(glibc 2.27) 빌드는 Astra Linux CE(glibc 2.24)의 C 라이브러리 후방 호환성 부족으로 실패했습니다.
최종 전략:
- Windows: Windows 11에서 빌드, Windows 7/10에서 테스트—호환성 성공.
- Linux: 커널 5.4와 glibc 2.24를 사용하는 Astra Linux CE에서 직접 빌드.
이 접근은 이식성을 극대화하지만 CI/CD 프로세스를 복잡하게 만듭니다.
문제 4: 트리밍과 종속 어셈블리
트리밍(미사용 코드 제거)은 메인 실행 파일 프로젝트에서 가장 큰 절감 효과를 발휘합니다. 보조 라이브러리에서는 총 300 KB 미만의 절감만 가능합니다.
그러나 공격적인 트리밍은 Avalonia 리소스, 특히 스타일을 제거할 수 있습니다. 테마를 트리밍에서 명시적으로 제외하는 것이 권장됩니다:
<ItemGroup>
<TrimmerRootAssembly Include="Avalonia.Themes.Fluent" />
</ItemGroup>
Avalonia 11.3.x에서는 불필요할 수 있지만, 안전을 위해 설정을 유지하는 것이 좋습니다.
문제 5: 백신 소프트웨어의 오탐지
Native AOT + 트리밍으로 빌드된 앱은 종종 악성으로 플래그됩니다. Windows Defender는 설치 프로그램을 Trojan:Win32/Bearfoos.B!ml, 포터블 버전을 Trojan:Script/Wacatac.B!ml로 분류했습니다.
이는 바이너리 구조 변경과 디지털 서명 부재로 인한 알려진 문제입니다. 해결책은 Microsoft SmartScreen 검증 통과와 디지털 서명 추가입니다.
주요 교훈
- Native AOT는 리플렉션과 동적 코드 생성을 완전히 배제해야 합니다.
- 컴파일은 대상 플랫폼에서 수행하며, 커널과 시스템 라이브러리 버전을 고려해야 합니다.
- JSON 처리를 위해 소스 생성 직렬화가 필수입니다.
- 트리밍은 UI 리소스를 손상시킬 수 있으므로 핵심 어셈블리를 수동으로 제외하세요.
- 서명되지 않은 AOT 바이너리는 백신 도구에서 자주 오탐지됩니다.
— Editorial Team
아직 댓글이 없습니다.