푸시 API

W3C 워킹 드래프트

이 문서에 대한 추가 정보
이 버전:
https://www.w3.org/TR/2025/WD-push-api-20250925/
최신 공개 버전:
https://www.w3.org/TR/push-api/
최신 에디터 드래프트:
https://w3c.github.io/push-api/
히스토리:
https://www.w3.org/standards/history/push-api/
커밋 히스토리
편집자:
Marcos Caceres (Apple Inc.)
Kagami Rosylight (Mozilla 재단)
이전 편집자:
Peter Beverloo (Google) -
Martin Thomson (Mozilla 재단) -
Bryan Sullivan (AT&T) -
Eduardo Fullea (Telefonica) -
Michaël van Ouwerkerk (Google) -
피드백:
GitHub w3c/push-api (풀 리퀘스트, 새 이슈, 오픈 이슈)

요약

Push API푸시 메시지를 웹 애플리케이션에 푸시 서비스를 통해 전송할 수 있게 해줍니다. 애플리케이션 서버는 웹 애플리케이션이나 사용자 에이전트가 비활성 상태일 때에도 언제든지 푸시 메시지를 보낼 수 있습니다. 푸시 서비스사용자 에이전트에 신뢰성 있고 효율적으로 전달함을 보장합니다. 푸시 메시지는 웹 애플리케이션의 오리진에서 실행되는 서비스 워커에 전달되며, 메시지의 정보를 사용해 로컬 상태를 업데이트하거나 사용자에게 알림을 표시할 수 있습니다.

이 명세는 웹 푸시 프로토콜과 함께 사용하도록 설계되었으며, 애플리케이션 서버 또는 사용자 에이전트푸시 서비스와 어떻게 상호작용하는지 설명합니다.

이 문서의 상태

이 섹션은 이 문서가 출판된 시점의 상태를 설명합니다. 최신 W3C 출판물 목록과 이 기술 보고서의 최신 개정본은 W3C 표준 및 초안 색인에서 확인할 수 있습니다.

이 문서는 웹 애플리케이션 작업 그룹에서 권고 트랙을 사용하여 워킹 드래프트로 출판되었습니다.

워킹 드래프트로 출판된다고 해서 W3C와 회원의 승인이나 지지를 의미하지 않습니다.

이 문서는 초안이며, 언제든지 다른 문서로 업데이트, 대체, 또는 폐기될 수 있습니다. 진행 중인 작업 외의 용도로 인용하는 것은 적절하지 않습니다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 작성되었습니다. W3C는 그룹 산출물과 관련하여 공개된 특허 목록을 유지하며, 해당 페이지에는 특허 공개 방법도 포함되어 있습니다. 개인이 본인 소유의 특허가 필수 청구항을 포함한다고 믿는 경우, W3C 특허 정책 6절에 따라 정보를 공개해야 합니다.

이 문서는 2025년 8월 18일 W3C 프로세스 문서에 따라 관리됩니다.

1. 소개

이 섹션은 규범적이지 않습니다.

Push API는 웹 애플리케이션이 사용자 에이전트와 비동기적으로 통신할 수 있게 해줍니다. 이를 통해 애플리케이션 서버사용자 에이전트에게 정보를 즉시 제공할 수 있으며, 사용자가 웹 애플리케이션을 열 때까지 기다릴 필요가 없습니다.

여기서 정의된 푸시 서비스는 언제든지 푸시 메시지의 전달을 지원합니다.

특히, 푸시 메시지는 웹 애플리케이션이 브라우저 창에서 활성 상태가 아니더라도 전달됩니다. 이는 사용자가 웹 애플리케이션을 종료하더라도 푸시 메시지를 수신할 때 웹 애플리케이션이 다시 시작될 수 있는 사용 사례와 관련이 있습니다. 예를 들어, 푸시 메시지는 사용자가 WebRTC 호출을 받고 있음을 알리는 데 사용될 수 있습니다.

푸시 메시지사용자 에이전트가 잠시 오프라인일 때에도 전송될 수 있습니다. 이를 지원하기 위해, 푸시 서비스사용자 에이전트가 사용 가능해질 때까지 메시지를 저장합니다. 이는 사용자가 오프라인일 때 웹 애플리케이션에서 발생한 변경 사항을 인지할 수 있도록 하며, 사용자 에이전트가 적시에 관련 정보를 받을 수 있도록 보장합니다. 푸시 메시지푸시 서비스에 저장되며, 사용자 에이전트가 접근 가능해지면 메시지가 전달됩니다.

Push API는 또한 사용자 에이전트가 웹 애플리케이션을 적극적으로 사용하는 동안에도 푸시 메시지의 신뢰성 있는 전달을 보장합니다. 예를 들어, 사용자가 웹 애플리케이션을 적극적으로 사용하거나, 웹 애플리케이션이 활성 워커, 프레임 또는 백그라운드 윈도우를 통해 애플리케이션 서버와 활발히 통신할 때 해당됩니다. 이것은 Push API의 주요 사용 사례는 아니며, 웹 애플리케이션은 애플리케이션 서버와 지속적인 통신을 유지하지 않기 위해 드물게 메시지를 보내고자 할 때 Push API를 선택할 수 있습니다.

푸시 메시징은 사용자 에이전트와 웹 애플리케이션 간에 이미 활성화된 통신 채널이 없을 때 가장 적합합니다. 푸시 메시지를 보내는 것은 Fetch API 또는 [WebSockets]과 같은 보다 직접적인 통신 방법에 비해 훨씬 더 많은 리소스를 필요로 합니다. 푸시 메시지는 일반적으로 직접 통신보다 더 높은 지연이 발생하며, 사용에 제한이 있을 수 있습니다. 대부분의 푸시 서비스는 전송 가능한 푸시 메시지의 크기와 양을 제한합니다.

2. 의존성

웹 푸시 프로토콜 [RFC8030]은 사용자 에이전트 또는 애플리케이션 서버푸시 서비스 간의 통신을 가능하게 하는 프로토콜을 설명합니다. 다른 프로토콜을 대신 사용할 수도 있지만, 본 명세는 해당 프로토콜의 사용을 가정합니다. 대체 프로토콜도 호환되는 의미론을 제공해야 합니다.

Content-Encoding HTTP 헤더는 [RFC7231]의 3.1.2.2절에 설명되어 있으며, 푸시 메시지 페이로드에 적용된 콘텐츠 코딩을 나타냅니다.

3. 개념

3.1 애플리케이션 서버

애플리케이션 서버는 웹 애플리케이션의 서버 측 구성 요소를 의미합니다.

3.2 푸시 메시지

푸시 메시지애플리케이션 서버에서 웹 애플리케이션으로 전송되는 데이터입니다.

푸시 메시지는 해당 메시지가 제출된 활성 워커와 연결된 푸시 구독에 전달됩니다. 서비스 워커가 현재 실행 중이 아니면, 전달을 위해 워커가 시작됩니다.

3.3 선언적 푸시 메시지

선언적 푸시 메시지푸시 메시지이며, 데이터가 사용자 에이전트가 이해할 수 있는 JSON 문서입니다. 사용자 에이전트는 각 수신되는 푸시 메시지를 기회적으로 파싱하여, 선언적 푸시 메시지인지 선언적 푸시 메시지 파서를 통해 판단합니다.

선언적 푸시 메시지는 서비스 워커의 개입 없이 알림을 생성하고 표시할 수 있습니다. 그럼에도 불구하고, 애플리케이션 서버가 원하면 서비스 워커가 개입할 수 있습니다. 이러한 경우, 푸시 메시지의 선언적 특성은 예를 들어 저장소 압박으로 서비스 워커가 제거된 경우 백업 역할을 합니다. 또한 알림 데이터를 전달하는 보다 객체 지향적인 방법을 제공합니다.

{
  "web_push": 8030,
  "notification": {
    "title": "Ada emailed ‘London’",
    "lang": "en-US",
    "dir": "ltr",
    "body": "Did you hear about the tube strikes?",
    "navigate": "https://email.example/message/12"
  }
}

3.3.1 멤버

선언적 푸시 메시지는 다음과 같은 멤버를 갖습니다:

web_push (필수)

