다음을 통해 공유


Windows 앱의 패키지 ID 개요

패키지 ID는 공간과 시간 전체에 걸친 고유한 식별자입니다. DNA가 인간을 고유하게 식별하는 것처럼 패키지 ID는 패키지를 고유하게 식별합니다.

패키지에는 연결된 비트 세트(파일 등)이 있습니다. 두 패키지의 ID는 같지 않으며, 패키지와 연결된 비트를 변경하려면 다른 ID가 필요합니다.

패키지 ID란?

패키지 ID는 패키지를 고유하게 식별하는 논리적 구문입니다. ID는 다음과 같은 5개 부분으로 이루어집니다.

  • 이름: 앱 개발자가 선택한 이름입니다. Microsoft Store는 스토어 내의 모든 앱 개발자에게 모든 앱 이름의 고유성을 강제하지만 일반 생태계에서는 이름의 고유함이 보장되지 않습니다.
  • 버전: 패키지의 버전 번호입니다. 앱 개발자는 임의의 버전 번호를 선택할 수 있지만 업데이트 시 버전 번호가 올라가도록 해야 합니다.
  • 아키텍처: 패키지의 대상이 되는 프로세서 아키텍처입니다. 각 빌드가 자체 패키지에 있는 서로 다른 프로세서 아키텍처를 대상으로 동일한 앱을 빌드할 수 있습니다.
  • ResourceId: 앱 개발자가 리소스 패키지를 고유하게 식별하기 위해 선택한 문자열(예: 다른 언어 또는 다른 표시 배율)입니다. 리소스 패키지는 일반적으로 아키텍처 중립적입니다. 번들의 경우 ResourceId:는 항상 ~입니다.
  • 게시자: 서명 인증서로 식별되는 앱 개발자의 주체 이름입니다. 이는 신뢰할 수 있는 인증 기관에서 고유한 실제 이름과 ID로 인증서의 주체 이름 필드를 채우기 때문에 이론적으로 이는 각 앱 개발자에게 고유합니다.

이 구성을 5부 투플이라고도 합니다.

참고

서명되지 않은 패키지는 (1)여전히 게시자가 필요합니다. (2)게시자는 서명되지 않은 표식(OID.2.25.311729368913984317654407730594956997722=1)을 포함해야 합니다. (3)서명되지 않은 표식은 반드시 Publisher 문자열의 마지막 필드여야 합니다. (4)서명되지 않은 패키지에 대해서는 인증서나 서명이 없습니다.

패키지 ID 필드 한도

필드 데이터 형식 한도 주석
이름 패키지 문자열 최소: 3
최대: 50
유효성 검사 API당 허용되는 값(패키지 문자열 참조)
버전 DotQuad 최소: 0.0.0.0
최대: 65535.65535.65535.65535
문자열 형식은 10진법 점 표기법인 'Major.Minor.Build.Revision'을 사용합니다.
아키텍처 열거형 최소: 해당 없음
최대: 해당 없음
허용되는 값은 'neutral', 'x86', 'x64', 'arm', 'arm64', 'x86a64'입니다.
ResourceId 패키지 문자열 최소: 0
최대: 30
유효성 검사 API당 허용되는 값(패키지 문자열 참조)
게시자 문자열 최소: 1
최대: 8192
X.509당 허용되는 값
PublisherId 문자열 최소: 13
최대: 13
Base32 인코딩, Crockford 변형, 즉 [a-hjkmnp-tv-z0-9]

‘패키지 문자열’이란?

패키지 문자열은 다음 문자를 허용하는 문자열입니다.

  • 허용되는 입력 문자(ASCII 하위 집합)
    • 대문자(U+0041 ~ U+005A)
    • 소문자(U+0061 ~ U+007A)
    • 숫자(U+0030 ~ U+0039)
    • 점(U+002E)
    • 대시(U+002D)

다음 값은 패키지 문자열로 사용할 수 없습니다.

