사용자가 활성 상태일 때 iOS 알림의 푸시 권한이 해제될 수 있습니다

iOS의 경우, 알림이 억제(suppressed)되면 푸시 권한을 잃을 수 있습니다. Discourse의 코드는 알림을 선택적으로 억제하도록 구성되어 있으며, 이 상황이 세 번 발생하면 푸시 권한이 취소됩니다.

다음은 푸시 알림 서비스 워커의 코드입니다.

이 코드에는 178번째 줄에 치명적인 버그가 있습니다. 해당 부분에서 서비스 워커는 사용자가 활성 상태(즉, 유휴 상태가 아님)인지 확인합니다. 사용자가 활성 상태인 경우, 알림을 표시하지 않고 푸시 이벤트가 false를 반환합니다.

(또한 payload.hide_when_active를 확인하지만, hide_when_active는 항상 true인 것으로 밝혀졌으며, 따라서 사용자가 활성 상태일 때 이 코드는 항상 false를 반환합니다.)

Apple은 조용한 푸시를 금지하며, 알림을 표시하지 않는 이벤트가 3회 발생하면 푸시 권한을 취소합니다

이는 Apple의 푸시 알림 규칙에 따라 허용되지 않습니다.

https://webkit.org/blog/12945/meet-web-push/

전력 및 프라이버시

WebKit 오픈소스 프로젝트와 Apple 모두 프라이버시를 근본적인 인권으로 간주합니다. 웹 플랫폼의 다른 특권 기능이 그렇듯이, 푸시 구독을 요청하려면 명시적인 사용자 제스처가 필요합니다. 또한 userVisibleOnly 플래그를 true로 설정하고, 푸시 메시지에 대한 응답으로 항상 알림을 표시하여 이 약속을 이행해야 합니다.

Web Push API는 조용한 백그라운드 런타임(silent background runtime)을 위한 초대가 아닙니다. 그렇게 하면 사용자의 신뢰를 위반할 뿐만 아니라 배터리 수명에 영향을 미치기 때문입니다.

userVisibleOnly 약속을 위반하면 푸시 구독이 취소됩니다.

(강조는 저의 것입니다)

이 내용은 Apple의 Web Push에 관한 WWDC 비디오의 9:57 지점에서 더 자세히 설명되어 있습니다.

https://developer.apple.com/videos/play/wwdc2022/10098/?time=596

푸시에 대해 항상 사용자 가시적으로(user visible) 만들겠다고 명시적으로 약속하고 있다는 점에 유의하십시오. JavaScript Push API 표준은 푸시에 대한 응답으로 조용한 JavaScript 런타임을 선택적으로 수용하지만, 대부분의 브라우저는 이를 지원하지 않습니다. Safari도 이를 지원하지 않습니다.

… 그리고 13:35 지점에서는 다음과 같이 설명합니다:

https://developer.apple.com/videos/play/wwdc2022/10098/?time=814

푸시 구독을 요청하는 방법을 보여준 코드에서 언급했듯이, 푸시가 사용자 가시적일 것임을 약속해야 합니다. 푸시 이벤트를 처리하는 것은 JavaScript가 조용한 백그라운드 런타임을 얻기 위한 초대가 아닙니다. 그렇게 하면 사용자의 신뢰와 배터리 수리에 모두 해를 끼치기 때문입니다. 푸시 이벤트를 처리할 때는 사실 Notification Center에 알림을 게시하는 것이 요구됩니다. 다른 브라우저들은 모두 푸시가 사용자 가시적이라는 약속을 위반하는 것에 대한 대응 조치를 갖추고 있으며, Safari도 마찬가지입니다. macOS Ventura의 베타 빌드에서, 적시에 알림을 게시하지 못하는 푸시 이벤트가 3회 발생하면 해당 사이트의 푸시 구독이 취소됩니다. 다시 권한 워크플로우를 거쳐야 합니다.

(강조는 저의 것입니다)

Apple은 알림을 닫은 후가 아니라 즉시 표시할 것을 권장합니다

Apple이 권장하는 코드는 11:39 지점의 다음과 같습니다:

https://developer.apple.com/videos/play/wwdc2022/10098/?time=699