반드시 8030이어야 하는 정수입니다. 선언적 푸시 메시지와 다른 JSON 문서를 구분하는 데 사용합니다.

notification (필수)

다음과 같은 멤버로 구성된 JSON 객체이며, 모두 알림 API의 기능과 유사하지만 때로는 조금 더 엄격한 타입을 가집니다. title을 제외한 모든 멤버는 NotificationOptions 딕셔너리에서 파생되었고, 동기화하여 유지해야 합니다. [NOTIFICATIONS]

title (필수)

문자열.

dir

"auto", "ltr", 또는 "rtl".

lang

언어 태그를 담는 문자열.

body

문자열.

navigate (필수)

URL을 담는 문자열.

tag

문자열.

image

URL을 담는 문자열.

icon

URL을 담는 문자열.

badge

URL을 담는 문자열.

vibrate

32비트 부호 없는 정수의 배열.

timestamp

64비트 부호 없는 정수.

renotify

불리언.

silent

불리언.

requireInteraction

불리언.

비고

NotificationOptions 딕셔너리와 일관성을 위해 require_interaction이 아닌 이름을 사용합니다.

data

임의의 JSON 값.

actions

다음과 같은 멤버를 가진 JSON 객체의 배열로, 모두 NotificationAction 딕셔너리에서 파생되었고 동기화하여 유지해야 합니다.

action (필수)

문자열.

title (필수)

문자열.

navigate (필수)

URL을 담는 문자열.

icon

URL을 담는 문자열.

mutable

불리언. true일 때 Notification 객체를 포함하는 push 이벤트가 서비스 워커(있는 경우)로 전달됩니다. 이 객체는 선언적 푸시 메시지에 의해 기술됩니다.

3.3.2 파서

선언적 푸시 메시지 파서 결과튜플이며, notification(알림)과 mutable(불리언)로 구성됩니다.

선언적 푸시 메시지 파서바이트 시퀀스 bytes, origin origin, URL baseURL, 그리고 EpochTimeStamp fallbackTimestamp를 받아 아래 단계들을 실행합니다. 실패 또는 선언적 푸시 메시지 파서 결과를 반환합니다.

  1. messageJSON 바이트를 Infra 값으로 파싱의 결과로 합니다. 예외가 발생하면 실패를 반환합니다.

  2. messagemap이 아니면 실패를 반환합니다.

  3. message["web_push"]가 존재하지 않거나 8030이 아니면 실패를 반환합니다.

  4. message["notification"]이 존재하지 않으면 실패를 반환합니다.

  5. notificationInputmessage["notification"]으로 합니다.

  6. notificationInputmap이 아니면 실패를 반환합니다.

  7. notificationInput["title"]이 존재하지 않거나 문자열이 아니면 실패를 반환합니다.

  8. notificationInput["navigate"]이 존재하지 않거나 문자열이 아니면 실패를 반환합니다.

  9. notificationTitlenotificationInput["title"]으로 합니다.

  10. notificationOptionsNotificationOptions 딕셔너리로 합니다.

  11. notificationInput["dir"]이 존재하고 "auto", "ltr", "rtl" 중 하나라면, notificationOptions["dir"] 에 notificationInput["dir"]을 설정합니다.

  12. notificationInput["lang"]이 존재하고 문자열이면, notificationOptions["lang"] 에 notificationInput["lang"]을 설정합니다.

  13. notificationInput["body"]이 존재하고 문자열이면, notificationOptions["body"] 에 notificationInput["body"]을 설정합니다.

  14. notificationOptions["navigate"] 에 notificationInput["navigate"](변환됨)을 설정합니다.

  15. notificationInput["tag"]이 존재하고 문자열이면, notificationOptions["tag"] 에 notificationInput["tag"]을 설정합니다.

  16. notificationInput["image"]이 존재하고 문자열이면, notificationOptions["image"] 에 notificationInput["image"](변환됨)을 설정합니다.

  17. notificationInput["icon"]이 존재하고 문자열이면, notificationOptions["icon"] 에 notificationInput["icon"](변환됨)을 설정합니다.

  18. notificationInput["badge"]이 존재하고 문자열이면, notificationOptions["badge"] 에 notificationInput["badge"](변환됨)을 설정합니다.

  19. notificationInput["vibrate"]가 존재하고항목32비트 부호 없는 정수리스트라면, notificationOptions["vibrate"] 에 notificationInput["vibrate"]을 설정합니다.

  20. notificationInput["timestamp"]가 존재하고 64비트 부호 없는 정수라면, notificationOptions["timestamp"] 에 notificationInput["timestamp"]을 설정합니다.

  21. notificationInput["renotify"]가 존재하고 불리언이면, notificationOptions["renotify"] 에 notificationInput["renotify"]을 설정합니다.

  22. notificationInput["silent"]가 존재하고 불리언이면, notificationOptions["silent"] 에 notificationInput["silent"]을 설정합니다.

  23. notificationInput["requireInteraction"]가 존재하고 불리언이면, notificationOptions["requireInteraction"] 에 notificationInput["requireInteraction"]을 설정합니다.

  24. notificationInput["data"]가 존재하면, notificationOptions["data"] 에 Infra 값을 JSON 호환 JavaScript 값으로 변환한 결과를 설정합니다.

  25. notificationInput["actions"]가 존재하고 리스트라면:

    1. notificationActions를 « »로 합니다.

    2. actionInput에 대해 notificationInput["actions"]:

      1. actionInput["action"]이 존재하지 않거나 문자열이 아니면 continue.

      2. actionInput["title"]이 존재하지 않거나 문자열이 아니면 continue.

      3. actionInput["navigate"]이 존재하지 않거나 문자열이 아니면 continue.

      4. actionNavigateactionInput["navigate"](변환됨)으로 합니다.

      5. notificationActionNotificationAction 딕셔너리로, «[ "action" → actionInput["action"], "title" → actionInput["title"], "navigate" → actionNavigate ]»로 합니다.

      6. actionInput["icon"]이 존재하고 문자열이면 notificationAction["icon"] 에 actionInput["icon"](변환됨)을 설정합니다.

      7. Append notificationActionnotificationActions에 추가합니다.

    3. notificationOptions["actions"] 에 notificationActions을 설정합니다.

  26. notification알림 생성의 결과로, notificationTitle, notificationOptions, origin, baseURL, fallbackTimestamp를 인자로 하여 생성합니다. 예외가 발생하면 실패를 반환합니다.

  27. notificationnavigation URL이 null이면 실패를 반환합니다.

  28. notificationactionsnavigation URL 중 하나라도 null이면 실패를 반환합니다.

  29. mutable을 false로 합니다.

  30. message["mutable"]가 존재하고 불리언이면, mutablemessage["mutable"]로 설정합니다.

  31. (notification, mutable)을 반환합니다.

3.4 푸시 구독

푸시 구독은 웹 애플리케이션을 대신하여 사용자 에이전트푸시 서비스 사이에 설정되는 메시지 전달 컨텍스트입니다.

푸시 구독에는 scope가 연관되어 있습니다. 이는 URL입니다.

푸시 구독scopepath리스트이며 크기가 1이고, scopepath[0]가 빈 문자열일 때 window-accessible scope를 가진 것으로 간주됩니다.

Note

즉, path 구성요소가 URL에서 "/"로 직렬화됩니다.

푸시 구독에는 푸시 엔드포인트가 연관되어 있습니다. 이는 반드시 푸시 서비스에서 노출되는 절대 URL이어야 하며, 애플리케이션 서버푸시 메시지를 전송할 수 있는 곳입니다. 푸시 엔드포인트반드시 푸시 구독을 고유하게 식별해야 합니다.

푸시 구독가질 수 있습니다 구독 만료 시간이 연관되어 있습니다. 설정된 경우, 이 값은 반드시 1970년 1월 1일 00:00:00 UTC부터 밀리초로 구독이 비활성화되는 시점을 나타내야 합니다. 사용자 에이전트가능하면 구독이 만료되기 전에 푸시 구독을 갱신해야 합니다.

푸시 구독에는 [RFC8291]에 따라 P-256 ECDH 키 페어와 인증 시크릿에 대한 내부 슬롯이 있습니다. 이러한 슬롯은 푸시 구독을 생성할 때 반드시 채워져야 합니다.

