Flutter 앱에서 푸시 알림을 어떻게 구현하나요?
Flutter 앱에서 푸시 알림을 구현하는 방법은 여러 가지가 있지만, 가장 일반적인 방법은 Firebase Cloud Messaging(FCM)을 사용하는 것입니다. 아래에서 Flutter 앱에 푸시 알림을 구현하는 전체 과정을 설명하겠습니다.
1. Firebase Cloud Messaging(FCM) 설정
필요한 패키지 추가
먼저 pubspec.yaml
파일에 필요한 패키지를 추가합니다:
dependencies:
flutter:
sdk: flutter
firebase_core: ^2.15.1
firebase_messaging: ^14.6.7
flutter_local_notifications: ^15.1.0+1
Firebase 프로젝트 설정
- Firebase 콘솔에서 새 프로젝트를 생성합니다.
- Flutter 앱을 Firebase에 등록합니다.
- 플랫폼별로 필요한 설정 파일을 다운로드하여 적용합니다:
- Android:
google-services.json
을android/app/
디렉토리에 배치 - iOS:
GoogleService-Info.plist
를ios/Runner/
디렉토리에 배치
- Android:
Android 설정
android/app/build.gradle
파일을 수정합니다:
dependencies {
implementation platform('com.google.firebase:firebase-bom:32.2.3')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-messaging'
}
android/build.gradle
파일에도 추가합니다:
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.3.15'
}
}
그리고 android/app/src/main/AndroidManifest.xml
파일에 다음을 추가합니다:
<manifest ...>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application ...>
<!-- FCM 서비스 추가 -->
<service
android:name="io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- 알림 아이콘 설정 -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
</application>
</manifest>
iOS 설정
ios/Runner/AppDelegate.swift
파일을 수정합니다:
import UIKit
import Flutter
import Firebase
import FirebaseMessaging
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: { _, _ in }
)
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
Messaging.messaging().delegate = self as? MessagingDelegate
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
iOS Info.plist
파일에 다음을 추가합니다:
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
2. Flutter 앱에서 푸시 알림 설정
기본 설정 및 권한 요청
main.dart
파일에서 다음과 같이 설정합니다:
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
// 백그라운드 메시지 핸들러
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('백그라운드 메시지 처리: ${message.messageId}');
}
// 로컬 알림 채널 설정
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'중요 알림', // title
description: '이 채널은 중요 알림에 사용됩니다.', // description
importance: Importance.high,
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// 백그라운드 메시지 핸들러 설정
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// 로컬 알림 플러그인 초기화
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
// iOS 권한 설정
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
_setupPushNotifications();
}
// 푸시 알림 설정
Future<void> _setupPushNotifications() async {
// 알림 권한 요청
NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
alert: true,
badge: true,
sound: true,
provisional: false,
);
print('사용자 알림 권한 상태: ${settings.authorizationStatus}');
// FCM 토큰 가져오기
String? token = await FirebaseMessaging.instance.getToken();
print('FCM 토큰: $token');
// 토큰 변경 리스너
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
// 서버에 새 토큰 전송
print('FCM 토큰 갱신: $newToken');
});
// 포그라운드 메시지 처리
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('포그라운드 메시지 수신: ${message.notification?.title}');
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
// 알림 표시 (안드로이드)
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: android.smallIcon,
// 기타 속성들
),
),
);
}
});
// 백그라운드 상태에서 알림 클릭 처리
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('알림 클릭으로 앱 열림: ${message.data}');
// 특정 화면으로 이동하는 로직 추가
});
// 앱이 종료된 상태에서 알림 클릭으로 열린 경우 처리
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
print('종료 상태에서 알림 클릭으로 앱 열림: ${initialMessage.data}');
// 특정 화면으로 이동하는 로직 추가
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '푸시 알림 데모',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('푸시 알림 데모'),
),
body: Center(
child: Text('FCM 푸시 알림 데모 앱'),
),
),
);
}
}
3. 토픽 구독 및 알림 전송
특정 토픽 구독하기
사용자가 특정 주제의 알림을 받을 수 있도록 토픽에 구독할 수 있습니다:
// 특정 토픽에 구독
FirebaseMessaging.instance.subscribeToTopic('news');
// 토픽 구독 취소
FirebaseMessaging.instance.unsubscribeFromTopic('news');
서버에서 푸시 알림 전송하기
Firebase Cloud Messaging API를 사용하여 서버에서 알림을 전송할 수 있습니다:
// 서버 측 코드 (Node.js 예제)
const admin = require('firebase-admin');
const serviceAccount = require('./service-account-key.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
// 특정 토큰에 메시지 전송
function sendToDevice() {
const registrationToken = 'USER_FCM_TOKEN';
const message = {
notification: {
title: '새 알림',
body: '새로운 메시지가 도착했습니다!'
},
data: {
type: 'chat',
id: '123456'
},
token: registrationToken
};
admin.messaging().send(message)
.then((response) => {
console.log('메시지 전송 성공:', response);
})
.catch((error) => {
console.log('메시지 전송 실패:', error);
});
}
// 토픽에 메시지 전송
function sendToTopic() {
const message = {
notification: {
title: '뉴스 알림',
body: '새로운 뉴스가 도착했습니다!'
},
data: {
type: 'news',
id: '789012'
},
topic: 'news'
};
admin.messaging().send(message)
.then((response) => {
console.log('토픽 메시지 전송 성공:', response);
})
.catch((error) => {
console.log('토픽 메시지 전송 실패:', error);
});
}
4. 알림 클릭 처리 및 데이터 활용
알림 클릭 처리
알림을 클릭했을 때 특정 화면으로 이동하는 로직을 구현할 수 있습니다:
// 알림 클릭 처리
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('알림 클릭으로 앱 열림: ${message.data}');
// 데이터에 따라 다른 화면으로 이동
if (message.data['type'] == 'chat') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(chatId: message.data['id']),
),
);
} else if (message.data['type'] == 'news') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewsDetailScreen(newsId: message.data['id']),
),
);
}
});
5. 푸시 알림 디버깅 및 테스트
Firebase 콘솔에서 테스트 메시지를 전송하거나, FCM HTTP v1 API를 사용하여 테스트할 수 있습니다. 또한 Firebase 디버그 로깅을 활성화하여 알림 처리를 모니터링할 수 있습니다:
// 디버그 로깅 활성화
FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
요약
- Firebase Cloud Messaging(FCM) 설정
- 플랫폼별 설정 (Android/iOS)
- 푸시 알림 초기화 및 권한 요청
- 포그라운드/백그라운드/종료 상태 알림 처리
- 토픽 구독 및 알림 전송
- 알림 클릭 처리 및 데이터 활용
- 테스트 및 디버깅
Flutter에서 푸시 알림을 올바르게 구현하려면 플랫폼별 설정이 중요하며, 알림 권한 관리와 다양한 앱 상태에서의 알림 처리를 고려해야 합니다.