.NET Native AOT를 통해 정적 라이브러리(.lib 또는 .a) 형태로 바이너리를 빌드할 경우, 일반적인 Native AOT의 제약 사항뿐만 아니라 링킹(Linking)과 관련된 치명적인 제한 사항이 존재합니다.

주요 제한 사항은 다음과 같습니다.

1. 단일 정적 라이브러리 링킹 제한 (가장 중요한 제약)

  • 1개만 사용 가능: 한 애플리케이션 내에 .NET Native AOT로 만든 정적 라이브러리는 단 하나만 링킹할 수 있습니다.
  • 심볼 충돌: 이는 정적 라이브러리가 각자 독립적인 가비지 컬렉터(GC), AppDomain, 런타임 심볼 사본을 포함하고 있기 때문입니다. 여러 개의 닷넷 정적 라이브러리를 하나로 합치려 하면 이러한 내부 심볼들이 서로 겹쳐 충돌이 발생합니다.
  • 다이아몬드 의존성 문제: 이를 내장 런타임의 다이아몬드 의존성 문제라고 하며, Go나 Rust 같은 언어에서도 동일하게 발생하는 현상입니다. 다만, 닷넷이 아닌 다른 방식으로 만들어진 정적 라이브러리와 섞어서 사용하는 것은 문제가 없습니다.

2. 런타임 및 리플렉션 제약

  • 동적 코드 생성 불가: System.Reflection.Emit과 같은 런타임 코드 생성을 절대 지원하지 않습니다.
  • 동적 로딩 불가: Assembly.LoadFile과 같은 방식을 통한 런타임 어셈블리 동적 로드가 불가능합니다.
  • 리플렉션 제한: 문자열 기반의 동적 리플렉션 사용 시 트리밍 과정에서 필요한 메타데이터가 삭제되어 오류가 발생할 수 있습니다.

3. 제네릭 및 메모리 관련 제약

  • 제네릭 인스턴스화: 정적으로 컴파일되므로 모든 제네릭 조합이 빌드 시점에 생성되어야 합니다. 특히 구조체(struct) 타입 인자를 사용하는 제네릭은 각 인스턴스마다 별도의 코드가 생성되어 디스크 크기가 크게 증가할 수 있습니다.
  • 개방형 제네릭: 실행 중에 MakeGenericType을 사용하여 새로운 제네릭 형상을 만드는 패턴은 AOT 컴파일러가 예측할 수 없어 실패하기 쉽습니다.

4. 기타 기능적 제한

  • LINQ 식 트리: System.Linq.Expressions는 런타임에 컴파일된 코드 대신 항상 인터프리터 형태로 실행되므로 성능이 더 느려질 수 있습니다.
  • Windows 전용 제약: Windows 환경에서는 기본 내장 COM 기능을 지원하지 않습니다.
  • C++/CLI 미지원: Native AOT 배포 모델에서는 C++/CLI를 사용할 수 없습니다.

요약하자면: 정적 라이브러리 형태의 Native AOT는 **"하나의 앱에 하나의 런타임만 존재해야 한다"**는 원칙 때문에 여러 개의 닷넷 정적 라이브러리를 동시에 사용하는 것이 불가능합니다. 따라서 여러 기능을 분리하고 싶다면 정적 라이브러리보다는 동적 라이브러리(DLL/SO) 형식을 고려해야 할 수도 있습니다.

이 상황은 마치 한 배에 두 명의 선장(런타임/GC)이 탈 수 없는 것과 같습니다. 각각의 정적 라이브러리가 배를 조종하는 자신만의 선장을 데리고 오기 때문에, 이를 하나로 묶으려 하면 배의 지휘권(심볼)이 충돌하여 배가 제대로 항해할 수 없게 되는 원리입니다.

Posted by gurupia
,