WNS(Windows 푸시 알림 서비스) 개요

타사 개발자는 WNS(Windows 푸시 알림 서비스)를 사용하여 클라우드 서비스에서 알림, 타일, 배지 및 원시 업데이트를 보낼 수 있습니다. WNS는 에너지 효율적이며 신뢰할 수 있는 방법으로 사용자에게 새 업데이트를 전달하는 메커니즘을 제공합니다.

작동 방식

다음 다이어그램은 푸시 알림을 보내기 위한 전체 데이터 흐름을 보여줍니다. 여기에는 다음 단계가 포함됩니다.

  1. 앱이 WNS에서 푸시 알림 채널을 요청합니다.
  2. Windows는 WNS에 알림 채널을 만들도록 요청합니다. 이 채널은 URI(Uniform Resource Identifier) 형식으로, 호출 디바이스로 반환됩니다.
  3. 알림 채널 URI는 WNS에서 앱으로 반환됩니다.
  4. 앱은 URI를 사용자 고유의 클라우드 서비스로 보냅니다. 그런 다음 알림을 보낼 때 URI에 액세스할 수 있도록 자체 클라우드 서비스에 URI를 저장합니다. URI는 사용자 고유 앱과 사용자 고유 서비스 간의 인터페이스입니다. 안전하고 보안이 철저한 웹 표준을 사용하여 이 인터페이스를 구현하는 것은 사용자의 책임입니다.
  5. 클라우드 서비스에 보낼 업데이트가 있는 경우 채널 URI를 사용하여 WNS에 알립니다. 이 작업은 SSL(Secure Sockets Layer)을 통해 알림 페이로드를 포함한 HTTP POST 요청을 실행하여 수행됩니다. 이 단계는 인증이 필요합니다.
  6. WNS는 요청을 수신하고 해당 디바이스에 알림을 라우팅합니다.

wns data flow diagram for push notification

앱 등록 및 클라우드 서비스에 대한 자격 증명 수신

WNS를 사용하여 알림을 보내려면 여기에 설명된 대로 먼저 스토어 대시보드에 앱을 등록해야 합니다.

알림 채널 요청

푸시 알림을 받을 수 있는 앱이 실행되면 먼저 CreatePushNotificationChannelForApplicationAsync를 통해 알림 채널을 요청해야 합니다. 전체 토론 및 예제 코드는 알림 채널을 요청, 만들기 및 저장하는 방법을 참조하세요. 이 API는 호출 애플리케이션 및 해당 타일에 고유하게 연결되고 모든 알림 유형을 보낼 수 있는 채널 URI를 반환합니다.

앱이 채널 URI를 성공적으로 만든 후 이 URI와 연결되어야 하는 앱별 메타데이터와 함께 클라우드 서비스로 보냅니다.

중요한 참고 사항

  • 앱에 대한 알림 채널 URI가 항상 동일하게 보장하지는 않습니다. 앱이 실행되고 URI가 변경되면 서비스를 업데이트할 때마다 새 채널을 요청하는 것이 좋습니다. 개발자는 채널 URI를 수정해서는 안 되며 이를 블랙박스 문자열로 간주해야 합니다. 현재, 채널 URI는 30일 후에 만료됩니다. Windows 10 앱이 백그라운드에서 주기적으로 채널을 갱신하는 경우 Windows 8.1용 푸시 및 정기 알림 샘플을 다운로드하고 소스 코드 및/또는 해당 소스 코드 및/또는 해당 패턴을 다시 사용할 수 있습니다.
  • 개발자는 클라우드 서비스와 클라이언트 앱 간의 인터페이스를 구현합니다. 앱은 자체 서비스를 사용하여 인증 프로세스를 거치고 HTTPS와 같은 보안 프로토콜을 통해 데이터를 전송하는 것이 좋습니다.
  • 클라우드 서비스는 항상 채널 URI가 기본 'notify.windows.com'을 사용하는지 확인하는 것이 중요합니다. 서비스는 다른 방법의 채널에 알림을 푸시해서는 안 됩니다. 앱의 콜백이 손상된 경우 악의적인 공격자가 채널 URI를 제출하여 WNS를 스푸핑할 수 있습니다. 도메인을 검사하지 않으면 클라우드 서비스에서 자신도 모르게 이러한 공격자에게 정보를 공개할 수도 있습니다. 채널 URI의 하위 도메인은 기본 변경될 수 있으며 채널 URI의 유효성을 검사할 때는 고려해서는 안 됩니다.
  • 클라우드 서비스가 만료된 채널에 알림을 전달하려고 하면 WNS는 응답 코드 410을 반환합니다. 해당 코드에 대한 응답으로 서비스는 더 이상 해당 URI에 알림을 보내려고 시도하지 않아야 합니다.

