푸시 outbox와 suppression audit

원천 변화부터 미발송 판단, outbox claim과 APNs 결과까지 남기는 알림 감사 기록

한강자리의 알림은 원천 변화에서 후보를 만들고 발송 규칙을 적용한 뒤 통과한 항목만 outbox에 넣는다. APNs 전송 결과와 억제 이유는 모두 audit에 남긴다.

운영자는 후보 부재, 구독 불일치, 규칙에 따른 억제와 APNs 전송 실패를 구분해야 한다. 성공한 발송 기록만으로는 이 네 상태를 나눌 수 없다.

원천 변화에서 알림 후보까지

push worker는 원천 이벤트를 canonical fact로 정규화한다. 주차 잔여 대수가 기준 아래로 내려간 상태, 공원 혼잡 변화와 행사 취소가 각각 fact가 된다.

그 다음 fact와 subscription을 맞춰 본다. 사용자가 어떤 공원이나 주차장에 관심을 표시했는지, 어떤 종류의 알림을 켰는지, 조용한 시간인지 같은 조건을 본다.

flowchart LR
  Fact["원천 변화<br/>알림용 fact"] --> Match["후보와 설정 맞추기"]
  Subscription["구독/알림 설정"] --> Match
  Match --> Policy["발송 정책과 결정<br/>push_now 또는 억제"]
  Policy --> Outbox["푸시 outbox"]

우선 적용하는 미발송 규칙

한강자리에는 명시적인 suppression rule이 있다.

예이유
주차장 상태 변화 알림 전체 억제사용자가 원하는 것은 잔여 대수 기준 알림에 가깝다
잔여 대수 없는 주차장 threshold 알림 억제행동 가능한 숫자가 없다
공원 상태 변화 알림 억제너무 넓고 사용자가 할 일이 불명확하다
혼잡 변화가 불명확하면 억제메시지 본문을 안전하게 만들 수 없다

이 rule을 통과한 후보에 사용자 설정과 영향도 평가를 적용한다.

사용자 설정과 영향도

suppression rule을 통과한 후보는 사용자 알림 모드와 fact의 우선순위를 함께 평가한다.

다음 조건을 순서대로 확인한다.

  • 사용자가 알림을 껐는가.
  • 이 변화는 긴급도가 높은가.
  • parking-first 모드에서 주차 관련 중요 신호인가.
  • outing-brief 모드에서 공원/행사 관련 중요 신호인가.
  • 그 외에는 smart mode에서 보낼 만큼 중요한가.

결과는 push_now 또는 suppress이며 반드시 reason code를 남긴다.

flowchart TD
  Candidate["맞은 후보"] --> Off{"알림 꺼짐?"}
  Off -->|예| Suppress["억제 이유 기록"]
  Off -->|아니오| Rule{"억제 규칙 해당?"}
  Rule -->|예| SuppressRule["이유와 함께 억제"]
  Rule -->|아니오| Tier{"중요하거나 T0?"}
  Tier -->|예| Push["push_now"]
  Tier -->|아니오| Mode["모드별 결정"]

delivery decision에 남기는 미발송 판단

미발송 상태는 후보 부재, 구독 불일치, 규칙 억제와 전송 실패로 나눠 기록한다.

delivery decision 기록에는 subscription, fact, target, decision, reason code, policy mode, policy version을 남긴다. 같은 fact와 subscription 조합을 두 번 처리하지 않도록 제한도 둔다.

reason code 분포가 갑자기 바뀌면 fact 생성과 구독 매칭을 각각 확인한다. suppression은 조건을 통과하지 못한 후보를 의도적으로 발송하지 않은 결과다.

outbox의 APNs 전송

outbox는 발송할 항목을 트랜잭션으로 저장하고 APNs 전송을 별도 dispatcher에서 처리하게 한다.

sequenceDiagram
  autonumber
  participant Worker as 푸시 워커
  participant Outbox as outbox 테이블
  participant Dispatcher as 푸시 디스패처
  participant APNs as APNs
  participant Repo as 전송 저장소

  Worker->>Outbox: 알림을 발송 대기열에 넣기
  Dispatcher->>Outbox: 보낼 차례가 된 알림 가져오기
  Dispatcher->>APNs: APNs에 메시지 보내기
  alt 성공
    Dispatcher->>Repo: 전송 성공으로 기록
  else 잘못된 device token
    Dispatcher->>Repo: 최종 실패와 token 정리 후보 기록
  else APNs 인증 오류
    Dispatcher->>Repo: 남은 작업 유예
  else 재시도 가능한 실패
    Dispatcher->>Repo: 다음 시도 시각과 대기 시간 저장
  end

outbox에서 보낼 때는 다음을 구분한다.

  • 만료된 항목은 보내지 않는다.
  • 번역 문구가 준비되지 않은 메시지는 최종 실패로 둔다.
  • 무효화된 device token은 cleanup 대상이 된다.
  • APNs 인증 오류가 나면 남은 항목을 무리하게 보내지 않고 유예한다.
  • 재시도 가능한 실패는 attempt count와 backoff를 반영한다.

판단 근거를 보여주는 대시보드

대시보드에는 발송, 억제, 재시도, invalid device token, 오래 기다린 outbox 항목과 우선순위별 backlog를 함께 표시한다. suppression reason이 늘면 fact 생성과 구독 규칙을 확인하고 APNs 오류는 재시도 가능 여부와 token 정리 여부로 나눈다.

reason code와 delivery attempt

delivery decision은 발송과 억제를 reason code로 남긴다. outbox와 delivery attempt는 번역 누락, invalid device token, APNs 인증 오류와 재시도 가능한 실패를 서로 다른 후속 조치로 연결한다.

Comments

댓글

    이미지 확대