1. 서론: 모놀리식 데스크톱 런처의 한계와 서드파티 플러그인 아키텍처의 필요성
72종의 시스템 도구와 온디바이스 AI 서비스를 탑재한 ZeliDesk는 공교육과 국방 행정 현장에서 전례 없는 생산성 혁신을 이끌어냈습니다. 그러나 전국 초·중·고 정보화 교사와 대학교 실습실, 공공 기관 전산 담당자들은 저마다 자신들의 기관 특성에 맞춘 고유한 도구(예: 학내 서버 전용 관리기, 학교 급식 식단 파서, 군 전술 통신 모니터 등)를 런처에 직접 탑재하기를 원했습니다.
모든 요구사항을 본체 소스코드에 직접 병합하는 방식은 프로젝트 크기를 비대하게 만들고, 회귀 버그의 위험을 높입니다. ZeliDesk 엔지니어링 팀은 본체 재컴파일이나 재배포 없이, 개발자가 빌드한 단 하나의 C# DLL 파일을 %LOCALAPPDATA%\ZeliDesk\plugins\ 폴더에 복사하는 것만으로 메인 화면에 새로운 타일과 윈도우가 즉시 활성화되는 서드파티 플러그인 SDK(ZeliDesk.Sdk)를 정립했습니다.
2. 계약 어셈블리(ZeliDesk.Sdk): 최소 침습 인터페이스 IZeliUtility 설계
플러그인 시스템의 성공 여부는 계약(Contract) 인터페이스의 간결성에 달려 있습니다. ZeliDesk.Sdk는 본체의 무거운 비즈니스 로직에 전혀 의존하지 않는 순수 경량 어셈블리로 분리되었으며, 단 하나의 인터페이스 IZeliUtility를 정의합니다:
namespace ZeliDesk.Sdk
{
public interface IZeliUtility
{
string Id { get; } // 고유 식별자 (소문자 영숫자)
string Icon { get; } // 런처 타일 표시 이모지 (예: 🔌)
string ShortName { get; } // 타일 제목 (예: "Sample Plugin")
// 다국어 설명 (ko/en/zh 동적 라우팅)
string GetDescription(string lang);
// 런처 타일 클릭 시 실제 윈도우를 생성하여 반환
Microsoft.UI.Xaml.Window CreateWindow();
}
}
플러그인 제작 규칙은 명료합니다. 플러그인 DLL 스캔 단계에서는 메타데이터(Id, Icon, ShortName)만 읽기 위해 인스턴스화가 수행되므로, 클래스 생성자에서는 절대 무거운 작업이나 UI 스레드 접근을 수행해서는 안 되며, 실제 WinUI 3 UI 구성은 CreateWindow()가 호출되는 순간에만 실행되어야 합니다.
3. Default AssemblyLoadContext 기반 동적 프로빙: InvalidCastException 극복
.NET Core 이후 도입된 AssemblyLoadContext(ALC)는 DLL 격리에 탁월하지만, 외부 플러그인을 별도의 격리 ALC에 로드할 경우 심각한 타입 동일성 불일치(Type Identity Mismatch) 문제가 발생합니다.
플러그인이 참조하는 ZeliDesk.Sdk.IZeliUtility와 호스트(본체)가 참조하는 ZeliDesk.Sdk.IZeliUtility가 서로 다른 ALC에서 로드되면, CLR 런타임은 두 타입을 완전히 다른 타입으로 인식하여 (IZeliUtility)Activator.CreateInstance(type) 캐스팅 시 InvalidCastException을 발생시킵니다.
ZeliDesk의 PluginLoader는 이를 극복하기 위해 플러그인을 AssemblyLoadContext.Default에 명시적으로 로드하여, 호스트에 이미 적재된 ZeliDesk.Sdk.dll 바이너리를 완벽하게 공유하도록 설계되었습니다:
var asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(dll);
foreach (var type in asm.GetTypes())
{
if (type.IsAbstract || !typeof(IZeliUtility).IsAssignableFrom(type))
continue;
// Default ALC 공유로 인한 100% 안전한 인터페이스 캐스팅 성립!
var utility = (IZeliUtility)Activator.CreateInstance(type);
string id = utility.Id?.Trim().ToLowerInvariant();
Plugins[id] = utility;
}
4. 코드 전용(Code-Only) WinUI 3 UI 구성: IXamlMetadataProvider 제약 우회
WinUI 3에서 외부 어셈블리의 .xaml 파일을 로드하려면 호스트 애플리케이션의 IXamlMetadataProvider 체인에 외부 어셈블리의 메타데이터 공급자를 수동으로 바인딩해야 하는 복잡한 배선 작업이 요구됩니다.
ZeliDesk SDK는 이 복잡성을 완전히 걷어내고 C# 순수 코드 기반(Code-Only UI) 윈도우 구성을 표준으로 채택했습니다:
public Window CreateWindow()
{
var window = new Window { Title = "Sample Plugin" };
var panel = new StackPanel
{
Spacing = 16,
Padding = new Thickness(32),
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
};
panel.Children.Add(new TextBlock { Text = "🔌", FontSize = 48, HorizontalAlignment = HorizontalAlignment.Center });
panel.Children.Add(new TextBlock { Text = "ZeliDesk 플러그인 PoC", FontSize = 22, FontWeight = FontWeights.Bold });
window.Content = panel;
return window;
}
XAML 컴파일러(XamlTypeInfo)의 제약이 없으므로, 플러그인 개발자는 Visual Studio나 dotnet new classlib CLI 환경에서 5분 만에 완벽하게 동작하는 독립 윈도우 유틸리티를 빌드할 수 있습니다.
5. 하위 폴더 배치와 의존성 리졸버(AssemblyResolve) 훅 메커니즘
실제 서드파티 플러그인은 단일 DLL뿐만 아니라 외부 NuGet 패키지(예: Newtonsoft.Json, CsvHelper 등)를 동반하는 경우가 많습니다. 여러 플러그인이 서로 다른 버전의 의존 DLL을 가질 때 루트 폴더에 DLL들이 섞이면 버전 충돌(DLL Hell)이 발생합니다.
ZeliDesk는 하위 폴더 배치(Subdirectory Deployment)를 기본 지원합니다:
plugins\MyTool.dll(단일 독립 DLL 형태)plugins\MyComplexTool\MyComplexTool.dll+Dependency.dll(패키지 형태)
PluginLoader는 하위 폴더들을 탐색 디렉터리 목록(s_probeDirs)에 등록하고, AssemblyLoadContext.Default.Resolving 이벤트를 후킹하여, 플러그인 실행 도중 의존 어셈블리가 필요할 때 해당 플러그인의 전용 폴더에서 바이너리를 우선적으로 찾아주는 지능형 리졸버 훅을 가동합니다.
6. 메인 런처 동적 타일 주입 및 UtilityGuide(📖) 카탈로그 자동 연동
PluginLoader.LoadAll()에 의해 발견된 서드파티 플러그인들은 MainWindow 부팅 시 메인 대시보드와 유틸리티 안내서에 즉각 주입됩니다:
-
메인 런처 타일: "플러그인(Plugins)" 카테고리에 새로운 직사각형 글라스모피즘 타일이 자동 생성되며, 클릭 시
CreateWindow().Activate()가 호출됩니다. - 유틸리티 가이드(📖): 72종 공식 도구 목록 바로 뒤에 플러그인 카탈로그가 추가되어 설명과 버전을 열람할 수 있습니다.
-
플러그인 폴더 바로가기 (🔌 버튼): 가이드 창 우상단의 플러그인 버튼을 누르면
%LOCALAPPDATA%\ZeliDesk\plugins폴더가 Windows 탐색기로 즉시 열려 손쉬운 추가/삭제가 가능합니다.
7. 결론: 산학 협력과 정보 교육 현장을 위한 오픈 데스크톱 생태계의 완성
ZeliDesk.Sdk와 PluginLoader 아키텍처는 폐쇄적인 상용 소프트웨어의 울타리를 넘어, 사용자와 교원, 학생이 스스로 필요한 도구를 직접 만들어 생태계에 기여할 수 있는 진정한 오픈 확장성을 완성했습니다.
초·중·고 정보 수업에서 학생들은 자신이 직접 C#으로 작성한 알고리즘 유틸리티가 ZeliDesk 런처의 정식 타일로 등록되어 작동하는 모습을 보며 컴퓨터 과학의 살아있는 즐거움을 체득할 수 있습니다. ZeliDesk 팀은 앞으로도 공교육과 국방 기술 현장의 창의적 소프트웨어 개발을 지속적으로 지원할 것입니다.