React Native(expo)와 firebase의 fcm을 통해서 푸시 메세지 기능을 구현하고 있었다.
https://docs.expo.dev/push-notifications/fcm-credentials/ <= 공식문서와
https://youtu.be/yCBecuxzUuU?si=GXzR5VVURsYEQ4yR <= 유튜브 영상을 통해 기능을 구현했고, 안드로이드 에뮬레이터에서 푸시메세지가 오는 것을 확인할 수 있었다.
코드를 살펴보도록 하자.
import React, { useRef } from 'react';
import { View, Text } from 'react-native';
import WebView from 'react-native-webview';
import useHardwareBack from './hooks/useHardwareBack';
import useSplash from './hooks/useSplash';
import useIsConnected from './hooks/useIsConnected';
import useNotification from './hooks/useNotification';
const INJECTEDJAVASCRIPT = `const meta = document.createElement('meta'); meta.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0'); meta.setAttribute('name', 'viewport'); document.getElementsByTagName('head')[0].appendChild(meta); `;
export default function Native() {
const webViewRef = useRef<WebView>(null);
const handleNavigationStateChange = useHardwareBack(webViewRef);
const handleLoad = useSplash();
const isConnected = useIsConnected();
useNotification();
return (
<View style={{ flex: 1 }}>
{isConnected ? (
<WebView
style={{ flex: 1 }}
textZoom={100}
source={{
uri: 'https://today-s-horoscope.vercel.app/',
}}
injectedJavaScript={INJECTEDJAVASCRIPT}
onNavigationStateChange={handleNavigationStateChange}
ref={webViewRef}
onLoadEnd={handleLoad}
/>
) : (
<View
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
height: '100%',
backgroundColor: '#191a18',
}}>
<View
style={{
display: 'flex',
alignItems: 'center',
gap: 24,
}}>
<Text style={{ fontSize: 18, fontWeight: '500', color: '#d3d3d3' }}>인터넷 연결</Text>
<Text style={{ fontSize: 14, fontWeight: '500', color: '#838f90' }}>
오프라인 상태입니다. 인터넷 연결을 확인하세요.
</Text>
</View>
</View>
)}
</View>
);
}
import { useEffect } from 'react';
import messaging from '@react-native-firebase/messaging';
import * as Notifications from 'expo-notifications';
export default function useNotification() {
const requestUserPermission = async () => {
const settings = await Notifications.requestPermissionsAsync();
const enabled = settings.granted || settings.ios?.status === Notifications.IosAuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('Authorization status:', settings);
}
return enabled;
};
const setupNotificationHandler = async () => {
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
};
useEffect(() => {
(async () => {
await setupNotificationHandler();
const permissionGranted = await requestUserPermission();
if (permissionGranted) {
const token = await messaging().getToken();
console.log(token);
} else {
console.log('Permission not granted');
}
const initialNotification = await messaging().getInitialNotification();
if (initialNotification) {
console.log('Notification caused app to open from quit state:', initialNotification.notification);
}
messaging().onNotificationOpenedApp(remoteMessage => {
console.log('Notification caused app to open from background state:', remoteMessage.notification);
});
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);
});
const unsubscribe = messaging().onMessage(async remoteMessage => {
await Notifications.scheduleNotificationAsync({
content: {
title: remoteMessage.notification?.title,
body: remoteMessage.notification?.body,
data: remoteMessage.data,
},
trigger: null,
});
});
return () => unsubscribe();
})();
}, []);
}
파이어베이스 콘솔에서 메세지를 보낼때 안드로이드 에뮬레이터에서는 푸시메세지가 잘 작동하였는데, apk 파일로 빌드해서 실제 디바이스에서 테스트 해보니 푸시메세지가 오지 않는 이슈가 발생했다. SHA-1, SHA-256과 같은 인증서 지문의 문제일까 싶어서 파이어베이스와 expo.dev에서 값들을 확인해 봤는데 정확히 일치되어있어서 원인을 파악하기가 힘들었다.
원인을 파악하기 위해 expo-dev-client를 도입하였다.
npx expo install expo-dev-client
이후 다시 빌드 하였다.
eas build -p android --profile preview
expo-dev-client 도입하기 전에는 확인할 수 없었던 에러가 발생했고, 빌드 로그에서 원인을 파악할 수 있었다.
google-services.json 파일이 누락되었다고 한다... EAS 빌드는 깃에서 추적하는 파일만 업로드한다고 한다. gitignore 파일에 google-services.json 파일을 추가한 것이 원인이었다..
해결법은 매우 간단했다. google-services.json 파일을 gitignore에서 google-services.json을 주석처리하니 빌드에 성공했고, 푸시 메세지도 잘 작동하였다.
하루를 고생하며 이 문제를 해결하려 시간을 보냈는데, 코드 한줄만 수정하면 되는거였다니.. expo-dev-client를 도입하지 않았다면 훨씬 더 오랜 기간이 걸렸을 것 같다. google-services.json 파일에는 api key와 같은 민감한 정보들도 포함되어있기 때문에 빌드를 할 때에만 gitignore에서 제외시키고, 코드를 커밋할 때에는 gitignore를 통해 버전관리되지 않게끔 보안에 신경써야할 것 같다.