self.addEventListener('push', (event) => {
    let pushMessageJSON = event.data.json();

    // Our server puts everything needed to show the notification
    // in our JSON data.
    event.waitUntil(self.registration.showNotification(pushMessageJSON.title, {
        body: pushMessageJSON.body,
        tag: pushMessageJSON.tag,
        actions: [{
            action: pushMessageJSON.actionURL,
            title: pushMessageJSON.actionTitle,
        }]
    }));
}

푸시를 구독할 때 JavaScript가 항상 사용자 가시적일 것임을 약속했다는 점을 기억하십시오. 즉, 각 푸시에 대한 응답으로 플랫폼 네이티브 알림을 항상 표시해야 합니다. 이는 푸시 이벤트 핸들러에서 가능한 한 초기에 수행하는 것이 가장 좋습니다.

Discourse의 코드는 권장되는 모범 사례를 따르지 않습니다. Discourse의 코드는 먼저 모든 알림을 닫은 다음, 비로소 알림을 표시합니다.

Discourse는 푸시 이벤트에 대한 응답으로 항상 showNotification을 호출해야 하며, 항상 가능한 한 빨리 이를 수행해야 합니다.

8개의 좋아요

@Falco / @featheredtoast 이 부분에 대해 어떻게 생각하시나요?

클로징 체크와 아이들 체크를 모두 제거하면 어떤 영향이 있을까요?

전체적으로 이 아이들 체크에 대해 전혀 확신이 없습니다. 혼란만 야기하는 것 같습니다.

최악의 경우 Android나 데스크톱(비 Safari)에서 이걸 필수로 요구해야 한다면 여기에 조건문을 추가할 수 있지만, 제 생각에는 여기서 코드를 그냥 제거하는 게 맞습니다.

훌륭한 디버깅이었습니다 @dfabulich :hugs:

3개의 좋아요

닫기 작업을 수행하는 것은 괜찮은 것 같네요. 다만 showNotification를 호출한 _이후_에 이루어져야 한다는 전제가 붙습니다.

… 하지만 솔직히 알림을 아예 닫는 것의 장점이 보이지 않습니다. 제 생각엔 그냥 알림 트레이에 쌓이게 두는 편이 나을 것 같습니다.

https://developer.mozilla.org/ko/docs/Web/API/Notification/close

참고: 이 API는 고정된 지연 시간 후에 알림이 화면에서 제거되도록 하는 목적으로만 사용해서는 안 됩니다. 이 메서드는 알림이 초기에 표시된 후 사용자가 더 이상 상호작용할 수 없도록 알림 트레이에서도 알림을 제거하기 때문입니다. 이 API의 유효한 사용 사례는 더 이상 관련이 없는 알림을 제거하는 것입니다(예: 메시징 앱에서 사용자가 웹페이지에서 알림을 이미 읽은 경우, 또는 음악 앱에서 다음 노래가 이미 재생되고 있는 경우).

3개의 좋아요

주말 동안 이 주제에 대해 읽어본 바로는, 푸시 이벤트 핸들러에서 async/promises를 사용할 수조차 없으며, 최대한 빨리 표시하지 않으면 Apple이 알림 권한을 박탈합니다.

이것은 @dfabulich의 말과도 일치합니다.

우리는 여기에서 너무 "지능적"인 것을 시도하고 있다고 생각합니다. 실제로 모든 확인을 제거하고 단순히 알림을 표시하는 것이 맞습니다.

6개의 좋아요

@dfabulich 님, 이걸 발견해 주셔서 정말 감사드립니다!

3개의 좋아요

이 건에 대한 PR을 올렸습니다.

@Falco / @featheredtoast 병합해도 될까요?

푸시 알림을 건너뛰어야 한다면 클라이언트가 아니라 서버에서 처리해야 합니다. 컬랩싱(폴딩) 로직은 애초에 다소 의문스러운 부분이 있었고, 앱들은 보통 그렇게 하지 않으니까요.

2개의 좋아요

오, 맞아요. 알림이 표시될 필요가 있을 때만 푸시 알림을 보내는 것이 훨씬 더 합리적이에요. 결국 다른 곳에서도 과도한 알림을 줄여야겠지만, 여기서는 이 변경 사항을 병합하는 데 문제가 없습니다 :+1:

알림 병합에 관해서는, 그런 기능이 사라지는 것이 아쉽긴 하지만… 이상적으로는 사용자가 로그인한 기기에서 알림을 처리해 나가는 동안, 다른 앱처럼 자동 삭제(auto dismissal)를 할 수 있으면 좋겠죠. 하지만 그렇게 하려면 좀 더 정교한 작업이 필요할 수 있어요. 거기서도 의도는 동일해서, 오래된 알림이 쌓여가는 상황을 원치 않는 거예요. 다시 말하지만, 지금은 괜찮지만, 이로 인해 얼마나 큰 불편을 겪는지 확인한 후에 나중에 다시 검토해야 해요.

2개의 좋아요