클라우드 서비스 인증

알림을 보내려면 WNS를 통해 클라우드 서비스를 인증해야 합니다. 이 프로세스의 첫 번째 단계는 Microsoft Store 대시보드를 사용하여 앱을 등록하는 것입니다. 등록 프로세스 중 앱에 SID(패키지 보안 식별자) 및 비밀 키가 제공됩니다. 이 정보는 클라우드 서비스에서 WNS로 인증하는 데 사용됩니다.

WNS 인증 체계는 OAuth 2.0 프로토콜의 클라이언트 자격 증명 프로필을 사용하여 구현됩니다. 클라우드 서비스는 자격 증명(패키지 SID 및 비밀 키)을 제공하여 WNS로 인증합니다. 그 대가로 액세스 토큰을 받습니다. 이 액세스 토큰을 사용하면 클라우드 서비스에서 알림을 보낼 수 있습니다. 토큰은 WNS로 전송되는 모든 알림 요청에 필요합니다.

높은 수준에서 정보 체인은 다음과 같습니다.

  1. 클라우드 서비스는 OAuth 2.0 프로토콜에 따라 HTTPS를 통해 해당 자격 증명을 WNS로 보냅니다. WNS를 사용하여 서비스를 인증합니다.
  2. 인증에 성공하면 WNS는 액세스 토큰을 반환합니다. 이 액세스 토큰은 만료될 때까지 모든 후속 알림 요청에 사용됩니다.

wns diagram for cloud service authentication

WNS를 사용한 인증에서 클라우드 서비스는 SSL(Secure Sockets Layer)을 통해 HTTP 요청을 제출합니다. 매개 변수는 'application/x-www-for-urlencoded' 형식으로 제공됩니다. 다음 예제와 같이 패키지 SID를 'client_id' 필드에 입력하고 비밀 키를 'client_secret' 필드에 입력합니다. 구문 세부 정보는 액세스 토큰 요청 참조를 확인하세요.

참고

이 내용은 예시에 불과하며, 고유 코드에 복사 및 붙여넣기하여 사용할 수 있는 코드가 아닙니다. 

 POST /accesstoken.srf HTTP/1.1
 Content-Type: application/x-www-form-urlencoded
 Host: https://login.live.com
 Content-Length: 211
 
 grant_type=client_credentials&client_id=ms-app%3a%2f%2fS-1-15-2-2972962901-2322836549-3722629029-1345238579-3987825745-2155616079-650196962&client_secret=Vex8L9WOFZuj95euaLrvSH7XyoDhLJc7&scope=notify.windows.com

WNS는 클라우드 서비스를 인증하고 성공하면 '200 OK'라는 응답을 보냅니다. 액세스 토큰은 'application/json' 미디어 형식을 사용하여 HTTP 응답 본문에 포함된 매개 변수에 반환됩니다. 서비스에서 액세스 토큰을 받은 후에는 알림을 보낼 준비가 된 것입니다.

다음 예제에서는 액세스 토큰을 포함하여 성공적인 인증 응답을 보여 줍니다. 구문에 대한 자세한 내용은 푸시 알림 서비스 요청 및 응답 헤더를 참조하세요.

 HTTP/1.1 200 OK   
 Cache-Control: no-store
 Content-Length: 422
 Content-Type: application/json
 
 {
     "access_token":"EgAcAQMAAAAALYAAY/c+Huwi3Fv4Ck10UrKNmtxRO6Njk2MgA=", 
     "token_type":"bearer"
 }

중요한 참고 사항

  • 이 절차에서 지원되는 OAuth 2.0 프로토콜은 초안 버전 V16을 따릅니다.
  • OAuth RFC(주석 요청)는 '클라이언트'라는 용어를 사용하여 클라우드 서비스를 참조합니다.
  • OAuth 초안이 완료될 때 이 절차가 변경될 수 있습니다.
  • 액세스 토큰은 여러 알림 요청에 다시 사용할 수 있습니다. 이렇게 하면 클라우드 서비스가 한 번의 인증으로 많은 알림을 보낼 수 있습니다. 그러나 액세스 토큰이 만료되면 클라우드 서비스는 새 액세스 토큰을 받기 위해 다시 인증해야 합니다.