사용자 에이전트가 어떠한 이유로든 푸시 구독의 키를 변경해야 하고, 푸시 구독연관된 서비스 워커 등록이 null이 아니면, 반드시 푸시 구독을 갱신해야 합니다.

푸시 구독 생성을 하려면, PushSubscriptionOptionsInit optionsDictionary가 주어졌을 때:

  1. subscription을 새로운 PushSubscription으로 생성합니다.
  2. options를 새롭게 생성된 PushSubscriptionOptions 객체로 만들고, optionsDictionary의 해당 멤버와 값으로 초기화합니다.
  3. subscriptionoptions 속성을 options로 설정합니다.
  4. 새로운 P-256 ECDH 키 페어를 생성합니다 [ANSI-X9-62]. 개인 키는 subscription의 내부 슬롯에 저장합니다. 이 값은 애플리케이션에 노출되어서는 안 됩니다. 공개 키도 내부 슬롯에 저장되며, getKey() 메서드에 "p256dh"를 인자로 넘겨 호출하여 가져올 수 있습니다.
  5. 새로운 인증 시크릿(옥텟 시퀀스)을 생성합니다. 이는 [RFC8291]에 정의된 방식입니다. 인증 시크릿은 subscription의 내부 슬롯에 저장합니다. 이 키는 getKey() 메서드에 "auth"를 인자로 넘겨 호출하여 가져올 수 있습니다.
  6. 새로운 푸시 구독을 요청합니다. optionsapplicationServerKey 속성이 설정되어 있다면 포함합니다. 예외가 발생하면 예외를 다시 던집니다.
  7. 푸시 구독 요청이 성공적으로 완료되면:
    1. subscriptionendpoint 속성을 푸시 구독푸시 엔드포인트로 설정합니다.
    2. 푸시 구독에서 제공된 경우, subscriptionexpirationTime을 설정합니다.
  8. subscription을 반환합니다.

3.4.1 서비스 워커 등록과의 관계

푸시 구독연관된 서비스 워커 등록서비스 워커 등록 중에서 scope URL동일푸시 구독scope를 가진 경우 해당 서비스 워커 등록입니다. 없다면 null입니다.

Note

푸시 구독연관된 서비스 워커 등록이 null이 될 수 있는 경우는 window-accessible scope를 가진 경우뿐입니다.

반대로, 서비스 워커 등록연관된 푸시 구독푸시 구독scope동일한 서비스 워커 등록scope URL을 가진 경우 해당 푸시 구독입니다. 없다면 null입니다.

3.4.2 구독 갱신

사용자 에이전트 또는 푸시 서비스선택적으로 리프레시를 수행할 수 있습니다. 이때 푸시 구독연관된 서비스 워커 등록이 null이 아닐 때, 예를 들어 일정 기간이 지난 경우 언제든지 리프레시 할 수 있습니다.

이런 일이 발생하면, 사용자 에이전트반드시 현재 푸시 구독을 생성할 때 제공된 PushSubscriptionOptions푸시 구독 생성 절차를 실행하고, 새 푸시 구독scope를 원래 구독의 scope로 설정해야 합니다. 새 푸시 구독은 원래 구독과는 다른 키 쌍을 가져야 합니다.

성공적으로 완료되면, 사용자 에이전트반드시 "pushsubscriptionchange" 이벤트를 발생시켜야 하며, 서비스 워커 등록registration으로, 기존 푸시 구독을 나타내는 PushSubscription 인스턴스를 oldSubscription으로, 새 푸시 구독을 나타내는 PushSubscription 인스턴스를 newSubscription으로 전달합니다.

애플리케이션 서버에 변경 사항이 전파될 시간을 허용하기 위해, 사용자 에이전트선택적으로 리프레시 이후 잠시 동안 이전 푸시 구독에 대한 메시지를 계속 수신할 수 있습니다. 리프레시된 푸시 구독에 대해 메시지가 수신되면, 모든 이전 푸시 구독반드시 비활성화되어야 합니다.

사용자 에이전트푸시 구독을 리프레시할 수 없는 경우, 가능하면 주기적으로 갱신을 재시도해야 합니다. 푸시 구독을 더 이상 사용할 수 없게 되면(예: 만료된 경우), 사용자 에이전트반드시 "pushsubscriptionchange" 이벤트를 발생시키고, 서비스 워커 등록registration으로, 비활성화되는 푸시 구독을 나타내는 PushSubscription 인스턴스를 oldSubscription으로, newSubscription에는 null을 전달해야 합니다.

3.4.3 구독 비활성화

푸시 구독비활성화될 때, 사용자 에이전트푸시 서비스 모두는 반드시 저장된 모든 상세 정보를 삭제해야 합니다. 이후 이 푸시 구독에 대한 푸시 메시지절대 전달되어서는 안 됩니다.

푸시 구독window-accessible scope가 없으면 연결된 service worker registration이 등록 취소될 때 반드시 비활성화되어야 하지만, 푸시 구독은 더 일찍 비활성화될 수 있습니다.

참고

푸시 구독window-accessible scope가 없으면 service worker registration이 지워질 때 제거됩니다.

3.5 푸시 서비스

푸시 서비스라는 용어는 애플리케이션 서버가 웹 애플리케이션에 푸시 메시지를 보낼 수 있게 하는 시스템을 의미합니다. 푸시 서비스는 자신이 관리하는 푸시 엔드포인트 또는 엔드포인트를 해당 푸시 구독에 제공합니다.

사용자 에이전트푸시 서비스에 연결하여 푸시 구독을 생성합니다. 사용자 에이전트선택적으로 사용 가능한 푸시 서비스의 선택을 제한할 수 있습니다. 그 이유는 서비스 가용성(특정 국가나 네트워크, 직장 환경 등에서 방화벽에 의해 서비스가 차단되는 경우 포함), 신뢰성, 배터리 수명에 미치는 영향, 특정 푸시 서비스로 메타데이터를 유도하거나 회피하는 계약 등 성능 관련 우려가 있을 수 있습니다.

3.6 권한

Push API는 강력한 기능이며, 이름"push"로 식별됩니다.

Permissions 명세와의 통합을 위해, 이 명세에서는 PushPermissionDescriptor permission descriptor type을 정의합니다.

WebIDLdictionary PushPermissionDescriptor : PermissionDescriptor {
  boolean userVisibleOnly = false;
};

userVisibleOnlyuserVisibleOnly와 동일한 의미를 가집니다.

{name: "push", userVisibleOnly: false}더 강력한 권한이며, {name: "push", userVisibleOnly: true}보다 우선합니다.

4. 보안 및 개인정보 보호 고려사항

푸시 메시지의 내용은 암호화됩니다 [RFC8291] 그러나 푸시 서비스애플리케이션 서버사용자 에이전트에게 푸시 구독을 통해 보낸 메시지의 메타데이터에는 여전히 노출됩니다. 여기에는 메시지의 타이밍, 빈도, 크기가 포함됩니다. 푸시 서비스를 변경하는 것 외에는(사용자 에이전트가 이를 허용하지 않을 수 있음), 알려진 유일한 완화책은 메시지의 크기를 패딩으로 늘리는 것입니다.

푸시 메시지가 웹 애플리케이션과 동일 출처의 애플리케이션 서버에 의해 전송되었다는 보장은 없습니다. 애플리케이션 서버푸시 구독을 사용하는 데 필요한 정보를 제3자에게 자체 재량으로 공유할 수 있습니다.

다음 요구사항들은 사용자의 개인정보와 보안을 최대한 보호하기 위한 것이며, 그 목표를 충족하는 범위 내에서 application server와 사용자 간의 통신의 무결성을 보호하기 위한 것입니다.

Push API는 개발자가 제공한 이벤트 핸들러를 실행하기 위해 service worker registration과 연결된 Service Worker를 깨워야 할 수 있습니다. 이는 네트워크 트래픽과 같은 리소스 사용을 야기할 수 있으며, user agent는 이러한 리소스 사용을 SHOULD 해당 push subscription을 생성한 웹 애플리케이션에 귀속시켜야 합니다.

user agent는 권한을 취득하거나 권한 상태를 결정할 때, PushSubscriptionOptions를 고려할 MAY 있습니다.

