FCM 푸시 토큰, 언제 수집해야 하나 — 첫 화면 진입 vs 알림 동의 시점

하이브리드 앱(WebView + Spring Boot API)에 푸시 알림을 붙이면서 팀 안에서 한 번 정리하고 넘어가야 했던 질문이 있다.

FCM 토큰은 언제 수집해서 서버에 저장해야 하는가?

구현 자체는 어렵지 않다. 문제는 “언제”다. 앱을 처음 켜자마자 받아둘지, 사용자가 알림에 동의한 시점에 받을지에 따라 서버의 데이터 모델과 개인정보 처리 관점이 달라진다. 결론은 동의 시점을 기본으로, 앱 실행 시 갱신을 보조로 두는 하이브리드 방식이었다. 그 근거를 정리한다.

두 가지 선택지

A. 첫 화면 진입 시 수집

앱이 실행되면 바로 getToken()을 호출해서 서버에 올린다. 단순하고, 사용자가 나중에 알림을 켰을 때 토큰이 이미 있으니 즉시 보낼 수 있다.

문제는 다음과 같다.

  • 동의하지 않은 사용자의 토큰이 서버에 쌓인다. 알림을 보내지 않더라도, 기기 식별이 가능한 값을 동의 없이 수집·보관하는 셈이다.
  • 서버에 “토큰은 있는데 동의는 없는” 상태가 생긴다. 발송 로직마다 동의 여부를 따로 확인해야 하고, 실수 한 번이면 동의 안 한 사용자에게 푸시가 나간다.
  • iOS는 어차피 시스템 알림 권한이 없으면 APNs 토큰이 의미가 없다. 권한 요청 전에 받은 토큰은 반쪽짜리다.

B. 알림 동의 시점에 수집

사용자가 앱 내 알림 설정을 ON으로 바꾸는 순간(그리고 OS 권한을 허용하는 순간)에 토큰을 받아 서버에 저장한다.

  • 동의 상태와 토큰 상태가 1:1로 매핑된다. 토큰이 있으면 동의한 것이고, 동의를 철회하면 토큰을 지운다.
  • 발송 로직이 단순해진다. 토큰이 있는 사용자에게만 보내면 된다.
  • 개인정보 처리 관점에서도 “수집 시점 = 동의 시점”이라 설명이 깔끔하다.

단점은 하나다. 동의 시점 한 번만 수집하면 토큰이 바뀌었을 때 따라가지 못한다.

FCM 토큰은 영구적이지 않다

이게 A와 B 중 하나만 고를 수 없는 이유다. FCM 토큰은 다음 상황에서 바뀐다.

  • 앱 재설치
  • 앱 데이터 초기화
  • 기기 복원
  • 장기간 미사용 후 토큰 만료/갱신

동의 시점에 저장한 토큰이 재설치 후 무효가 되면, 서버는 “동의한 사용자”에게 보내는데 기기에는 도착하지 않는다. 사용자는 알림을 켜놨는데 안 온다고 느낀다.

그래서 초기 수집 외에 별도의 갱신 메커니즘이 반드시 필요하다.

결론: 동의 시점 + 갱신

[1차 — 동의 시점]
  알림 설정 ON + OS 권한 허용
    → getToken()
    → POST /push/token { token, platform }

[2차 — 갱신]
  (a) onTokenRefresh / onNewToken 콜백
      → 서버에 저장된 토큰이 있는 사용자라면 즉시 교체
  (b) 앱 실행 시
      → 로컬에 "동의 ON" 상태이면 getToken() 후 서버 값과 비교, 다르면 갱신

핵심은 2차 갱신이 “이미 동의한 사용자”에게만 동작한다는 점이다. 동의 안 한 사용자는 앱을 켜도 토큰을 올리지 않는다. 이렇게 하면 B의 깔끔한 매핑을 유지하면서 A의 “토큰 최신성”을 확보한다.

서버 쪽 데이터 모델

sql

CREATE TABLE push_token (
  id            BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id       BIGINT NOT NULL,
  device_id     VARCHAR(64) NOT NULL,   -- 기기 단위 식별 (한 사용자 다기기)
  platform      VARCHAR(10) NOT NULL,   -- ANDROID / IOS
  token         VARCHAR(255) NOT NULL,
  consented_at  DATETIME NOT NULL,      -- 동의 시각 = 수집 시각
  updated_at    DATETIME NOT NULL,
  UNIQUE KEY uk_user_device (user_id, device_id)
);

동의를 철회하면 행을 삭제한다(soft delete가 아니라 실제 삭제). “동의 철회했는데 토큰은 남아 있는” 상태를 만들지 않기 위해서다. 다시 동의하면 새로 수집한다.

같은 사용자가 여러 기기를 쓰는 경우를 위해 device_id를 키에 포함한다. 한 사용자의 토큰을 1개로 덮어쓰면 태블릿에서 알림을 켠 순간 폰의 토큰이 사라진다.

하이브리드 앱에서의 호출 구조

WebView 안 React가 알림 설정 UI를 담당하고, 실제 토큰 발급은 네이티브가 한다.

React (WebView)                Native                      Spring Boot
  알림 ON 토글 ──JS Bridge──▶ 권한 요청 + getToken()
                            ◀── token 반환 ──
  POST /push/token ───────────────────────────────────▶ 저장

갱신 콜백(onNewToken)은 네이티브에서 발생하므로, 네이티브가 직접 서버에 올리거나 WebView로 이벤트를 넘겨서 올리게 한다. 둘 중 하나로 통일해야 한다. 양쪽에서 다 올리면 경쟁 조건이 생긴다.

정리

질문
토큰 수집 시점알림 동의(설정 ON + OS 권한 허용) 시점
왜 첫 화면이 아닌가동의 없는 토큰 보관을 피하고, 동의 상태와 토큰 상태를 1:1로 유지
토큰 변경 대응onNewToken 콜백 + 앱 실행 시 비교 갱신 (동의한 사용자만)
동의 철회 시토큰 행 삭제
다기기(user_id, device_id) 단위로 저장

관련 글

답글 남기기