알림 보내기

클라우드 서비스는 채널 URI를 사용하여 사용자에 대한 업데이트가 있을 때마다 알림을 보낼 수 있습니다.

위에서 설명한 액세스 토큰은 여러 알림 요청에 다시 사용할 수 있습니다. 클라우드 서버는 모든 알림에 대해 새 액세스 토큰을 요청할 필요가 없습니다. 액세스 토큰이 만료된 경우 알림 요청은 오류를 반환합니다. 액세스 토큰이 거부된 경우 알림을 두 번 이상 다시 보내지 않는 것을 권장합니다. 이 오류가 발생하면 새 액세스 토큰을 요청하고 알림을 다시 보내야 합니다. 정확한 오류 코드는 푸시 알림 응답 코드를 참조하세요.

  1. 클라우드 서비스는 채널 URI에 대한 HTTP POST를 만듭니다. 이 요청은 SSL을 통해 수행되어야 하며 필요한 헤더 및 알림 페이로드를 포함해야 합니다. 권한 부여 헤더에는 권한 부여를 위해 획득한 액세스 토큰이 포함되어야 합니다.

    요청에 대한 예제는 다음과 같습니다. 구문 세부 정보는 푸시 알림 응답 코드를 참조하세요.

    알림 페이로드 작성에 대한 자세한 내용은 빠른 시작: 푸시 알림 보내기를 참조하세요. 타일, 알림 메시지 또는 배지 푸시 알림의 페이로드는 정의된 각 적응형 타일 스키마 또는 레거시 타일 스키마를 준수하는 XML 콘텐츠로 제공됩니다. 로우 알림의 페이로드에 지정된 구조가 없습니다. 엄격하게 앱에서 정의됩니다.

     POST https://cloud.notify.windows.com/?token=AQE%bU%2fSjZOCvRjjpILow%3d%3d HTTP/1.1
     Content-Type: text/xml
     X-WNS-Type: wns/tile
     Authorization: Bearer EgAcAQMAAAAALYAAY/c+Huwi3Fv4Ck10UrKNmtxRO6Njk2MgA=
     Host: cloud.notify.windows.com
     Content-Length: 24
    
     <body>
     ....
    
  2. WNS에 알림이 수신되었으며 사용 가능한 다음 기회에 전달될 것임을 나타내기 위해 응답합니다. 그러나 WNS는 디바이스 또는 애플리케이션에서 알림을 받았다는 엔드투엔드 확인을 제공하지 않습니다.

이 다이어그램에서는 이러한 데이터 흐름을 보여 줍니다.

wns diagram for sending a notification

중요한 참고 사항

  • Microsoft는 푸시 알림의 안정성이나 대기 시간을 보장하지 않습니다.
  • 알림에는 기밀, 중요한 데이터 또는 개인 데이터가 포함되어서는 안 됩니다.
  • 알림을 보내려면 클라우드 서비스가 먼저 WNS를 사용하여 인증하고 액세스 토큰을 받아야 합니다.
  • 액세스 토큰은 클라우드 서비스가 토큰을 만든 단일 앱에만 알림을 보낼 수 있도록 허용합니다. 하나의 액세스 토큰을 사용하여 여러 앱에서 알림을 보내는 것은 불가능합니다. 따라서 클라우드 서비스가 여러 앱을 지원하는 경우 각 채널 URI에 알림을 푸시할 때 앱에 대한 올바른 액세스 토큰을 제공해야 합니다.
  • 디바이스가 오프라인 상태인 경우 기본적으로 WNS는 각 채널 URI에 대한 각 알림 유형(타일, 배지, 알림) 중 하나를 저장하고 로우 알림은 저장하지 않습니다.
  • 알림 콘텐츠가 사용자에게 개인 설정된 시나리오에서 WNS는 클라우드 서비스가 해당 업데이트를 받을 때 즉시 해당 업데이트를 보내는 것이 좋습니다. 이 시나리오의 예로는 소셜 미디어 피드 업데이트, 인스턴트 통신 초대, 새 메시지 알림 또는 경고가 있습니다. 또는 동일한 일반 업데이트가 사용자의 큰 하위 집합에 자주 전달되는 시나리오가 있을 수 있습니다. 예를 들어 날씨, 주식 및 뉴스 업데이트입니다. WNS 지침은 이러한 업데이트 빈도를 30분마다 최대 1개까지 지정합니다. 최종 사용자 또는 WNS는 더 자주 일상적인 업데이트를 악의적으로 판단할 수 있습니다.
  • Windows 알림 플랫폼은 소켓을 활성인 정상 상태로 유지하기 위해 WNS와 주기적인 데이터 연결을 유지합니다. 알림 채널을 요청하거나 사용하는 응용 프로그램이 없으면 소켓이 생성되지 않습니다.

