Flutter WebView + FCM 푸시 구현기

JungHanMa·2025년 2월 18일
post-thumbnail

🛎️ 팀과 함께 리텐션을 고민하다
그리고 내가 푸시 메시지를 개발하게 된 이야기
최근 마케팅 팀에서 “멤버십 유저 이탈률을 줄이기 위해 리텐션을 높일 방법이 필요하다”는 의견이 팀장님께 전달되었습니다.
그 논의 끝에 ‘놓치기 쉬운 쿠폰과 프로모션을 사용자에게 효과적으로 알리자’는 방향이 정해졌고,
자연스럽게 푸시 메시지 시스템 구축이 필요하다는 결론에 도달했습니다.

그렇게 팀장님은 저에게 푸시 메시지 시스템 개발을 제안하셨고,
“한 번 제대로 만들어보자!”는 마음으로 본격적인 개발을 시작하게 되었죠.

🧩 배경: 리텐션이 중요한 이유
저희 푸짐 서비스에서는 회원 가입 시 한 달 무료 멤버십을 제공해 초기 유입을 유도하고 있습니다.
멤버십 유저는 식자재를 일반가보다 저렴한 가격에 구매할 수 있어 구매 전환율이 높은 편입니다.

하지만 유입되는 멤버십 유저 수와 이탈 유저 수가 거의 비슷하거나, 이탈이 더 많은 경우도 생기고 있었습니다.
따라서 매출을 안정적으로 늘리기 위해서는 기존 사용자의 리텐션을 높이는 것이 핵심 과제로 떠올랐습니다.

🔔 푸시 메시지로 놓치지 않게 알리자
사용자들이 쿠폰이나 프로모션을 몰라서 혜택을 놓치는 일이 종종 있었고,
이런 기회를 제때, 효과적으로 알릴 수 있다면 리텐션 개선에 도움이 될 것이라 판단했습니다.

그래서 사용자 맞춤형 푸시 알림 시스템을 직접 구축하게 되었고,
Flutter WebView 기반 앱에서 FCM(Firebase Cloud Messaging) 연동이라는 다소 생소한 작업에 도전하게 되었습니다.

1. Flutter와 Firebase 연동

이 글이 제일 잘 정리되어 있습니다.

  • Xcode > Settings > Accounts에서 Apple 계정 로그인 후, 팀(Team) 선택 가능 여부 확인 (none이면 안 됨)
  • 팀 Role이 Admin 또는 Developer여야 함
  • iOS: 알림 권한을 허용해야 Firebase token을 가져올 수 있음
  • Android: 알림 권한 없어도 token은 가져오지만 API 33 이상

2. Flutter에서 WebView로 token 전달

Future<void> _sendFcmTokenToWebView() async {
  String? token = await FirebaseMessaging.instance.getToken();
  if (token != null) {
    String os = Platform.isIOS ? 'ios' : 'aos';
    await _controller.runJavaScript('''
      window.dispatchEvent(new CustomEvent("fcmTokenReceived", { 
        detail: {
          token: "${token}",
          os: "${os}"
        }
      }));
    ''');
  }
}

..addJavaScriptChannel(
  'FcmToken',
  onMessageReceived: (JavaScriptMessage message) {
    _sendFcmTokenToWebView();
  },
)

window.dispatchEvent를 통해 JS 이벤트를 날리면 WebView에서 fcmTokenReceived 이벤트를 받아 처리할 수 있습니다.

webview JS 이벤트 수신 예시

WebView 디버깅 방법

import android.webkit.WebView

WebView.setWebContentsDebuggingEnabled(true)

이후 chrome://inspect/#devices에서 디버깅 가능!

chrome inspect 확인

3. 푸시 메시지 보내기

  • 백그라운드 푸시: 서버에서 전송
  • 포어그라운드 푸시: 앱에서 로컬 알림 처리
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
  String? imageUrl = message.notification?.android?.imageUrl;
  String? landingUrl = message.data['landing_url'];

  String? localImagePath;
  if (imageUrl != null && imageUrl.isNotEmpty) {
    localImagePath = await _downloadAndSaveImage(imageUrl);
  }

  await _showNotification(
    message.notification?.title,
    message.notification?.body,
    imageUrl: localImagePath,
    landingUrl: landingUrl,
  );
});

flutter_local_notifications 패키지로 포어그라운드 알림 구현 가능.

포어그라운드 알림 예시 서버 메시지 포맷 예시

4. 푸시 메시지에 이미지 첨부하기

안드로이드

안드로이드 푸시 UI 설명

아이폰

Notification Service Extension 필요

  1. Xcode > File > New > Target > Notification Service Extension
  2. Runner와 동일한 Deployment Target 설정
  3. 식별자 등록 필수
Notification Service Extension 확장 설정 App Groups 설정

이번 푸시 메시지 기능 구현을 통해 알림 권한 요청, 백그라운드/포어그라운드 상황에 따른 메시지 처리 방식, 이미지 첨부, 딥링크 처리 등 다양한 앱 기능을 새롭게 학습할 수 있었습니다. 사이드 프로젝트에서 React Native로 앱 기능을 연동해본 경험은 있었지만, 실제 운영 중인 서비스에 직접 기능을 적용한 것은 이번이 처음이었습니다.

무엇보다, 제가 구현한 푸시 메시지를 통해 실제 결제가 발생하고 있는 모습을 믹스패널에서 확인할 때 큰 보람을 느꼈습니다. 다음에는 더 빠르고 효율적으로 구현할 수 있을 것 같다는 자신감도 생겼습니다.

📌 팁: 제목 30자 / 본문 35자 정도면 푸시 메시지가 이쁘게 출력됩니다.

profile
Frontend Junior

0개의 댓글