권한이 취소되면, user agent는 해당 권한으로 생성된 구독에 대해 MAY "pushsubscriptionchange" 이벤트를 발생시키고, service worker registrationregistration으로, PushSubscription 인스턴스를 oldSubscription으로, nullnewSubscription으로 전달합니다. user agentMUST 영향을 받은 구독을 병렬로 deactivate 해야 합니다.

push endpointMUST NOT 사용자의 기기, 신원 또는 위치와 같은 정보를 push service 외의 행위자가 추론할 수 있게 노출해서는 안 됩니다. 구체적인 요구사항은 [RFC8030]의 개인정보 고려사항을 참조하세요.

deactivatedpush subscriptionpush endpoint는 새로운 push subscription에 재사용되어서는 MUST NOT 합니다. 이는 사용자가 삭제할 수 없는 영구 식별자의 생성을 방지하고, 한 push subscription의 정보를 재사용하여 다른 push subscriptionpush messages를 보내는 것을 방지합니다.

5. 푸시 프레임워크

이 섹션은 규범적이지 않습니다.

푸시 메시지애플리케이션 서버에서 웹 애플리케이션으로 다음과 같이 전송됩니다:

이 전체 프레임워크는 애플리케이션 서버의 이벤트에 대응하여 Service Worker를 활성화할 수 있게 해줍니다. 해당 이벤트에 대한 정보는 푸시 메시지에 포함될 수 있으며, 이를 통해 웹 애플리케이션은 네트워크 요청 없이도 적절하게 반응할 수 있습니다.

아래 코드와 다이어그램은 Push API의 가상 사용 예를 보여줍니다.

5.1 예시

이 섹션은 규범적이지 않습니다.

// https://example.com/serviceworker.js
this.onpush = event => {
  console.log(event.data);
  // 여기에서 데이터를 IndexedDB에 저장하거나, 열린 윈도우에 보내거나, 알림을 표시할 수 있습니다.
}

// https://example.com/webapp.js
// 비동기 함수 내부...
try {
  const serviceWorkerRegistration = await navigator.serviceWorker.register(
    "serviceworker.js"
  );
  const pushSubscription = await serviceWorkerRegistration.pushManager.subscribe();
  // 애플리케이션 서버에 필요한 푸시 구독 정보가
  // 이제 사용 가능하며, 예를 들어 XMLHttpRequest로 서버에 전송할 수 있습니다.
  console.log(pushSubscription.endpoint);
  console.log(pushSubscription.getKey("p256dh"));
  console.log(pushSubscription.getKey("auth"));
} catch (err) {
  // 프로덕션 환경에서는 에러 정보를
  // 애플리케이션 서버에 보고하는 것도 의미 있을 수 있습니다.
  console.log(error);
}

5.2 시퀀스 다이어그램

이 섹션은 규범적이지 않습니다.

구독, 푸시 메시지 전달 및 구독 해지의 예시 이벤트 흐름
그림 1 구독, 푸시 메시지 전달 및 구독 해지의 예시 이벤트 흐름

5.3 푸시 서비스 사용

PushSubscription에 포함된 필드는 애플리케이션 서버푸시 메시지를 보내는 데 필요한 모든 정보를 담고 있습니다. Push API와 호환되는 푸시 서비스는 웹 푸시 프로토콜을 준수하는 푸시 엔드포인트를 제공합니다. 이러한 파라미터와 속성에는 다음과 같은 항목이 포함됩니다:

6. PushManagerAttribute 믹스인

본 명세는 WindowServiceWorkerRegistration 객체에 대해 PushManagerAttribute 믹스인을 확장합니다. [HTML] [SERVICE-WORKERS]

WebIDL[SecureContext]
interface mixin PushManagerAttribute {
  readonly attribute PushManager pushManager;
};
Window includes PushManagerAttribute;
ServiceWorkerRegistration includes PushManagerAttribute;

WindowServiceWorkerRegistration 객체는 연관된 PushManager 객체를 가집니다.

pushManager getter의 단계는 this의 연관된 PushManager 객체를 반환하는 것입니다.

Window 객체에서 PushManagerservice worker registration은 null입니다.

ServiceWorkerRegistration 객체에서 PushManagerservice worker registration서비스 워커 등록 객체가 나타내는 ServiceWorkerRegistration 객체입니다.

7. PushManager 인터페이스

PushManager 인터페이스는 푸시 서비스에 접근하는 작업을 정의합니다.

WebIDL[Exposed=(Window,Worker), SecureContext]
interface PushManager {
  [SameObject] static readonly attribute FrozenArray<DOMString> supportedContentEncodings;

  Promise<PushSubscription> subscribe(optional PushSubscriptionOptionsInit options = {});
  Promise<PushSubscription?> getSubscription();
  Promise<PermissionState> permissionState(optional PushSubscriptionOptionsInit options = {});
};

PushManager는 연관된 서비스 워커 등록을 가지며, 이는 null이거나 서비스 워커 등록입니다.

supportedContentEncodings 속성은 푸시 메시지 페이로드 암호화에 사용할 수 있는 지원 콘텐츠 인코딩 시퀀스를 노출합니다. 콘텐츠 인코딩은 푸시 서비스로 푸시 메시지를 요청할 때 Content-Encoding 헤더 필드를 통해 지정됩니다.

사용자 에이전트반드시 [RFC8291]에서 정의된 aes128gcm 콘텐츠 인코딩을 지원해야 하며, 선택적으로 하위 호환성을 위해 초안의 이전 버전에 정의된 콘텐츠 인코딩도 지원할 수 있습니다.

7.1 subscribe() 메서드

subscribe() 메서드 단계는 다음과 같습니다:

  1. promise새로운 promise를 할당합니다.
  2. globalthis관련 글로벌 객체를 할당합니다.
  3. scope를 null로 설정합니다.
  4. registration을 null로 설정합니다.
  5. 만약 this서비스 워커 등록이 null이라면:
    1. scope기본 URL 파서로 "/"와 global연관 DocumentURL을 넘겨 실행한 결과로 설정합니다.
  6. 그 외의 경우:
    1. Assert: this서비스 워커 등록서비스 워커 등록입니다.
    2. registrationthis서비스 워커 등록으로 설정합니다.
  7. 다음 단계들을 병렬로 실행합니다:
    Note: 사용자 에이전트마다 검증 순서가 다를 수 있음
    1. 만약 scope가 실패이거나, URLscheme이 "https"가 아니라면, 글로벌 태스크를 큐에 넣어 promise를 reject하고 "NotAllowedError" DOMException을 반환합니다.
    2. 만약 options 인자의 userVisibleOnly 값이 false이고, 사용자 에이전트가 반드시 true여야 한다면, 글로벌 태스크를 큐에 넣어 promise를 reject하고 "NotAllowedError" DOMException을 반환합니다.
    3. 만약 options 인자에 applicationServerKey 멤버에 non-null 값이 포함되어 있지 않고, 푸시 서비스가 이를 필요로 한다면, 글로벌 태스크를 큐에 넣어 promise를 reject하고 "NotSupportedError" DOMException을 반환합니다.
    4. 만약 options 인자에 applicationServerKey 속성에 non-null 값이 포함되어 있다면:
      1. 만약 optionsapplicationServerKeyDOMString이라면, 그 값을 base64url로 디코딩한 옥텟 시퀀스의 ArrayBuffer로 설정합니다 [RFC7515].
      2. 디코딩에 실패하면, 글로벌 태스크를 큐에 넣어 promise를 reject하고 "InvalidCharacterError" DOMException을 반환하고 단계 종료.
      3. 반드시 optionsapplicationServerKey 값이 P-256 곡선 상의 올바른 점이어야 합니다. 만약 값이 올바르지 않다면, 글로벌 태스크를 큐에 넣어 promise를 reject하고 "InvalidAccessError" DOMException을 반환하고 단계 종료.
    5. subscription을 null로 설정합니다.
    6. 만약 scope가 null이 아니라면:
      1. 만약 푸시 구독window-accessible scope를 가지고 있고, 그 scopeequals scope라면, subscription을 해당 푸시 구독으로 설정합니다.
    7. 그 외의 경우:
      1. Assert: registration은 null이 아님.
      2. 만약 registrationactive worker가 null이면, 글로벌 태스크를 큐에 넣어 promise를 reject하고 "InvalidStateError" DOMException을 반환하고 단계 종료.
      3. 만약 registration연관된 푸시 구독이 null이 아니라면, subscriptionregistration연관된 푸시 구독으로 설정합니다.
      4. scoperegistrationscope URL로 설정합니다.
    8. permission"push" 사용 권한 요청 결과를 할당합니다.
    9. 만약 permission이 "denied"라면, 글로벌 태스크를 큐에 넣어 promise를 reject하고 "NotAllowedError" DOMException을 반환하고 단계 종료.
    10. 만약 subscription이 null이 아니라면:
      1. subscription에 오류가 있으면, 글로벌 태스크를 큐에 넣어 promise를 reject하고 "AbortError" DOMException을 반환하고 단계 종료.
      2. options 인자와 subscriptionoptions 속성을 비교합니다. BufferSource 값의 내용은 참조가 아닌 동등성으로 비교합니다.
      3. options의 어떤 속성이 subscription에 저장된 값과 다르다면, 글로벌 태스크를 큐에 넣어 promise를 reject하고 "InvalidStateError" DOMException을 반환하고 단계 종료.
      4. 글로벌 태스크를 큐에 넣어 promise를 resolve하고 subscription을 반환하고 단계 종료.
    11. Assert: subscription이 null이고 scopeURL입니다.
    12. subscription푸시 구독 생성 결과로 설정합니다. 구독 생성 중 exception이 발생하면 글로벌 태스크를 큐에 넣어 promise를 reject하고 해당 exception을 반환하고 단계 종료.
    13. subscriptionscopescope로 설정합니다.
    14. 글로벌 태스크를 큐에 넣어 promise를 resolve하고 PushSubscription을 반환하고 단계 종료.
  8. promise를 반환합니다.

