-
[Flutter] FCM Push Notification 삽질기카테고리 없음 2024. 8. 5. 14:27
안녕하세요. 지스입니다.
제가 앱 개발시 FCM을 사용하여 Remote Push Notification 쪽을 담당하여 개발하였는데요.제가 겪었던 문제와 헷갈렸던 부분을 정리해 두었습니다. 여러분들도 Push 개발시에도 문제가 없길 바라며,,
1. iOS, AOS 둘 다 앱이 완전히 종료된 상태 (Terminated)에서도 Push 수신이 되어야 합니다.
위 이미지는 FCM 공식 가이드 문서 입니다. 자세히 보면
iOS의 경우 사용자가 앱 전환기에서 애플리케이션을 스와이프하여 닫은 경우 백그라운드 메시지가 다시 작동하려면 수동으로 다시 열어야 합니다.
Android의 경우 사용자가 기기 설정에서 앱을 강제 종료한 경우 메시지가 다시 작동하려면 수동으로 다시 열어야 합니다.이렇게 앱이 완전히 종료 되었으면 푸쉬를 못받는 것 처럼 설명이 되어 있더라구요.
하지만 두 OS 전부 다 앱이 완전히 꺼진 상태에서도 Push가 와야 합니다.
특히 AOS는 별다른 셋팅없이 Push가 잘 오지만, iOS쪽은 그렇지 않습니다.
하지만 iOS도 제대로 셋팅만 되어있다면 앱이 완전히 꺼져있어도 Push가 오게 됩니다.제가 개발할때 사실 AOS는 문제가되는게 없었고 99프로가 iOS 쪽의 문제였습니다 ㅠ
AOS와 iOS의 Push 콜백이 다르다.
저희 앱은
백엔드 : iOS 기종일 때 message body에 notification field추가, AOS는 없음.
클라이언트 : FCM 메시지 수신 -> 로컬 노티로 보여줌 -> 해당 Push를 누르면 -> 페이지 이동 컨트롤
이런식으로 구현을 해놓았는데요. 보통 포그라운드에서 FCM메시지가 발송되면 노티가 안띄워지기 때문에 이런 방법을 많이 사용 하실 것 같아요.앱의 상태는 Foreground, Background, Terminated 이렇게 3가지의 상태가 있지않습니까? 여기에서 각각 호출되는 메서드가 다르답니다,,,
상태 동작 AOS iOS 포그라운드 메시지 수신 FirebaseMessaging.onMessage 호출 -> 로컬 노티 Show AOS와 동일 메시지 선택 FlutterLocalNotificationsPlugin.onDidReceiveNotificationResponse 콜백 호출 됨 AOS와 동일 백그라운드 메시지 수신 FirebaseMessaging.onBackgroundMessage -> 로컬 노티 Show AOS와 동일 메시지 선택 FlutterLocalNotificationsPlugin.onDidReceiveNotificationResponse 콜백 호출 됨 AOS와 동일 종료 상태 메시지 수신 (추측) FirebaseMessaging.onBackgroundMessage 호출 (추측) FCM에서 notification 필드를 추가하면
FCM에서 노티를 띄우는 것 같음메시지 선택 로컬노티를 선택했기 때문에
1.앱 시작점 진입 후
2.FlutterLocalNotificationsPlugin.getNotificationAppLaunchDetails() 메서드 호출
3.메시지가 비동기로 수신되어 담겨져 있습니다. 여기서 페이지 핸들링을 처리하면 됩니다.FCM에서 메시지 노티를 띄웠기 때문에
1.앱 시작점 진입
2.FirebaseMessaging.instance.getInitialMessage() 메서드 호출
3.메시지가 비동기로 수신되어 담겨져 있습니다. 여기서 페이지 핸들링을 처리하면 됩니다.크게 다른점은 Terminated 상태에서
AOS는 FCM -> 로컬노티로 받기 때문에 로컬노티에서 핸들링을 , iOS는 FCM에서 Noti를 띄워주기 때문에FirebaseMessaging.getInitialMessage 으로 메시지를 받아 핸들링을 하면 됩니다.
중요한 점
iOS FirebaseMessaging.instance.getInitialMessage()이 호출될 때
메시지가 null로 오는 경우 가 있습니다. 찾아보니 버그성 인 것 같은데요.이슈 등록도 되어있는 상태 입니다.
저는 해결방법으로 아래 코드 처럼 콜드 스타트시(앱이 완전 종료후 다시 실행될 때) initMessage를 호출하기 전에 0.5초 정도 딜레이를 걸어 두니 message가 제대로 왔습니다. 이점 참고해 주세요.
await Future.delayed(const Duration(milliseconds: 500)); FirebaseMessaging.instance.getInitialMessage().then((message) { if (message != null) { handleMessage( payload: jsonEncode(message.data), ); } });
iOS Background, Terminated 상태에서 메시지가 오지 않을때
일단 셋팅을 FCM 가이드 문서대로 다 되어있는 상태에서 iOS만 Background, Terminated에서 메시지가 안오는 경우가 있었습니다.
이유는 좀 가지각색인 것 같고,, FCM 문서도 제대로 되어있는게 없고,, 그래서 제가 트러블슈팅 했던 걸 공유하겠습니다.- GoogleService-Info.plist 파일이 정상적으로 셋팅이 되어 있는지 확인
- 저희 앱팀은 prod, stage, dev 3개로 나누어 스키마를 사용하고 있었습니다.
하지만 각 스키마 별로 제대로 GoogleService-Info.plist이 셋팅이 안되어 있는 상태가 있어 각 스키마에 맞는 plist 파일을 셋팅할 수 있도록 수정하였습니다.
- 저희 앱팀은 prod, stage, dev 3개로 나누어 스키마를 사용하고 있었습니다.
- entitlements 파일의 APS Environment 벨류값 확인
- 저는 FCM메시지 테스트를 Post man으로 하고 있었는데요...! 항상 앱이 백그라운드로 가거나, 혹은 앱이 완전 꺼졌을때 메시지를 보내면 FCM 서버에서 400 에러가 나고 404에러가 나는 문제가 있었습니다.
몇일간 삽질을 해보니 Runner.entitlements -> APS Environment 가 developer로 되어있더라구요..
이렇게 되면 앱이 꺼지거나 하면 APNS서버에서 저희를 만료 처리하는 것 같더라구요.
해당 값을 production으로 바꾸니 만료가 안되고 정상적으로 작동하였습니다. 이걸로 한 3일은 헤맸습니다.
- message body 수정
이제 테스트시에 서버에서는 메시지를 보내면 무조건 200 OK 떨어지는데 백그라운드, 앱이꺼졌을때 메시지 수신이 안되는 문제가 있었습니다.
현재 메시지 바디 형태가
{ "message": { "token":"", "data": { "title": "타이틀", "subject": "바디!" } } }
이런식으로 서버에서 FCM으로 보내고 있었는데요..!
저 메시지 바디에 notification 을 넣어줘야 iOS에서 정상적으로 노티가 오는게 확인되었습니다.
아무리 이유를 찾아봐도 나오는 곳이 없더라구요...ㅠㅠ그래서 서버측에서 분기를 처서 iOS일 때만 notification 필드를 추가해서 사용하는 것으로 마무리 지었습니다.
{ "message": { "token":"", "data": { "title": "타이틀", "subject": "바디!" }, "notification": { "title": "타이틀", "subject": "바디!" } } }
하지만 이렇게 notification을 추가 해두면 AOS는 메시지가 두번 나오게 됩니다.
FirebaseMessaging.onBackgroundMessage 콜백 메서드가 호출되어 노티 메시지를 보여주고,
FCM에서 노티를 또 보여주게 됩니다.
그래서 서버측에서 분기처리를 해두어 iOS일때만 notification을 추가하는 것으로 마무리 하였습니다.이제 iOS에서도 정상적으로 Push Notification이 오게 되었습니다..ㅎㅎㅎ
마무리
자 이렇게 해서 Remote Push Notification을 정상적으로 셋팅하고 마무리 하였습니다.
사실 글은 짧지만 한 일주일 정도 고생한 것 같아요.. 그래도 삽질을 통하여 새롭게 배운 것 들이 있으니 만족합니다 ㅎㅎ..이제 정말 앱 런칭이 일주일도 안남았는데요...! 이후에는 글을 좀 더 자주 쓸 수 있도록 노력하겠습니다..ㅎㅎ 감사합니다!
- GoogleService-Info.plist 파일이 정상적으로 셋팅이 되어 있는지 확인