타일 및 배지 알림 만료

기본적으로 타일 및 배지 알림은 다운로드 후 3일 후에 만료됩니다. 알림이 만료되면 타일 또는 큐에서 콘텐츠가 제거되고 사용자에게 더 이상 표시되지 않습니다. 타일의 콘텐츠가 관련 콘텐츠보다 오래 지속되지 않도록 모든 타일 및 배지 알림에서 만료(앱에 적합한 시간 사용)를 설정하는 것이 가장 좋습니다. 명시적 만료 시간은 정의된 수명이 있는 콘텐츠에 필수적입니다. 또한 클라우드 서비스가 알림 전송을 중지하거나 사용자가 오랜 기간 동안 네트워크에서 연결을 끊는 경우 부실 콘텐츠를 제거할 수 있습니다.

클라우드 서비스는 X-WNS-TTL HTTP 헤더를 설정하여 알림의 전송 후 유효 기간을 지정함으로써 각 알림에 대해 만료를 설정할 수 있습니다. 자세한 내용은 푸시 알림 서비스 요청 및 응답 헤더를 참조하세요.

예를 들어 주식 시장의 활성 거래일 동안 주식 가격 업데이트의 만료를 전송 간격의 두 배로 설정할 수 있습니다(예: 30분마다 전송하는 경우 수신 후 1시간). 또 다른 예로, 뉴스 앱은 하루가 일일 뉴스 타일 업데이트에 적절한 만료 시간이라고 판단할 수 있습니다.

푸시 알림 및 배터리 절약 모드

배터리 절약 모드는 디바이스에서 백그라운드 작업을 제한하여 배터리 수명을 연장합니다. Windows 10을 사용하면 배터리가 지정된 임계값 아래로 떨어질 때 배터리 절약 모드가 자동으로 켜지도록 설정할 수 있습니다. 배터리 절약 모드가 켜져 있으면 푸시 알림 수신이 비활성화되어 에너지를 절약할 수 있습니다. 그러나 몇 가지 예외 사항이 있습니다. 다음 Windows 10 배터리 절약 모드 설정(Settings 앱에서 찾을 수 있습니다)을 사용하면 배터리 절약 모드가 켜져 있는 경우에도 앱이 푸시 알림을 받을 수 있습니다.

  • 배터리 절약 모드에 있는 동안 모든 앱에서 푸시 알림 허용: 이 설정을 사용하면 배터리 절약 모드가 켜져 있는 동안 모든 앱에서 푸시 알림을 받을 수 있습니다. 이 설정은 데스크톱용 Windows 10 버전(Home, Pro, Enterprise 및 Education)에만 적용됩니다.
  • 항상 허용: 이 설정을 사용하면 푸시 알림 수신을 포함하여 배터리 절약 모드가 켜져 있는 동안 특정 앱이 백그라운드에서 실행될 수 있습니다. 이 목록은 사용자가 수동으로 유지합니다.

이 두 설정의 상태 검사 방법은 없지만 배터리 절약 모드의 상태를 검사 수 있습니다. Windows 10에서 EnergySaverStatus 속성을 사용하여 배터리 절약 모드를 검사. 또한 앱은 EnergySaverStatusChanged 이벤트를 사용하여 배터리 절약 장치의 변경 내용을 수신 대기할 수 있습니다.

앱이 푸시 알림에 크게 의존하는 경우 배터리 절약 모드가 켜져 있는 동안 알림을 받지 못할 수 있음을 사용자에게 알리고 배터리 절약 모드 설정을 쉽게 조정할 수 있도록 하는 것이 좋습니다. Windows 10, ms-settings:batterysaver-settings에서 배터리 절약 모드 설정 URI 체계를 사용하여 설정 앱에 편리한 링크를 제공할 수 있습니다.