getSubscription() 메서드 단계는 다음과 같습니다:

  1. promise새로운 promise를 할당합니다.
  2. globalthis관련 글로벌 객체를 할당합니다.
  3. windowScope를 null로 설정합니다.
  4. registration을 null로 설정합니다.
  5. 만약 this서비스 워커 등록이 null이라면, windowScope기본 URL 파서로 "/"와 global연관 DocumentURL을 넘겨 실행한 결과로 설정합니다.
  6. 그 외의 경우:
    1. Assert: this서비스 워커 등록서비스 워커 등록입니다.
    2. registrationthis서비스 워커 등록으로 설정합니다.
  7. 다음 단계들을 병렬로 실행합니다:
    1. subscription을 null로 설정합니다.
    2. 만약 windowScope가 null이 아니라면:
      1. 푸시 구독scopewindowScope인 것이 있다면, subscription을 해당 푸시 구독으로 설정합니다.
    3. 그 외의 경우:
      1. Assert: registration은 null이 아님.
      2. 만약 registration연관된 푸시 구독이 null이 아니라면, subscriptionregistration연관된 푸시 구독으로 설정합니다.
    4. 만약 subscription이 null이면, promise를 null로 resolve합니다.
    5. subscription에 오류가 있으면, promise를 "AbortError" 이름의 DOMException으로 reject하고 단계 종료.
    6. PushSubscription에 해당하는 subscription으로 promise를 resolve합니다.
  8. promise를 반환합니다.

permissionState() 메서드는 호출 시 반드시 다음의 단계를 실행합니다:

  1. promise새로운 promise를 할당합니다.
  2. promise를 반환하고, 다음 단계는 비동기로 계속합니다.
  3. descriptorPermissionDescriptor를 새로 생성하여 name을 "push"로 초기화합니다.
  4. statedescriptorpermission state를 할당합니다.
  5. promisestate로 resolve합니다.

푸시 서비스를 사용할 권한은 영속적일 수 있으며, 유효한 권한이 있으면 이후 구독에 대해 다시 확인할 필요가 없습니다.

권한이 필요하다면 반드시 subscribe() 메서드를 호출하여 요청해야 합니다.

7.2 PushSubscriptionOptions 인터페이스

WebIDL[Exposed=(Window,Worker), SecureContext]
interface PushSubscriptionOptions {
  readonly attribute boolean userVisibleOnly;
  [SameObject] readonly attribute ArrayBuffer? applicationServerKey;
};

userVisibleOnly 속성은 get 시 초기화된 값을 반환합니다.

applicationServerKey 속성은 get 시 초기화된 값을 반환합니다.

applicationServerKey 값이 존재할 경우 반드시 P-256 타원 곡선 [DSS]의 점을 포함해야 하며, [ANSI-X9-62] Annex A(즉, 0x04로 시작하는 65바이트, 비압축 형태)로 인코딩되어야 합니다. DOMString으로 제공되는 경우 반드시 base64url 인코딩 [RFC7515]을 사용해야 합니다.

사용자 에이전트는 푸시 서비스가 운영상 필요로 한다면 applicationServerKey 가 존재하지 않을 때 구독 시도를 거부할 수 있습니다.

applicationServerKey 값은 반드시 메시지 암호화에 사용되는 값과 달라야 합니다 [RFC8291].

7.3 PushSubscriptionOptionsInit 딕셔너리

WebIDLdictionary PushSubscriptionOptionsInit {
  boolean userVisibleOnly = false;
  (BufferSource or DOMString)? applicationServerKey = null;
};

userVisibleOnly 멤버가 true로 설정된 경우, 해당 푸시 구독사용자에게 가시적으로 효과가 나타나는 푸시 메시지(예: 웹 알림 표시)만을 위해 사용됨을 나타냅니다. [NOTIFICATIONS]

PushSubscriptionOptionsInit푸시 구독과 연관된 추가 옵션을 나타냅니다. 사용자 에이전트사용자에게 명시적 권한을 요청할 때 이러한 옵션을 고려할 수 있습니다. 옵션이 고려되면, 사용자 에이전트해당 옵션을 수신되는 푸시 메시지에 반드시 적용해야 합니다.

이러한 옵션은 선택사항이며, 사용자 에이전트일부 옵션만 지원할 수 있습니다. 사용자 에이전트지원하지 않는 옵션을 노출해서는 안 됩니다.

한 번 설정된 푸시 구독의 옵션은 변경할 수 없습니다. 기존 푸시 구독unsubscribe를 통해 해지한 뒤, 새로운 옵션으로 푸시 구독을 만들 수 있습니다.

applicationServerKey 멤버는 사용자 에이전트푸시 서비스푸시 구독을 설정할 때 사용됩니다. applicationServerKey 옵션에는 애플리케이션 서버용 타원 곡선 공개키가 포함되어 있습니다. 이 키는 애플리케이션 서버푸시 구독푸시 메시지를 전송할 때 자신을 인증하기 위해 사용합니다 ([RFC8292]에 정의됨). 푸시 서비스는 해당 개인키로 인증 토큰을 생성하지 않은 푸시 메시지를 모두 거부합니다.

8. PushSubscription 인터페이스

PushSubscription 객체는 푸시 구독을 나타냅니다.

WebIDL[Exposed=(Window,Worker), SecureContext]
interface PushSubscription {
  readonly attribute USVString endpoint;
  readonly attribute EpochTimeStamp? expirationTime;
  [SameObject] readonly attribute PushSubscriptionOptions options;
  ArrayBuffer? getKey(PushEncryptionKeyName name);
  Promise<boolean> unsubscribe();

  PushSubscriptionJSON toJSON();
};

dictionary PushSubscriptionJSON {
  USVString endpoint;
  EpochTimeStamp? expirationTime = null;
  record<DOMString, USVString> keys;
};

endpoint 속성을 가져올 때, 사용자 에이전트반드시 푸시 엔드포인트를 해당 푸시 구독과 연결된 값으로 반환해야 합니다. 사용자 에이전트입력값에 따라 분기하지 않는(즉, 상수 시간) 직렬화 방식을 사용해야 합니다.

expirationTime 속성을 가져올 때, 사용자 에이전트반드시 해당 푸시 구독과 연결된 구독 만료 시간을 반환해야 하며, 값이 없으면 null을 반환합니다.