조건 금지된 값
다음과 같을 수 없음 '.', '..', 'con', 'prn', 'aux', 'nul', 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9', 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9'
다음으로 시작할 수 없음 'con.', 'prn.', 'aux.', 'nul.', 'com1.', 'com2.', 'com3.', 'com4.', 'com5.', 'com6.', 'com7.', 'com8.', 'com9.', 'lpt1.', 'lpt2.', 'lpt3.', 'lpt4.', 'lpt5.', 'lpt6.', 'lpt7.', 'lpt8.', 'lpt9.', 'xn--'
다음으로 끝날 수 없음 "="
다음을 포함할 수 없음 '.xn--'

패키지 문자열은 대/소문자를 구분하지 않는 서수 문자열 비교 API(예: _wcsicmp)를 사용하여 비교해야 합니다.

패키지 ID의 nameresourceid 필드는 패키지 문자열입니다.

PackageId 개체

PackageId는 5부 투플을 개별 필드(Name, Version, Architecture, ResourceId, Publisher)로 포함하는 개체입니다.

패키지 전체 이름

패키지 전체 이름은 패키지 ID의 5개 부분(이름, 버전, 아키텍처, 리소스 ID, 게시자) 전부를 사용해 만든 불투명 문자열입니다.

<Name>_<Version>_<Architecture>_<ResourceId>_<PublisherId>

예를 들어 Windows 사진 앱의 패키지 전체 이름은 'Microsoft.Windows.Photos_2020.20090.1002.0_x64__8wekyb3d8bbwe'입니다. 여기서 'Microsoft.Windows.Photos'는 이름, '2020.20090.1002.0'은 버전 번호, 'x64'는 대상 프로세서 아키텍처이며, 리소스 ID는 비어 있고(마지막의 두 밑줄 사이에 내용이 없음), '8wekyb3d8bbwe'는 Microsoft의 게시자 ID입니다.

패키지 전체 이름MSIX 패키지 또는 번들을 고유하게 식별합니다. 두 개의 패키지나 번들이 내용은 다른데 패키지 전체 이름이 동일한 것은 오류가 발생한 것입니다.

참고

MSIX는 이전 APPX의 새 이름입니다. 자세한 내용은 MSIX란?을 참조하세요.

패키지 패밀리 이름

패키지 패밀리 이름 패키지 ID의 두 부분(이름게시자)을 가져와서 만든 불투명 문자열입니다.

<Name>_<PublisherId>

예를 들어 Windows 사진 앱의 패키지 패밀리 이름은 'Microsoft.Windows.Photos_8wekyb3d8bbwe'입니다. 여기서 'Microsoft.Windows.Photos'는 이름이고 '8wekyb3d8bbwe'는 Microsoft의 게시자 ID입니다.

패키지 패밀리 이름은 '버전 없는 패키지 전체 이름'이라고도 합니다.

참고

패키지 패밀리 이름에는 아키텍처와 리소스 ID도 없기 때문에 완전히 정확한 이름은 아닙니다.

참고

데이터와 보안은 일반적으로 패키지 패밀리로 범위가 지정됩니다. 예를 들어 Wordwrap을 사용하도록 메모장 버전 1.0.0.0 패키지에서 설치된 메모장 앱을 구성한다면 좋은 환경이 될 수 없을 것입니다. 그러면 메모장이 1.0.0.1로 업데이트되고, 구성 데이터가 최신 버전의 패키지로 전달되지 않습니다.

게시자 Id

패키지 패밀리 이름은 형식이 있는 문자열입니다.

<name>_<publisherid>

여기에는 게시자 Id에 매우 구체적인 속성이 있습니다.

  • 게시자에서 파생됨
  • MinLength = MaxLength = 13자 [fixed-size]
  • 허용 문자(regex로) = a-hj-km-np-tv-z0-9
    • Base-32, Crockford 변형, 즉 I(아이), L(엘), O(오), U(유)를 제외한 영숫자(A-Z0-9)
  • 비교에 대/소문자를 구분하지 않는 서수 --- ABCDEFABCDEFG == abcdefabcdefg