사용자에게 배터리 절약 모드 설정에 대해 알릴 때 나중에 메시지가 표시되지 않게 만드는 방법을 제공하는 것이 좋습니다. 예를 들어 다음 예제의 dontAskMeAgainBox 체크 박스는 LocalSettings 사용자의 기본 설정을 유지합니다.

다음은 Windows 10에서 배터리 절약 모드가 켜져 있는지 확인하는 방법의 예입니다. 이 예제에서는 사용자에게 알리고 설정 앱을 배터리 절약 모드 설정으로 시작합니다. dontAskAgainSetting을(를) 사용하여 다시 알림을 받고 싶지 않을 때 사용자가 메시지를 표시하지 않도록 할 수 있습니다.

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Windows.System;
using Windows.System.Power;
...
...
async public void CheckForEnergySaving()
{
   //Get reminder preference from LocalSettings
   bool dontAskAgain;
   var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
   object dontAskSetting = localSettings.Values["dontAskAgainSetting"];
   if (dontAskSetting == null)
   {  // Setting does not exist
      dontAskAgain = false;
   }
   else
   {  // Retrieve setting value
      dontAskAgain = Convert.ToBoolean(dontAskSetting);
   }
   
   // Check if battery saver is on and that it's okay to raise dialog
   if ((PowerManager.EnergySaverStatus == EnergySaverStatus.On)
         && (dontAskAgain == false))
   {
      // Check dialog results
      ContentDialogResult dialogResult = await saveEnergyDialog.ShowAsync();
      if (dialogResult == ContentDialogResult.Primary)
      {
         // Launch battery saver settings (settings are available only when a battery is present)
         await Launcher.LaunchUriAsync(new Uri("ms-settings:batterysaver-settings"));
      }

      // Save reminder preference
      if (dontAskAgainBox.IsChecked == true)
      {  // Don't raise dialog again
         localSettings.Values["dontAskAgainSetting"] = "true";
      }
   }
}
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.System.Power.h>
#include <winrt/Windows.UI.Xaml.h>
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Navigation.h>
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::System;
using namespace winrt::Windows::System::Power;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Controls;
using namespace winrt::Windows::UI::Xaml::Navigation;
...
winrt::fire_and_forget CheckForEnergySaving()
{
    // Get reminder preference from LocalSettings.
    bool dontAskAgain{ false };
    auto localSettings = ApplicationData::Current().LocalSettings();
    IInspectable dontAskSetting = localSettings.Values().Lookup(L"dontAskAgainSetting");
    if (!dontAskSetting)
    {
        // Setting doesn't exist.
        dontAskAgain = false;
    }
    else
    {
        // Retrieve setting value
        dontAskAgain = winrt::unbox_value<bool>(dontAskSetting);
    }

    // Check whether battery saver is on, and whether it's okay to raise dialog.
    if ((PowerManager::EnergySaverStatus() == EnergySaverStatus::On) && (!dontAskAgain))
    {
        // Check dialog results.
        ContentDialogResult dialogResult = co_await saveEnergyDialog().ShowAsync();
        if (dialogResult == ContentDialogResult::Primary)
        {
            // Launch battery saver settings
            // (settings are available only when a battery is present).
            co_await Launcher::LaunchUriAsync(Uri(L"ms-settings:batterysaver-settings"));
        }

        // Save reminder preference.
        if (dontAskAgainBox().IsChecked())
        {
            // Don't raise the dialog again.
            localSettings.Values().Insert(L"dontAskAgainSetting", winrt::box_value(true));
        }
    }
}

이 예제에 소개된 ContentDialog용 XAML입니다.

<ContentDialog x:Name="saveEnergyDialog"
               PrimaryButtonText="Open battery saver settings"
               SecondaryButtonText="Ignore"
               Title="Battery saver is on."> 
   <StackPanel>
      <TextBlock TextWrapping="WrapWholeWords">
         <LineBreak/><Run>Battery saver is on and you may 
          not receive push notifications.</Run><LineBreak/>
         <LineBreak/><Run>You can choose to allow this app to work normally
         while in battery saver, including receiving push notifications.</Run>
         <LineBreak/>
      </TextBlock>
      <CheckBox x:Name="dontAskAgainBox" Content="OK, got it."/>
   </StackPanel>
</ContentDialog>