본문으로 이동
주 메뉴
주 메뉴
사이드바로 이동
숨기기
둘러보기
대문
최근 바뀜
임의의 문서로
미디어위키 도움말
특수 문서 목록
학교의 모든 지식. SMwiki
검색
검색
보이기
로그인
개인 도구
로그인
로그아웃한 편집자를 위한 문서
더 알아보기
기여
토론
플러터:푸시 알람(FCM)
편집하기 (부분)
문서
토론
한국어
읽기
편집
원본 편집
역사 보기
도구
도구
사이드바로 이동
숨기기
동작
읽기
편집
원본 편집
역사 보기
일반
여기를 가리키는 문서
가리키는 글의 최근 바뀜
파일 올리기
문서 정보
보이기
사이드바로 이동
숨기기
경고:
로그인하지 않았습니다. 편집을 하면 IP 주소가 공개되게 됩니다.
로그인
하거나
계정을 생성하면
편집자가 사용자 이름으로 기록되고, 다른 장점도 있습니다.
스팸 방지 검사입니다. 이것을 입력하지
마세요
!
= 예시 프로젝트 = === 사전 준비 === {| class="wikitable" !과정 !설명 !비고 |- |Firebase 프로젝트 생성 | | |- |Android / iOS 앱 등록 | | |- | |<code>google-services.json</code> 또는 <code>GoogleService-Info.plist</code> 설정 완료 | |- |Flutter 설정 | === pubspec.yaml === <syntaxhighlight lang="yaml"> dependencies: flutter: sdk: flutter firebase_core: ^2.24.2 firebase_messaging: ^14.7.10 </syntaxhighlight> |flutter pub get |} === 예시 코드 === * onMessage, onMessageOpenedApp, getInitialMessage 이들을 이해하는 게 핵심. <syntaxhighlight lang="dart"> import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; /// 🔹 백그라운드 메시지 핸들러 (top-level 필수) Future<void> firebaseMessagingBackgroundHandler( RemoteMessage message) async { await Firebase.initializeApp(); // UI 접근 불가, 필요하면 로그/저장만 debugPrint('백그라운드 메시지 수신: ${message.messageId}'); } Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 백그라운드 핸들러 등록 FirebaseMessaging.onBackgroundMessage( firebaseMessagingBackgroundHandler, ); runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { String? fcmToken; final List<String> messages = []; @override void initState() { super.initState(); _initFCM(); _setupMessageHandlers(); } /// FCM 초기 설정 + 토큰 발급 Future<void> _initFCM() async { // iOS 권한 요청 (Android에서는 무시됨) await FirebaseMessaging.instance.requestPermission(); final token = await FirebaseMessaging.instance.getToken(); setState(() { fcmToken = token; }); } /// 메시지 수신 / 클릭 처리 void _setupMessageHandlers() { // 1️⃣ 포그라운드 수신 FirebaseMessaging.onMessage.listen((RemoteMessage message) { final title = message.notification?.title ?? '(no title)'; final body = message.notification?.body ?? '(no body)'; setState(() { messages.add('[FOREGROUND] $title - $body'); }); }); // 2️⃣ 백그라운드 상태에서 알림 클릭 FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { final title = message.notification?.title ?? '(no title)'; setState(() { messages.add('[CLICK - BG] $title'); }); }); // 3️⃣ 종료 상태에서 알림 클릭 FirebaseMessaging.instance.getInitialMessage().then((message) { if (message != null) { final title = message.notification?.title ?? '(no title)'; setState(() { messages.add('[CLICK - TERMINATED] $title'); }); } }); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('FCM 단일 파일 예제')), body: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'FCM Token', style: TextStyle(fontWeight: FontWeight.bold), ), SelectableText( fcmToken ?? '토큰 불러오는 중...', style: const TextStyle(fontSize: 12), ), const SizedBox(height: 16), const Text( '수신 / 클릭 메시지', style: TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Expanded( child: ListView.builder( itemCount: messages.length, itemBuilder: (context, index) { return ListTile( title: Text(messages[index]), ); }, ), ), ], ), ), ), ); } } </syntaxhighlight> == 푸시 알림 수신 == === 권한 요청 (iOS 필수) === <syntaxhighlight lang="dart"> await FirebaseMessaging.instance.requestPermission( alert: true, badge: true, sound: true, ); </syntaxhighlight> === 포그라운드 수신 === <syntaxhighlight lang="dart"> FirebaseMessaging.onMessage.listen((RemoteMessage message) { print('푸시 수신: ${message.notification?.title}'); }); </syntaxhighlight> === 백그라운드 수신 === <syntaxhighlight lang="dart"> Future<void> firebaseMessagingBackgroundHandler( RemoteMessage message) async { await Firebase.initializeApp(); } </syntaxhighlight><syntaxhighlight lang="dart"> FirebaseMessaging.onBackgroundMessage( firebaseMessagingBackgroundHandler, ); </syntaxhighlight> == 서버 측 (예시) == === Device Token 테이블 구조 === {| class="wikitable" !필드명 !설명 |- |user_id |사용자 ID |- |fcm_token |기기 토큰 |- |platform |android / ios |} === 푸시 전송 (JSON 예시) === <syntaxhighlight lang="json"> { "to": "FCM_TOKEN", "notification": { "title": "테스트 알림", "body": "푸시 알림이 도착했습니다" } } </syntaxhighlight> == 로그인 시 처리 == === 클라이언트(Flutter) === 앱 실행 후 FCM 토큰을 발급받는다.<syntaxhighlight lang="dart"> FirebaseMessaging messaging = FirebaseMessaging.instance; String? token = await messaging.getToken(); </syntaxhighlight>로그인 성공 시, 해당 토큰을 서버로 전송한다. === 서버 === 서버는 사용자 계정과 FCM 토큰을 매핑하여 저장한다. {| class="wikitable" !user_id !fcm_token !platform |- |123 |token_A |android |- |123 |token_B |ios |} * 하나의 계정은 여러 기기를 가질 수 있다. * FCM 토큰은 '''기기 단위'''로 관리한다. == 로그아웃 / 계정 삭제 처리 == === 로그아웃 === * 현재 기기의 FCM 토큰만 삭제 * 다른 기기 로그인은 유지 <syntaxhighlight lang="sql"> DELETE FROM device_token WHERE user_id = 123 AND fcm_token = 'token_A'; </syntaxhighlight> === 계정 삭제(탈퇴) === * 해당 계정에 연결된 모든 FCM 토큰 삭제 <syntaxhighlight lang="sql"> DELETE FROM device_token WHERE user_id = 123; </syntaxhighlight> == 토큰 변경 및 예외 처리 == === 토큰 갱신 === 다음과 같은 경우 FCM 토큰이 변경될 수 있다. * 앱 재설치 * 기기 변경 * OS 업데이트 * Firebase 정책 변경 Flutter에서는 토큰 변경 이벤트를 수신하여 서버에 반영해야 한다.<syntaxhighlight lang="dart"> FirebaseMessaging.instance.onTokenRefresh.listen((newToken) { // 서버로 새 토큰 전송 }); </syntaxhighlight> === 앱 삭제 === * 서버는 앱 삭제 여부를 직접 알 수 없음 * 푸시 전송 시 FCM에서 에러 반환 대표적인 에러: * <code>NotRegistered</code> * <code>InvalidRegistration</code> 이 경우 서버에서 해당 토큰을 삭제해야 한다. == 푸시 전송 == === 서버 → FCM === 서버는 FCM 서버 키를 사용하여 푸시 요청을 전송한다.<syntaxhighlight lang="json"> { "to": "fcm_token", "notification": { "title": "새 메시지", "body": "홍길동: 안녕하세요" } } </syntaxhighlight> === 중요 사항 === * FCM 서버 키는 서버에서만 사용 * 클라이언트에 노출 금지 == 핵심 정리 == * FCM은 '''전달자''' 역할만 수행한다. * 사용자와 기기의 연결은 '''서버에서 직접 관리'''해야 한다. * 로그인 = 기기 등록 * 로그아웃 = 기기 해제 * 탈퇴 = 모든 기기 해제 == 체크리스트 == * 로그인 시 FCM 토큰 등록 * 로그아웃 시 해당 토큰 삭제 * 탈퇴 시 전체 토큰 삭제 * 토큰 갱신 처리 * FCM 에러 발생 시 토큰 정리
요약:
학교의 모든 지식. SMwiki에서의 모든 기여는 크리에이티브 커먼즈 저작자표시-동일조건변경허락 라이선스로 배포된다는 점을 유의해 주세요(자세한 내용에 대해서는
학교의 모든 지식. SMwiki:저작권
문서를 읽어주세요). 만약 여기에 동의하지 않는다면 문서를 저장하지 말아 주세요.
또한, 직접 작성했거나 퍼블릭 도메인과 같은 자유 문서에서 가져왔다는 것을 보증해야 합니다.
저작권이 있는 내용을 허가 없이 저장하지 마세요!
취소
편집 도움말
(새 창에서 열림)
검색
검색
플러터:푸시 알람(FCM)
편집하기 (부분)
새 주제