따라서 % : \ / " ?를 절대 볼 수 없습니다. 또는 기타 문자가 게시자 Id에 보이지 않습니다.

자세한 내용은 PackageFamilyNameFromIdPackageNameAndPublisherIdFromFamilyName을 참조하세요.

게시자 ID는 PublisherId라고도 합니다.

왜 게시자 Id가 존재하나요?

게시자 Id는 게시자가 인증서의 X.509 이름/서명자와 일치해야 하므로 존재하며, 따라서 다음과 같은 문제가 있을 수 있습니다.

  • 값이 매우 클 수 있음(길이 <= 8192자)
  • 어색하거나 제한된 문자(백슬래시 등)이 포함될 수 있음

이러한 문제로 인해 일부 X.509 문자열을 파일 시스템, 레지스트리, URL 및 기타 컨텍스트에서 사용하기 어렵거나 사용하지 못할 수 있습니다.

PublisherId를 만들려면 어떻게 해야 하나요?

PackageNameAndPublisherIdFromFamilyName을 사용하여 PackageFamilyName에서 PublisherId을(를) 추출합니다.

PackageIdFromFullName을 사용하여 PackageFullName에서 PublisherId을(를) 추출합니다.

Publisher에서 PublisherId을(를) 만들어야 하는 경우는 드물지만 사용 가능한 API를 통해 만들 수 있습니다.

#include <appmodel.h>

