Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions hooks/use-push-notification.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -20,20 +26,22 @@
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;

Check failure on line 34 in hooks/use-push-notification.ts

View workflow job for this annotation

GitHub Actions / typecheck

Argument of type 'string | object' is not assignable to parameter of type 'string'.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

기존 구현에서는 data?.type이 없더라도 알림을 정상적으로 표시했으나, 변경된 코드에서는 !type 조건으로 인해 type이 없는 일반 알림(예: Firebase 콘솔에서 테스트로 발송한 알림이나 data 페이로드가 없는 순수 알림)이 포어그라운드에서 무시되는 문제가 발생합니다.\n\ntype이 존재하고 TRACKING_TYPES에 포함된 경우에만 제외하도록 조건을 수정해야 합니다.

Suggested change
const type = remoteMessage.data?.type;
if (!type || TRACKING_TYPES.has(type)) return;
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(
Expand All @@ -51,7 +59,7 @@
});

return () => {
foregroundSub.remove();
unsubscribeFcm();
tapSub.remove();
};
}, [router, enabled]);
Expand Down
Loading