개발 slecs

Flutter 앱에 FCM 푸시 알림 수신과 설정 화면 첫 통합

목차

Flutter 앱에 FCM을 처음으로 붙이는 작업을 마무리했다. firebase_messaging 패키지 연동부터 토큰 등록, 알림 수신 시 화면 이동, 푸시 설정 UI까지 한 번에 묶어서 올린 커밋이다.

왜 지금 이 시점에 FCM을 붙였나

단계별로 기능을 쌓아가는 구조였고, 이번 단계가 드디어 푸시 알림을 붙여야 하는 타이밍이었다. 팀 내부적으로도 "일단 기본 기능 먼저, 알림은 나중에"라는 암묵적 합의가 있었는데, 그 "나중"이 이번 스프린트로 당겨진 셈이다.

FCM 통합은 변경 파일 수 자체는 네 개로 많지 않지만, 건드리는 레이어가 꽤 넓다.

파일 역할 이번 변경 의미
AndroidManifest.xml Android 앱 설정 진입점 FCM 서비스 선언, 알림 권한 추가
fcm_repository.dart FCM 관련 데이터 레이어 토큰 fetch/등록, 메시지 수신 처리
firebase_config.dart Firebase 초기화 설정 FlutterFire 초기화 진입점 통합
push_settings_screen.dart UI 레이어 사용자 푸시 수신 동의/설정 화면

네이티브 설정, 데이터 레이어, Firebase 초기화 설정, UI까지 수직으로 다 건드리는 작업이었다.

작업 내용 — 주요 흐름

FCM 통합을 할 때 보통 세 가지 상태를 모두 처리해야 한다.

  • Foreground: 앱이 켜져 있을 때 메시지 수신
  • Background: 앱이 백그라운드에 내려가 있을 때
  • Terminated: 앱이 완전히 종료된 상태에서 알림 탭으로 앱 진입

이 세 케이스를 fcm_repository.dart 안에서 각각 처리했다. 특히 Terminated 상태에서는 getInitialMessage()로 초기 메시지를 잡아야 하고, 이걸 놓치면 "알림 눌렀는데 그냥 홈으로 가버림" 같은 UX 버그가 된다.

// fcm_repository.dart 패턴 예시
Future<void> initialize() async {
  // Terminated → 앱 진입 시
  RemoteMessage? initialMessage =
      await FirebaseMessaging.instance.getInitialMessage();
  if (initialMessage != null) {
    _handleMessage(initialMessage);
  }

  // Background → 알림 탭
  FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);

  // Foreground
  FirebaseMessaging.onMessage.listen((message) {
    // 인앱 배너 or 커스텀 처리
  });
}

알림 navigate의 경우, payload에 담긴 데이터를 파싱해서 어느 화면으로 이동할지 분기하는 로직이 들어가는데, 이 부분은 라우팅 구조가 어떻게 잡혀 있냐에 따라 복잡도가 달라진다. 이번엔 명확한 route key로 분기하는 방식으로 단순하게 처리했다.

AndroidManifest.xml에서는 <meta-data>로 기본 알림 채널과 아이콘을 잡아줬다. 이걸 빠뜨리면 Android 8.0 이상에서 알림이 아예 안 뜨거나 기본 아이콘이 깨지는 문제가 생긴다. 늘 놓치기 쉬운 포인트다.

push_settings_screen — 사용자 동의 처리

푸시 설정 화면을 별도로 만든 건 의도적인 결정이었다. iOS는 requestPermission()으로 시스템 권한 팝업이 뜨지만, Android 13 이상도 런타임 권한이 필요하다. 그리고 권한을 허용했더라도 사용자가 앱 내에서 "수신 안 함"으로 설정할 수 있는 토글이 있어야 한다.

push_settings_screen.dart가 그 역할이다. 시스템 권한 상태와 앱 내 설정 상태를 분리해서 보여주는 구조가 돼야 UX적으로 자연스럽다.

시스템 권한 OFF → "설정에서 알림 허용 필요" 안내 + 설정 이동 버튼
시스템 권한 ON + 앱 설정 OFF → 앱 내 토글만으로 제어 가능
시스템 권한 ON + 앱 설정 ON → 정상 수신

이 세 가지 상태를 화면에서 명확하게 구분해서 보여줘야 "왜 알림이 안 오지?" 같은 CS를 줄일 수 있다.

회고

FCM은 연동 자체는 어렵지 않은데 항상 엣지 케이스에서 시간이 빠진다. 특히 토큰 갱신 처리(onTokenRefresh)나 Terminated 상태 분기를 꼼꼼히 안 챙기면 나중에 "특정 기기에서만 알림이 안 온다"는 제보가 들어온다. 이번엔 처음부터 세 가지 상태를 다 잡고 들어갔기 때문에 그 부분은 비교적 깔끔하게 마무리됐다.

팀에 FCM 경험이 적은 동료도 있어서, fcm_repository.dart의 메서드 단위를 최대한 의도가 명확하게 나뉘도록 짰다. 코드리뷰할 때 "이 메서드가 뭘 처리하는지"를 쉽게 이해할 수 있어야 나중에 수정 요청이 들어와도 빠르게 대응 가능하다. 그게 결국 팀 전체 유지보수 비용을 낮추는 거라고 생각한다.

다음 단계는 서버에서 토픽 기반 발송 또는 개별 토큰 발송을 어떻게 조합할지 결정해야 한다.

댓글 0

첫 댓글 달아줘.