options 속성을 가져올 때, 사용자 에이전트반드시 해당 푸시 구독과 연결된 옵션을 나타내는 PushSubscriptionOptions 객체를 반환해야 합니다.

getKey() 메서드는 메시지 암호화 및 인증을 위한 키 데이터를 가져옵니다. getKey()가 호출되면 다음 절차를 따릅니다:

  1. name 인자로 지정된 이름에 해당하는 내부 슬롯을 찾습니다.
  2. 해당 슬롯이 없으면 null을 반환합니다.
  3. key 변수를 새로 생성한 ArrayBuffer 인스턴스로 초기화합니다.
  4. 내부 슬롯에 비대칭 키 쌍이 있으면, key에 해당 키 쌍의 공개키 직렬화 값을 설정합니다. 직렬화 포맷은 해당 이름을 정의한 명세를 따릅니다. 예를 들어 [RFC8291]에서는 "p256dh" 공개키가 [ANSI-X9-62] Annex A에서 정의한 비압축 포맷(즉, 0x04로 시작하는 65 바이트 시퀀스)으로 인코딩됨을 명시합니다.
  5. 그 외에 내부 슬롯에 대칭키가 있으면, key에 해당 슬롯의 값 복사본을 설정합니다. 예를 들어 auth 파라미터는 사용자 에이전트애플리케이션 서버가 보내는 메시지를 인증하는 데 사용하는 바이트 시퀀스를 포함합니다.
  6. key를 반환합니다.

"p256dh" 및 "auth" 키는 반드시 지원되어야 하며, 값은 반드시 [RFC8291]에 따라 사용자 에이전트가 수신된 푸시 메시지를 복호화하는 데 필요한 값이어야 합니다.

unsubscribe() 메서드는 호출 시 반드시 다음 단계를 실행합니다:

  1. promise새로운 promise로 만듭니다.
  2. promise를 반환하고, 아래 단계는 비동기로 계속 진행합니다.
  3. 푸시 구독이 이미 비활성화된 경우, promisefalse로 resolve하고 단계를 종료합니다.
  4. 다음 단계를 병렬로 실행합니다:
    1. 비활성화 푸시 구독. 사용자 에이전트반드시 더 이상 푸시 메시지푸시 구독에 대해 전달하지 않아야 합니다.

      만약 사용자 에이전트푸시 서비스비활성화를 요청하지 못한 경우 (예: 네트워크 오류), 푸시 구독에 대한 요청을 푸시 서비스에 합리적인 시간 동안 다시 시도해야 합니다.

  5. promisetrue로 resolve합니다.

toJSON() 메서드는 호출 시 반드시 다음 단계를 실행합니다:

  1. json을 새 PushSubscriptionJSON 딕셔너리로 생성합니다.
  2. json["endpoint"]에 endpoint 속성 가져오기 결과를 설정합니다.
  3. json["expirationTime"]에 expirationTime 속성 가져오기 결과를 설정합니다.
  4. keys를 빈 record<DOMString, USVString> 인스턴스로 만듭니다.
  5. 내부 슬롯의 키들에 해당하는 식별자 i 각각에 대해(이름 순서대로):
    1. 내부 슬롯이 비대칭 키 쌍에 해당하면, b를 키 이름 i에 대해 공개키를 인코딩한 값으로, 해당 키 이름에 정의된 인코딩을 사용해 만듭니다(getKey() 참고).
    2. 그 외에는 bgetKey로 반환된 값으로 설정합니다.
    3. sbUSVString으로 변환한 뒤, URL-safe base64 인코딩(패딩 없음) [RFC4648] 결과로 설정합니다. 사용자 에이전트b에 따라 분기하지 않는 직렬화 방식을 사용해야 합니다.
    4. keys[i]에 s를 설정합니다.
  6. json["keys"]에 keys를 설정합니다.
  7. json을 반환합니다.

PushSubscriptionJSON 딕셔너리는 JSON 타입PushSubscription을 나타냅니다. ECMAScript에서는 JSON.stringify() 함수로 JSON 문자열로 변환할 수 있습니다.

keys 레코드는 PushEncryptionKeyName 값마다 해당 값을 URL-safe base64로 인코딩한 표현을 포함합니다 [RFC4648].

PushSubscription의 옵션은 직렬화되지 않음을 참고하세요.

8.1 PushEncryptionKeyName 열거형

푸시 메시지 암호화에 사용되는 암호화 키는 getKey() 메서드 또는 PushSubscription의 직렬화기를 통해 웹 애플리케이션에 제공됩니다. 각 키는 PushEncryptionKeyName 열거형의 값으로 이름이 지정됩니다.

WebIDLenum PushEncryptionKeyName {
  "p256dh",
  "auth"
};

p256dh 값은 [RFC8291]에 기술된 P-256 ECDH 디피-헬만 공개키를 가져오는 데 사용됩니다.

auth 값은 [RFC8291]에 기술된 인증 시크릿을 가져오는 데 사용됩니다.

9. PushMessageData 인터페이스

WebIDL[Exposed=ServiceWorker, SecureContext]
interface PushMessageData {
  ArrayBuffer arrayBuffer();
  Blob blob();
  Uint8Array bytes();
  any json();
  USVString text();
};

PushMessageData 객체는 생성될 때 bytes (바이트 시퀀스)를 갖으며, 이는 생성 시 설정됩니다.

arrayBuffer() 메서드는 ArrayBuffer 객체를 반환하며, 내용은 thisbytes입니다. ArrayBuffer 객체 생성 중 발생한 예외는 재throw됩니다.

blob() 메서드는 Blob 객체를 새로 반환하며, 내용은 thisbytes입니다.

bytes() 메서드는 Uint8Array 객체를 새로 반환하며, 내부적으로 ArrayBuffer의 내용은 thisbytes입니다. ArrayBuffer 객체 생성 중 발생한 예외는 재throw됩니다.

json() 메서드는 JSON 바이트를 자바스크립트 값으로 파싱한 결과를 thisbytes에 대해 반환합니다.

text() 메서드는 UTF-8 디코드 결과를 thisbytes에 대해 반환합니다.

object에서 바이트 시퀀스를 추출하려면 다음 단계를 실행합니다:

  1. bytes를 빈 바이트 시퀀스로 설정합니다.
  2. object의 타입에 따라 분기합니다:
    BufferSource
    bytesobject의 내용을 복사한 값으로 설정합니다.
    USVString
    objectutf-8 인코드 실행 결과를 bytes로 설정합니다.
  3. bytes를 반환합니다.

10. 이벤트

10.1 ServiceWorkerGlobalScope 인터페이스 확장

Service Worker 명세는 ServiceWorkerGlobalScope 인터페이스를 정의합니다 [SERVICE-WORKERS] 이 명세는 해당 인터페이스를 확장합니다.

WebIDL[Exposed=ServiceWorker, SecureContext]
partial interface ServiceWorkerGlobalScope {
  attribute EventHandler onpush;
  attribute EventHandler onpushsubscriptionchange;
};

onpush 속성은 이벤트 핸들러 IDL 속성이며, 해당 이벤트 핸들러 이벤트 타입은 "push"입니다. "push" 이벤트는 푸시 메시지푸시 구독에 수신되었음을 나타냅니다.

onpushsubscriptionchange 속성은 이벤트 핸들러 IDL 속성이며, 해당 이벤트 핸들러 이벤트 타입은 "pushsubscriptionchange"입니다.

10.2 PushEvent 인터페이스

WebIDL[Exposed=ServiceWorker, SecureContext]
interface PushEvent : ExtendableEvent {
  constructor(DOMString type, optional PushEventInit eventInitDict = {});
  readonly attribute PushMessageData? data;
  readonly attribute Notification? notification;
};

dictionary PushEventInit : ExtendableEventInit {
  PushMessageDataInit? data = null;
  Notification? notification = null;
};

typedef (BufferSource or USVString) PushMessageDataInit;

PushEvent 인터페이스 또는 해당 인터페이스를 상속하는 인터페이스의 constructor가 호출될 때, 통상적인 이벤트 생성 단계에 다음 단계를 추가합니다:

  1. eventInitDictdata 멤버가 없으면, 이벤트의 data 속성에 null을 할당하고 이 단계를 종료합니다.
  2. beventInitDict의 "data" 멤버에서 바이트 시퀀스를 추출한 결과로 설정합니다.
  3. 이벤트의 data 속성에 PushMessageData 인스턴스를 새로 생성하여, bytesb로 할당합니다.