HRESULT PublisherIdFromPublisher(
    _In_ PCWSTR publisher,
    _Out_writes_(PACKAGE_PUBLISHERID_MAX_LENGTH + 1) PWSTR publisherId)
{
    PCWSTR name{ L"xyz" };
    const size_t nameLength{ ARRAYSIZE(L"xyz") - 1 };
    const size_t offsetToPublisherId{ name + 1 }; // xyz_...publisherid...
    PACKAGE_ID id{};
    id.name = name;
    id.publisher = publisher;
 
    WCHAR familyName[PACKAGE_PUBLISHERID_MAX_LENGTH + 1]{};
    UINT32 n{ ARRAYSIZE(familyName) };
    RETURN_IF_WIN32_ERROR(PackageFamilyNameFromId(&id, &n, familyName);
    RETURN_IF_FAILED(StringCchCopyW(publisherId, PACKAGE_PUBLISHERID_MAX_LENGTH + 1, familyName + offsetToPublisherId));
    return S_OK;
}

다음은 동일한 작업의 클래식 Windows C 구현입니다.

#include <appmodel.h>

HRESULT PublisherIdFromPublisher(
    _In_ PCWSTR publisher,
    _Out_writes_(PACKAGE_PUBLISHERID_MAX_LENGTH + 1) PWSTR publisherId)
{
    const WCHAR c_name[]{ L"xyz" };
    const UINT32 c_nameLength{ ARRAYSIZE(c_nameForPublisherToPublisherId) - 1 };

    PACKAGE_ID id{};
    id.name = c_name;
    id.publisher = publisher;
    WCHAR familyName[PACKAGE_PUBLISHERID_MAX_LENGTH + 1]{};
    UINT32 n{ ARRAYSIZE(familyName) };
    RETURN_IF_WIN32_ERROR(PackageFamilyNameFromId(&id, &n, familyName));
    RETURN_IF_FAILED(StringCchCopyW(publisherId, PACKAGE_PUBLISHERID_MAX_LENGTH + 1,  familyName + c_nameLength + 1);
    return S_OK;
}

이렇게 하면 패키지 ID를 패키지 패밀리 이름으로 변환하여 결과 형식 xyz_<publisherid>과(와) 함께 PublisherId가 만들어집니다. 이 방법은 안정적이고 신뢰할 수 있는 방법입니다.

이렇게 하려면 SDK에서 appmodel.h로 컴파일하고 kernel32.lib(또는 APIets를 사용하는 경우 kernelbase.lib, onecore.lib 또는 api-ms-win-appmodel-runtime-l1.lib)와 연결하면 됩니다.

패키지 ID의 프로세서 아키텍처 이해

Architecture=x64은(는) 패키지에 x64 코드만 포함할 수 있음을 의미한다고 잘못 해석하는 경우가 많습니다. 사실이 아닙니다. 실제로는 패키지가 x64 코드를 지원하는 시스템에서 작동하고, x64 앱에서 사용할 수 있다는 것을 뜻합니다. PDF 파일만 포함된 패키지를 만들 수 있지만 x64 호환 시스템에만 설치할 목적이므로 <Identity Architecture=x64...>(으)로 선언합니다(예: x86, Arm 및 Windows 10 Arm64 시스템은 x64를 지원하지 않으므로 x64 패키지는 x64 및 Arm64 시스템(Windows 11 기준)에만 설치할 수 있습니다).

오해하는 경우가 더욱 많은데, Architecture=neutral은(는) 패키지에 실행 가능한 코드가 없음을 뜻하지 않습니다. 이는 패키지가 모든 아키텍처에서 작동한다는 것을 의미합니다. 예를 들어, JavaScript, Python, C#등으로 작성된 AES 암호화 API를 포함하는 패키지를 만들 수 있지만 Arm64 시스템에서는 실행이 허용되지 않습니다. 따라서 최적화된 Arm64 이진 파일을 포함하고 이를 처리하는 API를 구현합니다.

void Encrypt(...)
{
    HANDLE h{};
    if (GetCpu() == arm64)
    {
        h = LoadLibrary(GetCurrentPackagePath() + "\bin\encrypt-arm64.dll")
        p = GetProcAddress(h, "Encrypt")
        return (*p)(...)
    }
    else
    {
        // ...call other implementation...
    }
}

또는 여러 변형을 사용하여 중립 패키지를 만들 수 있습니다.

\
    bin\
        encrypt-x86.dll
        encrypt-x64.dll
        encrypt-arm.dll
        encrypt-arm64.dll

그런 다음 개발자는 LoadLibrary("bin\encrypt-" + cpu + ".dll") 런타임에 프로세스에 적절한 이진 파일을 가져올 수 있습니다.

일반적으로 중립 패키지는 아키텍처별 콘텐츠가 없지만, 보유할 수는 있습니다. 수행할 수 있는 작업에는 제한이 있습니다(예: x86 + x64 + arm + arm + arm64용으로 컴파일된 notepad.exe를 포함하는 메모장 패키지를 만들 수 있지만 appxmanifest.xml은 그 중 하나를 가리키는 <Application Executable=...>만 선언할 수 있음). 필요한 비트만 설치할 수 있는 번들이 있다는 점을 감안하면, 이는 매우 드문 일입니다. 이는 불법이 아니며, 고급지고, 흔하지 않을 뿐입니다.

또한 Architecture=x86(또는 x64|arm|arm64)은(는) 패키지에 지정된 아키텍처에 실행 가능한 코드만 포함됨을 뜻하지 않습니다. 그러한 경우가 압도적으로 많을 뿐입니다.

참고

이 맥락에서 '코드' 또는 '실행 가능한 코드'는 PE(이식 가능한 실행 파일) 파일을 의미합니다.

패키지 ID는 대/소문자를 구분하나요?

대부분이 그렇지 않지만, Publisher은(는) 대/소문자를 구분합니다.

남아 있는 필드(Name, ResourceId, PublisherId, PackageFullName, PackageFamilyName)는 그렇지 않습니다. 이러한 필드는 대/소문자를 유지하지만 이를 구분하지 않고 비교합니다.

참고 항목

패키지 ID

PackageFamilyNameFromId

PackageNameAndPublisherIdFromFamilyName