[푸시알림 구현] Flutter 앱 - Firebase Cloud Message 연동

2025. 5. 5. 05:33Project Log/학부 졸업프로젝트

개요

Flutter 앱에 푸시 알림 기능을 추가하고자 한다. 이번에는 Firebase에서 제공하는 Cloud Messaging 서비스 (FCM)을 이용했다. 알림의 내용이 서비스 관점에서 훨씬 중요하지만, 우선 이 포스팅은 기술적인 알림 구현에 관한 내용이다.

 

앱 자체의 권한 설정, 알림 코드, Firebase 콘솔에서의 각종 설정이 필요하여, 조금 복잡한 면이 있다. Foreground, Background, Terminated 상태 모두에서 알림이 오는 것과, FCM 토큰 등록과 푸시 알림 자동화를 어떻게 수행할지 정하는 것이 중요한 부분이다.

 

Firebase 프로젝트에 Flutter 앱 등록하기

Firebase 프로젝트를 생성한다. (가이드만 잘 따르면 되므로 과정은 생략한다.)

프로젝트에 들어간 후, 앱 추가 구역에서 Flutter 로고가 있는 버튼을 클릭한다. 

 

FlutterFire CLI를 설치한다.

dart pub global activate flutterfire_cli

 

firebase 로그인이 필요하다. nodejs LTS를 설치한 후, npm으로 firebase-tools를 설치한다.

firebase 로그인을 CLI에서 진행한다.

sudo npm install -g firebase-tools
firebase login

 

플러터 프로젝트 디렉토리의 루트에서 아래 커맨드를 실행한다. 플랫폼별 앱이 Firebase에 자동으로 등록되고 lib/firebase_options.dart 파일이 추가된다. 프로젝트 ID는 프로젝트 목록 화면에서 확인할 수 있다.

flutterfire configure --project={프로젝트 ID}

 

 

android, ios 앱이 생성되었다. (web과 windows 앱은 실수로 모든 옵션을 선택해서 생성된 것이고, 사용하지는 않는다.)

 

main.dart에 Firebase 초기화 코드를 추가한다.

import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

Future<void> main() async {
  (중략)
  
  await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
  );  

  (중략)

  runApp(const MyApp());
}

 

Flutter 앱에 알림 관련 코드 추가하기

AndroidManifest.xml 파일에 권한을 추가한다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

 

Flutter 프로젝트에 FCM 관련 라이브러리를 설치한다.

flutter pub add firebase_core
flutter pub add firebase_messaging
flutter pub add flutter_local_notifications

 

main.dart에 알림 수신 코드를 작성한다.

ForegroundBackground 상태를 모두 고려한 코드이다.

 

Background의 경우 푸시 알림을 자동으로 생성해 주지만, Foreground일 때는 Local Notification을 생성하여 앱 내에서 알림이 보이도록 한다.

(중략)
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  (중략)
  
  await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
  );  

  (중략)
  
  FirebaseMessaging messaging = FirebaseMessaging.instance;
  NotificationSettings settings = await messaging.requestPermission(
    alert: true,
    announcement: false,
    badge: true,
    carPlay: false,
    criticalAlert: false,
    provisional: false,
    sound: true,
  );
  print('User granted permission: ${settings.authorizationStatus}');

  final fcmToken = await messaging.getToken();
  if (settings.authorizationStatus == AuthorizationStatus.authorized && fcmToken != null) {
    print('FCM Token: $fcmToken');
  } else {
    print('FCM Token: null');
  }

  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

  await _initializeLocalNotifications();

  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
		print('Got a message whilst in the foreground!');
		print('Message data: ${message.data}');
 
		if (message.notification != null) {
			print('Message also contained a notification: ${message.notification}');

      flutterLocalNotificationsPlugin.show(
        0,
        message.notification!.title,
        message.notification!.body,
        const NotificationDetails(
          android: AndroidNotificationDetails(
            'default_channel_id',
            '기본 채널',
            importance: Importance.max,
            priority: Priority.high,
          ),
        ),
      );
      
		}
	});

  runApp(const MyApp());
}

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
}

final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
    FlutterLocalNotificationsPlugin();

Future<void> _initializeLocalNotifications() async {
  const AndroidInitializationSettings initializationSettingsAndroid =
      AndroidInitializationSettings('@mipmap/ic_launcher');

  const InitializationSettings initializationSettings =
      InitializationSettings(android: initializationSettingsAndroid);

  await flutterLocalNotificationsPlugin.initialize(initializationSettings);
}

 

알림 생성 및 테스트 메시지 전송하기

알림 내용을 작성한다.

 

위의 플러터 코드를 실행하면, 로그에서 디바이스의 FCM Token을 확인할 수 있다.

토큰을 복사하고, '테스트 메시지 전송' 버튼을 누르면 나오는 모달의 텍스트 필드에 붙여 넣는다.

 

Android 에뮬레이터와 실제 Android 기기에서 테스트를 진행했다.

앱을 Foreground에서 실행 중일 때, 앱을 Background에서 실행 중일 때, 앱을 완전히 종료했을 때 모두 정상적으로 알림을 받을 수 있었다.

 

 

To do

Firebase와 Flutter 앱을 연동하는 기본적인 방법을 알아보았다.

FCM에서 테스트 메시지 전송을 통해 수동으로 알림을 테스트했다.

이제 푸시 알림 '자동화' 방법을 알아봐야 한다.