data 속성은 초기화된 값을 반환해야 합니다.

notification 속성은 초기화된 값을 반환해야 합니다.

10.3 푸시 메시지 수신

사용자 에이전트푸시 서비스로부터 푸시 메시지를 받으면, 다음 단계를 반드시 실행해야 합니다.

  1. subscription푸시 메시지에 해당하는 활성 푸시 구독이라고 합시다.

  2. registrationsubscription연결된 서비스 워커 등록이라고 합시다.

  3. bytes를 null로 둡니다.

  4. 만약 푸시 메시지가 페이로드를 포함한다면:

    1. subscription과 연결된 키 쌍의 개인 키와 [RFC8291]에 설명된 프로세스를 사용하여 푸시 메시지의 페이로드를 해독합니다. 결과 바이트 시퀀스bytes를 설정합니다.

    2. 어떤 이유로든 푸시 메시지 페이로드를 해독할 수 없다면, 푸시 메시지를 승인하고 이 단계를 중단합니다.

      Note

      push 이벤트는 푸시 구독과 연결된 키 쌍을 사용하여 성공적으로 해독되지 않은 푸시 메시지에 대해 발생하지 않습니다.

  5. 만약 bytes가 null이 아니라면:

    1. baseURLsubscription범위라고 합시다.

    2. originbaseURLorigin이라고 합시다.

    3. fallbackTimestamp현재 Coarsened Wall Time이라고 합시다.

    4. declarativeResultbytes, origin, baseURL, 그리고 fallbackTimestamp이 주어진 선언적 푸시 메시지 파서를 실행한 결과라고 합시다.

    5. 만약 declarativeResult가 실패가 아니라면:

      1. notificationdeclarativeResult알림이라고 합시다.

      2. notification서비스 워커 등록registration으로 설정합니다.

      3. notificationShown을 false로 설정합니다.

      4. 만약 declarativeResultmutable이 true이고 registration이 null이 아니라면:

        1. resultregistration, null, 그리고 notification을 나타내는 새로운 Notification 객체가 주어진 푸시 이벤트 발생의 결과라고 합시다.

        2. 만약 result가 실패가 아니라면, notificationShownresult알림 표시 여부로 설정합니다.

      5. 만약 notificationShown이 false라면, notification이 주어진 알림 표시 단계를 실행합니다.

      6. 푸시 메시지를 승인하고 이 단계를 중단합니다.

  6. 만약 registration이 null이라면, 이 단계를 중단합니다.

  7. dataPushMessageData 객체로 둡니다. bytesbytes가 null이 아니면 bytes이고, 그렇지 않으면 null입니다.

  8. resultregistration, data, 그리고 null이 주어진 푸시 이벤트 발생의 결과라고 합시다.

  9. 만약 result가 실패이고 동일한 푸시 메시지서비스 워커 등록에 여러 번 실패적으로 전달되었다면, 푸시 메시지를 승인합니다.

  10. 만약 result가 실패가 아니라면, 푸시 메시지를 승인합니다.

푸시 이벤트 결과튜플이며, notification shown(불리언)을 포함합니다.

푸시 이벤트 발행서비스 워커 등록 registration, PushMessageData 객체 또는 null data, notification 또는 null notification이 주어졌을 때 실행합니다. 실패 또는 푸시 이벤트 결과를 반환합니다.

  1. notificationResult를 null로 설정합니다.

  2. registrationshowNotification() 성공 여부를 false로 설정합니다.

  3. "push"라는 기능적 이벤트 발행PushEvent 타입으로 registration에 대해 아래 속성으로 실행합니다:

    data
    data
    notification
    notification

    그리고 dispatchedEvent에 대해 다음 단계를 병렬로 실행합니다:

    1. dispatchedEventextend lifetime promises가 모두 resolve될 때까지 대기합니다.

    2. 성공적으로 resolve되지 않으면 notificationResult를 실패로 설정합니다.

    3. 그렇지 않으면 notificationResultregistrationshowNotification() 성공 여부로 설정합니다.

  4. notificationResult가 null이 아닐 때까지 대기합니다.

  5. notificationResult가 실패면, 실패를 반환합니다.

  6. Assert: notificationResult불리언입니다.

  7. (notificationResult)를 반환합니다.

푸시 메시지 인지pushMessage를 인자로 받으며, [RFC8030]에 따라 pushMessage의 수신을 인지합니다.

푸시 메시지 인지는 푸시 서비스가 메시지 전달을 중지하고 애플리케이션 서버에 성공을 보고하도록 합니다. 이렇게 하면 동일한 푸시 메시지푸시 서비스에 의해 무한 반복 전달되지 않습니다.

인지는 또한 애플리케이션 서버푸시 메시지가 성공적으로 전달되었다는 수신 확인을 잘못 받을 수 있음을 의미합니다. 따라서 여러 번 거부를 반드시 허용해야 하며, 최소 3회 시도를 허용하는 것이 좋습니다.

10.4 pushsubscriptionchange 이벤트

pushsubscriptionchange 이벤트는 푸시 구독에 대한 변경이 애플리케이션의 제어 외부에서 발생했음을 나타냅니다. 예를 들면 갱신, 철회, 손실 등이 해당됩니다.

"pushsubscriptionchange" 이벤트 발행서비스 워커 등록 registration, newSubscription, oldSubscription이 주어졌을 때, 사용자 에이전트가 "pushsubscriptionchange"라는 기능적 이벤트를 PushSubscriptionChangeEvent 타입으로 registration에 대해 아래 속성으로 발행합니다:

newSubscription
newSubscription
oldSubscription
oldSubscription
참고

새로운 푸시 구독의 세부 정보를 애플리케이션 서버로 보낼 때 [WEB-BACKGROUND-SYNC]와 같은 보다 안정적인 동기화 메커니즘을 사용하는 것을 고려하십시오. 사용자는 fetch가 실패할 수 있는 불안정한 네트워크 환경에 놓일 수 있습니다.

10.4.1 PushSubscriptionChangeEvent 인터페이스

WebIDL[Exposed=ServiceWorker, SecureContext]
interface PushSubscriptionChangeEvent : ExtendableEvent {
  constructor(DOMString type, optional PushSubscriptionChangeEventInit eventInitDict = {});
  readonly attribute PushSubscription? newSubscription;
  readonly attribute PushSubscription? oldSubscription;
};

newSubscription 속성은 get 시 초기화된 값을 반환합니다.

oldSubscription 속성은 get 시 초기화된 값을 반환합니다.

10.4.2 PushSubscriptionChangeEventInit 인터페이스

WebIDLdictionary PushSubscriptionChangeEventInit : ExtendableEventInit {
  PushSubscription newSubscription = null;
  PushSubscription oldSubscription = null;
};

newSubscription 멤버는 푸시 구독에 대한 세부 정보를 제공하며, 이는 pushsubscriptionchange 이벤트 호출 시 유효합니다. 예를 들어 웹 애플리케이션이 명시적 권한을 잃어 새 푸시 구독을 설정할 수 없는 경우 값은 null이 됩니다.

oldSubscription 멤버는 더 이상 사용 하면 안 되는 푸시 구독에 대한 세부 정보를 제공합니다. 예를 들어 부분적인 데이터베이스 손상으로 인해 사용자 에이전트가 전체 세부 정보 집합을 제공할 수 없는 경우 값은 null이 됩니다.

11. 접근성

Push API푸시 서비스에서 받은 데이터를 직접적으로 표시하는 방법을 제공하지 않습니다. 대신 Push API는 다른 API, 주로 Notifications API Standard에 의존하여 사용자가 정보를 확인할 수 있게 합니다. 따라서 Push API 자체에는 접근성 요구사항이 없습니다. 하지만 [NOTIFICATIONS] 등 명세에서는 알림을 접근성 있게 표시하는 방법에 대한 가이드라인을 제공합니다.

