이전 글 C# AOT 라이브러리 활용에 이어 이번 글에서는 로그 시스템으로 많이 사용하는 NLog를 AOT(Ahead-of-Time)로 빌드하여 라이브러리로 사용하는 법을 알아본다.
실행파일과 같은 폴더에 AppLogs 폴더를 만들고 여기에 연월/날짜/로그타입별파일로 기록하고 디버그 모드로 빌드 후 실행 했을때 DebugViewPP로 실시간 로그를 볼 수 있도록 하였다.
기본 패키지의 리플랙션(Reflection)1을 피해서 작성해야 하므로 기본 사용법과 차이가 있을 수 있다. 참고로 Avalonia+SukiUi+MVVM로 구성된 프로젝트를 AOT로 빌드할 수 있도록 주요 패키지를 하나씩 옮겨보려고 한다.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<IsAotCompatible>true</IsAotCompatible>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OptimizationPreference>Speed</OptimizationPreference>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="6.1.1"/>
<PackageReference Include="NLog.OutputDebugString" Version="6.1.1"/>
</ItemGroup>
<Target Name="CopyLogHelperDebug" AfterTargets="Publish">
<Copy Condition="'$(Configuration)' == 'Debug'"
SourceFiles="$(PublishDir)LogHelper.dll"
DestinationFiles="$(PublishDir)LogHelper.Debug.dll"
SkipUnchangedFiles="true"/>
<Copy Condition="'$(Configuration)' == 'Release'"
SourceFiles="$(PublishDir)LogHelper.dll"
DestinationFiles="$(PublishDir)LogHelper.Release.dll"
SkipUnchangedFiles="true"/>
</Target>
</Project>
C#은 관리형 언어(Managed Language)이다. 개발자가 메모리 할당과 해제를 하는 것을 CLR(런타임 환경)이 이를 대신해 주고 가비지 컬렉션(Garbage Collection)으로 사용하지 않는 메모리를 시스템이 자동으로 회수하여 메모리 누수를 방지한다.
대신에 빌드하면 소스코드가 바로 기계어로 바뀌는 것이 아니라 중간 언어(IL, Intermediate Language)로 컴파일된 실행 시점에 JIT(Just-In-Time)에 의해 기계어로 번역된다. 그래서 파일(exe, dll)을 ILSpy 같은 툴로 보면 소스코드가 훤히 보여 중요한 문자열을 숨기지 못하는 단점이 있다.
C#의 AOT(Ahead-of-Time) 컴파일은 애플리케이션을 실행하기 전(빌드 시점)에 코드를 해당 운영체제와 CPU가 이해할 수 있는 네이티브 기계어로 미리 번역해 두는 기술이다. 이것 때문에 이를 활용하면 ILSpy툴로 내용(소스)을 볼 수 없다. 다만 문자열은 헥사에니터 같은것으로 충분히 볼 수 있기 때문에 이를 보완해야 한다.
C++ constexpr, zig comptime, rust의 매크로함수인 obfstr!함수를 이용하여 컴파일 타임에 문자열을 난독화한다. 다만 여기에서는 AOT만을 이용하여 기본을 학습한 후 이것을 토대로 zig, rust, c++를 라이브러리로 만들어 c#에서 활용해 보려고 한다.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<IsAotCompatible>true</IsAotCompatible>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
Rust는 dotnet(C#)에 비해 성능이 우수하다고 알려져 있다.1 대표적인 Rust의 Web Frameworks에는 대표적으로 Actix Web, Rocket, Warp, Axum, Poem 등이 있는데 각각의 특징을 간략하게 예제(Axum)와 더불어 정리하였다.
| 프레임워크 | 언어 | 요청/초 | 지연시간(ms) | CPU 사용률 |
|---|---|---|---|---|
| Actix-web (Rust) | Rust | 748,051 | 1.7 | 98% |
| Axum (Rust) | Rust | 723,892 | 1.8 | 97% |
| ASP.NET Core | C# | 692,345 | 2.1 | 95% |
| Spring Boot | Java | 153,846 | 6.5 | 92% |
| Express.js | Node.js | 58,824 | 17.0 | 85% |
dotNET의 경우 Rust에 비해 떨어지지 않는 성능을 보여준다.