서비스, 확장 및 패키지로 앱 확장

Windows 앱이 다른 앱에 기능을 제공하거나 타사 추가 기능을 사용할 수 있는 몇 가지 기술을 제공합니다. 이 문서에서는 Windows 앱 SDK 데스크톱 앱에 사용할 수 있는 확장성 옵션을 비교합니다.

확장성 옵션 개요

기술 Description 패키지 ID 필요 최소 OS
App Services 를 통해 앱 간의 요청/응답 통신 AppServiceConnection Yes Windows 10 1607
앱 확장 플러그 인 모델 - 호스트 앱이 확장 패키지에서 콘텐츠를 검색합니다. Yes Windows 10 1607
패키지 확장 uap17:PackageExtension를 통한 더 광범위한 패키지 수준 확장성 Yes 윈도우 11
선택적 패키지 기본 앱을 보완하는 추가 콘텐츠 패키지 Yes Windows 10 1709
리소스 패키지 시장별로 구분된 언어, 배율 및 접근성 리소스 Yes 윈도우 10

올바른 기술 선택

앱 서비스를 사용하는 경우

  • 별도의 앱 간에 양방향 통신이 필요합니다.
  • 소비자 앱은 요청을 보내고 응답을 기다립니다.
  • API와 유사한 인터페이스를 다른 앱에 노출하려고 합니다.

예: 다른 앱에서 텍스트를 번역하기 위해 호출할 수 있는 번역 서비스입니다.

다음과 같은 경우 앱 확장을 사용하십시오

  • 앱에는 타사에서 콘텐츠, 테마 또는 추가 기능을 제공하는 플러그 인 모델이 필요합니다.
  • 확장은 설치된 패키지에서 런타임에 검색됩니다.
  • 확장은 실행 코드가 아닌 데이터 또는 구성을 제공합니다(코드 실행은 앱 서비스를 사용해야 합니다).

예: 설치된 확장 패키지에서 필터 팩을 검색하는 이미지 편집기입니다.

패키지 확장을 사용하는 경우

  • Windows 11 더 광범위한 패키지 수준 확장성이 필요합니다.
  • 확장은 모델이 허용하는 것보다 더 많은 패키지 콘텐츠에 PublicFolder 액세스해야 합니다.

선택적 패키지를 사용할 때

  • 별도의 패키지로 배포된 추가 콘텐츠(DLC, 프리미엄 기능)가 있습니다.
  • 콘텐츠는 동일한 게시자가 작성합니다.

아키텍처 패턴

확장 검색 기능이 있는 App Service

전체 플러그 인 아키텍처에 대한 앱 확장과 앱 서비스를 결합합니다.

  1. 호스트 앱은 설치된 확장을 검색하는 데 사용합니다 AppExtensionCatalog .
  2. 각 확장은 해당 기능을 설명하는 속성을 선언합니다.
  3. 사용자가 확장을 활성화하면 호스트 앱이 양방향 통신을 위해 확장의 앱 서비스에 연결됩니다.
┌─────────────────┐      ┌──────────────────┐
│   Host app       │      │  Extension app    │
│                  │      │                   │
│ AppExtension     │◄────►│ AppExtension      │
│   Catalog        │      │   declaration     │
│                  │      │                   │
│ AppService       │◄────►│ AppService        │
│   Connection     │      │   provider        │
└─────────────────┘      └──────────────────┘

콘텐츠 확장만

확장이 정적 콘텐츠(테마, 템플릿, 데이터 파일)를 제공하는 간단한 시나리오의 경우:

  1. 호스트 앱은 AppExtensionCatalog를 통해 확장을 검색합니다.
  2. 확장 프로그램의 PublicFolder에서 파일을 읽습니다.
  3. 앱 서비스가 필요하지 않습니다.

UWP 확장성과의 차이점

여기에 설명된 확장성 기술은 UWP에서와 동일한 방식으로 Windows 앱 SDK 데스크톱 앱에서 작동하며 한 가지 요구 사항인 MSIX 패키지 ID를 사용합니다. 모든 확장성 기능은 선언에 대한 패키지 매니페스트와 검색을 위한 패키지 카탈로그를 사용합니다.

데스크톱 앱이 패키지되지 않은 경우 이러한 확장성 기술을 사용할 수 없습니다. 다음과 같은 다른 방법을 고려합니다.

  • COM 기반 플러그 인 인터페이스
  • 파일 시스템 기반 확장 프로그램 검색
  • 명명된 파이프 또는 기타 IPC 메커니즘

패키지되지 않은 앱에 대한 파일 기반 플러그 인 검색

패키지되지 않은 WinUI 3 앱의 경우 .NET의 AssemblyLoadContext를 사용하여 지정된 폴더에서 확장을 로드하는 플러그인 시스템을 구현할 수 있습니다.

public class PluginLoader
{
    private readonly string _pluginDirectory;

    public PluginLoader(string pluginDirectory)
    {
        _pluginDirectory = pluginDirectory;
    }

    public IEnumerable<T> LoadPlugins<T>() where T : class
    {
        if (!Directory.Exists(_pluginDirectory))
            yield break;

        foreach (var dll in Directory.GetFiles(_pluginDirectory, "*.dll"))
        {
            var context = new PluginLoadContext(dll);
            var assembly = context.LoadFromAssemblyPath(Path.GetFullPath(dll));

            foreach (var type in assembly.GetTypes()
                .Where(t => typeof(T).IsAssignableFrom(t) && !t.IsAbstract))
            {
                if (Activator.CreateInstance(type) is T plugin)
                    yield return plugin;
            }
        }
    }
}

// Custom AssemblyLoadContext to isolate plugin dependencies
public class PluginLoadContext : AssemblyLoadContext
{
    private readonly AssemblyDependencyResolver _resolver;

    public PluginLoadContext(string pluginPath) : base(isCollectible: true)
    {
        _resolver = new AssemblyDependencyResolver(pluginPath);
    }

    protected override Assembly? Load(AssemblyName assemblyName)
    {
        var path = _resolver.ResolveAssemblyToPath(assemblyName);
        return path != null ? LoadFromAssemblyPath(path) : null;
    }
}

Warning

유효성 검사 없이 디스크에서 어셈블리를 로드하는 것은 보안 위험입니다. 프로덕션 환경에서 로드하기 전에 어셈블리 서명(예: Authenticode)을 확인하고, 플러그 인 디렉터리의 ACL 권한을 제한하고, 권한이 감소된 별도의 프로세스에서 플러그 인을 실행하는 것이 좋습니다.

호스트와 플러그 인이 참조하는 별도의 어셈블리에서 공유 인터페이스 계약을 정의합니다.

// Contoso.App.Contracts (shared assembly)
public interface IPluginExtension
{
    string Name { get; }
    string Description { get; }
    void Execute(IServiceProvider services);
}

비고

AssemblyLoadContext에서 isCollectible: true를 사용하면 런타임에 플러그인을 언로드할 수 있습니다. 이 방법은 데스크톱 앱에서 (관리되는 확장성 프레임워크)에서 발생할 수 있는 MEF 버전 관리 문제를 방지합니다.