또한, 접근성 있는 인터페이스를 제공하려면 푸시 메시지가 전달할 수 있는 것보다 더 많은 정보를 전달해야 할 수도 있습니다. 푸시 메시지는 소량의 콘텐츠나 식별자를 전달하는 데 가장 적합합니다. 더 큰 리소스는 서버에서 따로 가져와야 합니다.

12. 준수

비규범 섹션뿐만 아니라, 이 명세에 포함된 모든 작성 지침, 도표, 예시, 비고는 모두 비규범적입니다. 이 명세의 그 밖의 모든 내용은 규범적입니다.

이 문서에서 MAY, MUST, MUST NOT, SHOULD, SHOULD NOT과 같은 핵심 단어는 BCP 14 [RFC2119] [RFC8174] 에서 설명된 대로 해석되어야 하며, 반드시 대문자로 표시된 경우에만 해당합니다.

이 명세는 하나의 제품에 적용되는 준수 기준을 정의합니다. 즉, 이 명세에 포함된 인터페이스를 구현하는 사용자 에이전트가 그 대상입니다.

A. IDL 색인

WebIDLdictionary PushPermissionDescriptor : PermissionDescriptor {
  boolean userVisibleOnly = false;
};

[SecureContext]
interface mixin PushManagerAttribute {
  readonly attribute PushManager pushManager;
};
Window includes PushManagerAttribute;
ServiceWorkerRegistration includes PushManagerAttribute;

[Exposed=(Window,Worker), SecureContext]
interface PushManager {
  [SameObject] static readonly attribute FrozenArray<DOMString> supportedContentEncodings;

  Promise<PushSubscription> subscribe(optional PushSubscriptionOptionsInit options = {});
  Promise<PushSubscription?> getSubscription();
  Promise<PermissionState> permissionState(optional PushSubscriptionOptionsInit options = {});
};

[Exposed=(Window,Worker), SecureContext]
interface PushSubscriptionOptions {
  readonly attribute boolean userVisibleOnly;
  [SameObject] readonly attribute ArrayBuffer? applicationServerKey;
};

dictionary PushSubscriptionOptionsInit {
  boolean userVisibleOnly = false;
  (BufferSource or DOMString)? applicationServerKey = null;
};

[Exposed=(Window,Worker), SecureContext]
interface PushSubscription {
  readonly attribute USVString endpoint;
  readonly attribute EpochTimeStamp? expirationTime;
  [SameObject] readonly attribute PushSubscriptionOptions options;
  ArrayBuffer? getKey(PushEncryptionKeyName name);
  Promise<boolean> unsubscribe();

  PushSubscriptionJSON toJSON();
};

dictionary PushSubscriptionJSON {
  USVString endpoint;
  EpochTimeStamp? expirationTime = null;
  record<DOMString, USVString> keys;
};

enum PushEncryptionKeyName {
  "p256dh",
  "auth"
};

[Exposed=ServiceWorker, SecureContext]
interface PushMessageData {
  ArrayBuffer arrayBuffer();
  Blob blob();
  Uint8Array bytes();
  any json();
  USVString text();
};

[Exposed=ServiceWorker, SecureContext]
partial interface ServiceWorkerGlobalScope {
  attribute EventHandler onpush;
  attribute EventHandler onpushsubscriptionchange;
};

[Exposed=ServiceWorker, SecureContext]
interface PushEvent : ExtendableEvent {
  constructor(DOMString type, optional PushEventInit eventInitDict = {});
  readonly attribute PushMessageData? data;
  readonly attribute Notification? notification;
};

dictionary PushEventInit : ExtendableEventInit {
  PushMessageDataInit? data = null;
  Notification? notification = null;
};

typedef (BufferSource or USVString) PushMessageDataInit;

[Exposed=ServiceWorker, SecureContext]
interface PushSubscriptionChangeEvent : ExtendableEvent {
  constructor(DOMString type, optional PushSubscriptionChangeEventInit eventInitDict = {});
  readonly attribute PushSubscription? newSubscription;
  readonly attribute PushSubscription? oldSubscription;
};

dictionary PushSubscriptionChangeEventInit : ExtendableEventInit {
  PushSubscription newSubscription = null;
  PushSubscription oldSubscription = null;
};

B. 감사의 글

편집자들은 Firefox OS 푸시 메시지 솔루션을 구현한 Mozilla 및 Telefónica Digital 팀에 감사를 표하며, 이 문서에 중요한 기술적 기여를 해주신 다음 분들께도 깊은 감사의 뜻을 전합니다: Antonio Amaya, Miguel García Arribas, Ben Bangert, Kit Cambridge, José Manuel Cantera, JR Conlin, Albert Crespell, Matt Gaunt, Phil Jenvey, Guillermo López, Nikhil Marathe, John Mellor, Pınar Özlen, Fernando R. Sela, Shijun Sun, Doug Turner.

C. 참고 문헌

C.1 규범적 참고 문헌

[ANSI-X9-62]
Public Key Cryptography for the Financial Services Industry, The Elliptic Curve Digital Signature Algorithm (ECDSA). ANSI. 2005. URL: https://webstore.ansi.org/RecordDetail.aspx?sku=ANSI+X9.62%3a2005
[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[DSS]
FIPS PUB 186-5: Digital Signature Standard (DSS). 미국 상무부/NIST. 2023년 2월 3일. 국가 표준. URL: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
[ecmascript]
ECMAScript Language Specification. Ecma International. URL: https://tc39.es/ecma262/multipage/
[encoding]
Encoding Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://encoding.spec.whatwg.org/
[fileapi]
File API. Marijn Kruisselbrink. W3C. 2024년 12월 4일. W3C 워킹 드래프트. URL: https://www.w3.org/TR/FileAPI/
[hr-time]
High Resolution Time. Yoav Weiss. W3C. 2024년 11월 7일. W3C 워킹 드래프트. URL: https://www.w3.org/TR/hr-time-3/
[HTML]
HTML Standard. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[NOTIFICATIONS]
Notifications API Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://notifications.spec.whatwg.org/
[Permissions]
Permissions. Marcos Caceres; Mike Taylor. W3C. 2025년 6월 24일. W3C 워킹 드래프트. URL: https://www.w3.org/TR/permissions/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. 1997년 3월. 현재 모범 사례. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC4648]
The Base16, Base32, and Base64 Data Encodings. S. Josefsson. IETF. 2006년 10월. 제안 표준. URL: https://www.rfc-editor.org/rfc/rfc4648
[RFC7231]
Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content. R. Fielding, Ed.; J. Reschke, Ed. IETF. 2014년 6월. 제안 표준. URL: https://httpwg.org/specs/rfc7231.html
[RFC7515]
JSON Web Signature (JWS). M. Jones; J. Bradley; N. Sakimura. IETF. 2015년 5월. 제안 표준. URL: https://www.rfc-editor.org/rfc/rfc7515
[RFC8030]
Generic Event Delivery Using HTTP Push. M. Thomson; E. Damaggio; B. Raymor, Ed. IETF. 2016년 12월. 제안 표준. URL: https://www.rfc-editor.org/rfc/rfc8030
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. 2017년 5월. 현재 모범 사례. URL: https://www.rfc-editor.org/rfc/rfc8174
[RFC8291]
Message Encryption for Web Push. M. Thomson. IETF. 2017년 11월. 제안 표준. URL: https://www.rfc-editor.org/rfc/rfc8291
[RFC8292]
Voluntary Application Server Identification (VAPID) for Web Push. M. Thomson; P. Beverloo. IETF. 2017년 11월. 제안 표준. URL: https://www.rfc-editor.org/rfc/rfc8292
[SERVICE-WORKERS]
Service Workers. Yoshisato Yanagisawa; Monica CHINTALA. W3C. 2025년 3월 6일. CRD. URL: https://www.w3.org/TR/service-workers/
[url]
URL Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://url.spec.whatwg.org/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/

C.2 참고용 참고 문헌

[Fetch]
Fetch Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://fetch.spec.whatwg.org/
[WEB-BACKGROUND-SYNC]
Web Background Synchronization. W3C. Draft Community Group Report. URL: https://wicg.github.io/background-sync/spec/
[WebSockets]
WebSockets Standard. Adam Rice. WHATWG. Living Standard. URL: https://websockets.spec.whatwg.org/