앱 푸시 알림 체계

Capacitor 앱(novato_react)의 FCM 푸시 알림 + 알림함 시스템. 2026-07-25 정식 개통.

한눈에 보기 — 알림 종류

알림시점 (KST)내용딥링크
☕ 커피타임허브 등록 즉시 (트리거)별명(실명 금지, 없으면 “팀원”) + 랜덤 농담 10종/lounge
📅 주간 일정매주 월 09:00이번 주(월~일) 전체 경기 목록/schedule
🏀 오늘 경기경기 당일 09:00그날 경기 목록/schedule
🏀 개인 경기 예고경기 전날 22:00”OO님, 내일 7/29(수) 18:30 3M vs 야호…” + 우리 팀 유니폼 색 — 본인 팀 경기가 있는 사람만/schedule
💬 댓글 멘션즉시 (트리거)경기 댓글 “@선수이름” → 그 선수의 연결 계정에만 (작성자 제외)/records
🙋 가입 요청신청 즉시 (트리거)super_admin 에게만 — 탭 시 브라우저로 허브 회원관리https://hub.novato.kr/members
📊 경기 결과결과 등록 즉시 (트리거, 점수 첫 기록 시 1회)해당 경기 결과 + 팀 순위 전체/ranking
🤖 AI 프리뷰/리뷰생성 즉시 (트리거)매치업 + 헤드라인/predict/<gameId>, /records
(임시) 테스트9~17시 2시간 간격”별명님, N시 알림 테스트입니다”/dashboard

수신 대상 공통 규칙: 승인(approved) 회원 + player 연결 + 활성 푸시 토큰 (push_audience() RPC). player 미연결/미승인/익명 토큰은 어떤 알림도 받지 않는다.

알림함 (앱 상단 🔔)

  • 최근 7일 수신 알림 최신순. 항목 탭 → 해당 알림의 route(딥링크)로 인앱 이동.
  • 안 읽은 알림이 있으면 종 아이콘에 빨간 점. 알림함 열면 전체 읽음 처리.
  • 데이터: app_notifications (RLS: 본인 행만 조회/읽음). send-push 가 발송할 때 유저별로 자동 기록하므로 별도 처리 불필요.
  • 7일 지난 행은 크론 purge-app-notifications-7d (매일 KST 04:00)가 삭제.
  • 앱 코드: src/pages/NotificationInboxPage.tsx, src/services/notificationService.ts

아키텍처

크론(pg_cron) / DB 트리거
        │  net.http_post (service key = Vault 'service_role_key')
        ▼
notify-events (Edge Function)   ← 문구 조합 전담 (농담/별명/일정/순위)
        │  HTTP (target: user ids)
        ▼
send-push (Edge Function)       ← FCM v1 발송 + 알림함(app_notifications) 기록
        │                          + 무효 토큰(404/403) 자동 비활성화
        ▼
FCM → 단말 (탭 시 data.route 로 인앱 라우팅 — src/native/pushNotifications.ts)
  • notify-events supabase/functions/notify-events/index.ts — kind: morning(주간/오늘/개인 예고 통합) · results · coffee · ai. dry: true 로 발송 없이 문구만 확인 가능.
  • send-push supabase/functions/send-push/index.ts — target: all | user(uuid 배열) | token(테스트). 개인별 다른 내용 = 유저별 호출.
  • 배선 SQL(원격 적용 완료): novato_react/supabase/notify_events_wiring.sql, app_notifications_inbox.sql, push_test_cron.sql

서버 오브젝트 (Supabase)

종류이름역할
cron (jobid 7)notify-morning-kst9UTC 0시 = KST 09:00 → kind morning
triggertrg_notify_result on game_league점수 null→값 UPDATE → 결과+순위 즉시
triggertrg_notify_mention on game_comments’@’ 포함 INSERT → 멘션 알림
cron (jobid 10)notify-game-eve-kst22UTC 13시 = KST 22:00 → kind game-eve (전날 개인+유니폼)
cron (jobid 5)purge-app-notifications-7d알림함 7일 정리
cron (jobid 3)push-test-2h-kst-9-18(임시) 개인화 테스트 알림
triggertrg_notify_coffee on coffee_timesINSERT → 커피 알림
triggertrg_notify_preview on game_previewsINSERT → AI 프리뷰 알림
triggertrg_notify_review on game_reviewsINSERT → AI 리뷰 알림
RPCpush_audience()수신 대상 (service_role 전용)
tableapp_notifications알림함 (7일 보관)
tablepush_tokensFCM 토큰 (무효 시 자동 비활성)

운영 방법

  • 알림 끄기: SELECT cron.unschedule('<jobname>'); / 트리거는 DROP TRIGGER <이름> ON <테이블>;
  • 문구 수정: notify-events/index.ts 수정 후 supabase functions deploy notify-events (농담 목록 COFFEE_JOKES 포함)
  • 테스트(발송 없이): notify-events 에 {"kind":"morning","dry":true} 등으로 POST (Authorization: service key)
  • 테스트 크론 제거: SELECT cron.unschedule('push-test-2h-kst-9-18');

함정 노트 (겪은 것들)

  • OTA·manifest 는 CapacitorHttp 필수 — R2 는 CORS 헤더가 없어 웹뷰 fetch 는 조용히 실패 → 아키텍처 및 index 변경 이력(2026-07-23) 참조
  • Firebase 재등록(패키지명 변경) 시: google-services.json 은 네이티브 리소스라 APK 재설치 필수(OTA 불가). 옛 클라이언트 토큰은 FCM 404/403 → send-push 가 자동 비활성화.
  • capgo OTA 번들은 앱이 백그라운드로 갈 때 적용 — 강제 종료(force-stop)는 적용 단계를 건너뜀.

관련: 앱-개요 · 아키텍처 · AI-경기프리뷰