본문으로 건너뛰기

웹 푸시 알림

ChurchApps 웹 앱은 W3C 웹 푸시 API를 통해 푸시 알림을 전달합니다 -- 서버 측에서 Firebase Cloud Messaging이 사용하는 메커니즘과 동일하지만 FCM 대신 브라우저의 기본 PushManager를 통해 전달됩니다. 하나의 VAPID 키 쌍은 모든 소비자(B1 Admin, B1 App, 향후 PWA)를 포함합니다.

푸시가 발생하는 경우

MessagingApi는 Api/src/modules/messaging/helpers/NotificationHelper.ts를 통해 라우팅되는 3가지 상황에서 웹 푸시 메시지를 전달합니다:

  1. 그룹 / 콘텐츠 알림 -- 사용자가 따르거나 언급되는 스레드에 누군가가 회신합니다.
  2. 비공개 메시지 -- POST /messaging/privatemessages는 수신자의 등록된 기기로 푸시를 트리거합니다.
  3. 일반 알림 -- POST /messaging/notifications/create 또는 /ping에 직접 호출합니다.

푸시는 NotificationHelper의 에스컬레이션 래더에서 최후의 수단 계층입니다. 수신자가 관련 방에 활성 WebSocket 연결을 가지고 있으면(실시간 아키텍처 참고), 메시지를 앱 내에서 수신하고 푸시가 해당 전달에 대해 억제됩니다. 사용자가 오프라인이거나 최근에 보이지 않은 경우에만 푸시가 발생합니다.

필수 환경 변수

VAPID 키는 Environment에 저장되며 푸시를 활성화하려면 존재해야 합니다:

변수설명
webPushPublicKeyVAPID 공개 키 (base64url). 클라이언트가 GET /messaging/webpush/publicKey를 통해 반환됩니다
webPushPrivateKeyVAPID 개인 키. 모든 아웃바운드 푸시에 서명하는 데 사용됩니다
webPushSubject푸시 서비스에 보고된 mailto: URI. 기본값은 mailto:support@churchapps.org

WebPushHelper.isEnabled()는 키가 누락된 경우 false를 반환합니다 -- 메시징 모듈은 계속 작동합니다. 푸시 전달은 단순히 작업 안함입니다.

저장소 모델

웹 푸시 구독은 FCM 기기 기록과 함께 기존 devices 테이블에 저장됩니다. fcmToken 열의 webpush: 접두사로 구별됩니다:

fcmToken = "webpush:" + JSON.stringify({ endpoint, keys: { p256dh, auth } })

이를 통해 단일 loadByPersonId 호출로 플랫폼에 관계없이 사용자가 등록한 모든 기기를 반환할 수 있습니다.

끝점

기본 경로: /messaging/webpush

메서드경로인증설명
GET/publicKey공개{ publicKey, enabled }를 반환합니다
POST/subscribeJWT인증된 사용자를 위해 구독을 등록 또는 업데이트합니다
POST/unsubscribe공개주어진 끝점을 포함하는 기기 행을 삭제합니다
DELETE/subscription/:idJWT서버 측 ID로 특정 기기 행을 삭제합니다

클라이언트 기본: WebPushHelper

@churchapps/apphelperWebPushHelper는 단일 클라이언트 측 진입점입니다. 호스트가 부팅 시 한 번 구성하고 로그인 후 subscribe()를 호출합니다.

import { WebPushHelper } from "@churchapps/apphelper";

WebPushHelper.configure({
scope: "/", // service worker scope
appName: "B1AppPwa" // stored on the device row
});

await WebPushHelper.subscribe();

소비자가 무료로 얻는 동작:

  • 기능 확인 -- isSupported()는 PushManager이 없는 브라우저에서 false를 반환합니다.
  • 쿨다운 -- canPromptNow()는 localStorage를 통해 프롬프트 간에 7일 쿨다운을 적용합니다.
  • 옵트아웃 -- setOptedOut(true)unsubscribe()는 재프롬프트를 차단하고 서버 측 기기 행을 제거합니다.
  • 독립 실행형 PWA 감지 -- isStandalone()은 호스트가 iOS 푸시 프롬프트를 "사용자가 PWA를 홈 화면에 설치했음" 뒤에 게이트할 수 있게 합니다.
  • 교회 전환 시 재등록 -- refreshEnrollment()는 사용자를 다시 프롬프트하지 않고 새로운 userChurch에 대해 기존 브라우저 구독을 다시 게시합니다.

기술 세부 정보

서비스 워커는 등록된 범위에서 필수입니다. 푸시가 도착할 때 OS 수준 알림을 렌더링하기 위해 push 이벤트 핸들러를 포함해야 합니다.

운영 참고

  • gone: true 결과 -- 푸시 서비스가 404 또는 410으로 응답하면 구독이 영구적으로 유효하지 않습니다. 코드는 이러한 기기 행을 삭제하므로 다시 시도되지 않습니다.
  • TTL -- 푸시 메시지는 TTL: 86400(24시간)으로 전송됩니다.
  • 재시도 없음 -- 일시적 오류는 로그되고 재시도되지 않습니다. 푸시는 최선의 노력입니다.
  • 비활성화된 환경 -- 스테이징 및 개발은 VAPID 키를 비워둘 수 있습니다.

관련 페이지