From 0420089d354b2ae816ad092dd24adcd3c89c7ca4 Mon Sep 17 00:00:00 2001 From: Jio Date: Fri, 5 Jun 2026 02:45:26 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix=20:=20=EC=9D=BC=EB=B0=98=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=8F=AC=EC=96=B4=EA=B7=B8=EB=9D=BC=EC=9A=B4?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=8B=A0=20Firebase=20onMessage=EB=A1=9C?= =?UTF-8?q?=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hooks/use-push-notification.ts | 40 ++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/hooks/use-push-notification.ts b/hooks/use-push-notification.ts index f179cee..5e32b0d 100644 --- a/hooks/use-push-notification.ts +++ b/hooks/use-push-notification.ts @@ -1,17 +1,23 @@ import * as Sentry from "@sentry/react-native"; import { api } from '@/lib/api'; import { getApp } from '@react-native-firebase/app'; -import { getMessaging, getToken } from '@react-native-firebase/messaging'; +import { getMessaging, getToken, onMessage } from '@react-native-firebase/messaging'; import * as Device from 'expo-device'; import * as Notifications from 'expo-notifications'; import { useRouter } from 'expo-router'; import { useEffect } from 'react'; import { Platform } from 'react-native'; +// 트래킹 전용 타입 — use-tracking-fcm.ts에서 처리하므로 여기서 제외 +const TRACKING_TYPES = new Set(['TRACKING_PHOTO_MILESTONE', 'TRACKING_SUMMIT_REACHED']); /** * 앱 시작 시 FCM 토큰을 서버에 등록하고 * 포어그라운드 알림 수신 및 알림 탭 이벤트를 처리하는 훅 + * + * 역할 분리: + * - 이 훅: 트래킹 외 모든 FCM 포어그라운드 수신 → 시스템 배너 표시 + * - use-tracking-fcm.ts: TRACKING_PHOTO_MILESTONE → 인앱 PhotoWindowBanner */ export function usePushNotification(enabled = true): void { const router = useRouter(); @@ -20,20 +26,22 @@ export function usePushNotification(enabled = true): void { if (!enabled) return; registerFcmToken(); - // 포어그라운드 알림 수신 — Firebase SDK 충돌로 배너가 안 뜨는 경우 로컬 알림으로 재표시 - // TRACKING_PHOTO_MILESTONE은 use-tracking-fcm.ts에서 별도 처리하므로 제외 (무한 루프 방지) - const foregroundSub = Notifications.addNotificationReceivedListener( - async (notification) => { - const data = notification.request.content.data as PushData | null; - if (data?.type === 'TRACKING_PHOTO_MILESTONE') return; - const { title, body } = notification.request.content; - if (!title && !body) return; - await Notifications.scheduleNotificationAsync({ - content: { title: title ?? 'SEMOSAN', body: body ?? '' }, - trigger: null, - }); - }, - ); + // 포어그라운드 FCM 수신 — Firebase SDK가 expo-notifications보다 우선하므로 onMessage 사용 + // 트래킹 타입은 use-tracking-fcm.ts에서 처리하므로 제외 + const fcmMessaging = getMessaging(getApp()); + const unsubscribeFcm = onMessage(fcmMessaging, async (remoteMessage) => { + const type = remoteMessage.data?.type; + if (!type || TRACKING_TYPES.has(type)) return; + + const title = remoteMessage.notification?.title ?? remoteMessage.data?.title as string | undefined; + const body = remoteMessage.notification?.body ?? remoteMessage.data?.body as string | undefined; + if (!title && !body) return; + + await Notifications.scheduleNotificationAsync({ + content: { title: title ?? 'SEMOSAN', body: body ?? '' }, + trigger: null, + }); + }); // 알림 탭 → 화면 이동 const tapSub = Notifications.addNotificationResponseReceivedListener( @@ -51,7 +59,7 @@ export function usePushNotification(enabled = true): void { }); return () => { - foregroundSub.remove(); + unsubscribeFcm(); tapSub.remove(); }; }, [router, enabled]); From d517fa1e1efad0cf79beb20aa9c85097dd28619e Mon Sep 17 00:00:00 2001 From: Jio Date: Fri, 5 Jun 2026 02:50:54 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix=20:=20use-push-notification=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hooks/use-push-notification.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hooks/use-push-notification.ts b/hooks/use-push-notification.ts index 5e32b0d..0fbd4c8 100644 --- a/hooks/use-push-notification.ts +++ b/hooks/use-push-notification.ts @@ -33,8 +33,8 @@ export function usePushNotification(enabled = true): void { const type = remoteMessage.data?.type; if (!type || TRACKING_TYPES.has(type)) return; - const title = remoteMessage.notification?.title ?? remoteMessage.data?.title as string | undefined; - const body = remoteMessage.notification?.body ?? remoteMessage.data?.body as string | undefined; + const title = remoteMessage.notification?.title ?? (typeof remoteMessage.data?.title === 'string' ? remoteMessage.data.title : undefined); + const body = remoteMessage.notification?.body ?? (typeof remoteMessage.data?.body === 'string' ? remoteMessage.data.body : undefined); if (!title && !body) return; await Notifications.scheduleNotificationAsync({ From df25b82328e65ac91b997bb5d5520be81cdc460c Mon Sep 17 00:00:00 2001 From: Jio Date: Fri, 5 Jun 2026 02:52:46 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix=20:=20use-push-notification=20type=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EA=B0=80=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hooks/use-push-notification.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/use-push-notification.ts b/hooks/use-push-notification.ts index 0fbd4c8..7b436e8 100644 --- a/hooks/use-push-notification.ts +++ b/hooks/use-push-notification.ts @@ -30,7 +30,7 @@ export function usePushNotification(enabled = true): void { // 트래킹 타입은 use-tracking-fcm.ts에서 처리하므로 제외 const fcmMessaging = getMessaging(getApp()); const unsubscribeFcm = onMessage(fcmMessaging, async (remoteMessage) => { - const type = remoteMessage.data?.type; + const type = typeof remoteMessage.data?.type === 'string' ? remoteMessage.data.type : undefined; if (!type || TRACKING_TYPES.has(type)) return; const title = remoteMessage.notification?.title ?? (typeof remoteMessage.data?.title === 'string' ? remoteMessage.data.title : undefined); From ed7d03ba37bb549764d4c42d0f9eb2a622fc61c8 Mon Sep 17 00:00:00 2001 From: Jio Date: Fri, 5 Jun 2026 02:54:32 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix=20:=20type=20=EC=97=86=EB=8A=94=20?= =?UTF-8?q?=EC=88=9C=EC=88=98=20=EC=95=8C=EB=A6=BC=EB=8F=84=20=ED=8F=AC?= =?UTF-8?q?=EC=96=B4=EA=B7=B8=EB=9D=BC=EC=9A=B4=EB=93=9C=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hooks/use-push-notification.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/use-push-notification.ts b/hooks/use-push-notification.ts index 7b436e8..18a32b7 100644 --- a/hooks/use-push-notification.ts +++ b/hooks/use-push-notification.ts @@ -31,7 +31,7 @@ export function usePushNotification(enabled = true): void { const fcmMessaging = getMessaging(getApp()); const unsubscribeFcm = onMessage(fcmMessaging, async (remoteMessage) => { const type = typeof remoteMessage.data?.type === 'string' ? remoteMessage.data.type : undefined; - if (!type || TRACKING_TYPES.has(type)) return; + if (type && TRACKING_TYPES.has(type)) return; const title = remoteMessage.notification?.title ?? (typeof remoteMessage.data?.title === 'string' ? remoteMessage.data.title : undefined); const body = remoteMessage.notification?.body ?? (typeof remoteMessage.data?.body === 'string' ? remoteMessage.data.body : undefined);