작성
·
135
·
수정됨
0
안녕하세요, 강의를 듣다 궁금한 점이 생겨 질문드립니다. foreground 상태 인 경우에도 Push Alert가 위에서 발생하는 앱들이 있는데 이 같은 경우는 따로, 해당 데이터가 왔을 떄 뭔가 Alert창이나, 위에서 아래로 알림창 같은게 내려오는 컴포넌트같은 것을 따로 구현하여 보여주는 것인가요??
Toast와 같은 컴포넌트를 만들어서 보여주는 것인지, React-native-push-notification에서 따로 할 수 있는 방법이 있는지 궁금합니다!
그리고 Channel 같은 경우는 프론트에서 먼저 구성한 후, 백엔드와 맞추어야 하는건지 궁금합니다!!!
답변 2
0
일단 importance: 'high'로 하시면 알아서 알림이 뜰 것이고요. 기본 알림이 마음에 안 들어 수정해야 하는 경우에는 백엔드로부터 push 알림이 오면 onNotification에서 localNotification 띄워주시면 됩니다. notification 객체 안에 정보를 통해 분기처리 하시면 되고요. 다만 localNotification이 뜨면 다시 onNotification 콜백이 호출되므로 localNotification의 경우에는 onNotification에서 아무 것도 하지 않게 막아두셔야 합니다(무한 호출 방지).
백엔드에 이미 todo라고 channelId 하드코딩해두었으므로 프론트에서는 channelId를 보내실 필요가 없습니다.
PushNotification.localNotification({
channelId: Platform.OS === 'android' ? channelId : undefined, // Android 채널
title: "My Notification",
message: "This is a test notification",
playSound: true,
soundName: Platform.OS === 'ios' ? "customSound.caf" : "default",
category: "categoryID", // iOS에서 카테고리 설정
});
import {LogBox, Platform} from 'react-native';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {DevToolsBubble} from 'react-native-react-query-devtools';
import messaging from '@react-native-firebase/messaging';
import PushNotification, {Importance} from 'react-native-push-notification';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import Toast, {
BaseToast,
BaseToastProps,
ErrorToast,
} from 'react-native-toast-message';
import AppSetupContainer from './src/containers/AppSetupContainer.tsx';
import RootNavigator from './src/navigators/root/RootNavigator.tsx';
LogBox.ignoreLogs(['Sending']);
const toastConfig = {
success: (props: BaseToastProps) => (
<BaseToast
{...props}
style={{borderLeftColor: 'pink'}}
contentContainerStyle={{paddingHorizontal: 15}}
text1Style={{
fontSize: 15,
fontWeight: '400',
}}
/>
),
error: (props: BaseToastProps) => (
<ErrorToast
{...props}
text1Style={{
fontSize: 17,
}}
text2Style={{
fontSize: 15,
}}
/>
),
};
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Messaging handled in the background', remoteMessage);
console.log('underground', remoteMessage);
});
// Must be outside of any component LifeCycle (such as `componentDidMount`).
PushNotification.configure({
// (optional) Called when Token is generated (iOS and Android)
onRegister: function (token) {
// 기기마다 고유한 토큰
console.log('TOKEN:', token);
},
// (required) Called when a remote is received or opened, or local notification is opened
onNotification: function (notification: any) {
console.log('NOTIFICATION:', notification);
// iOS에서 foreground 상태이고, 알림이 로컬 알림에서 온 것이 아니라면만 로컬 알림을 생성
if (
Platform.OS === 'ios' &&
notification.foreground &&
!notification.userInteraction // 로컬 알림으로 인한 상호작용이 아닌 경우
) {
PushNotification.localNotification({
title: notification.title,
message: notification.message,
playSound: true,
soundName: 'default',
priority: 'high',
vibrate: true,
});
}
// 알림 처리 완료 후 마무리
notification.finish(PushNotificationIOS.FetchResult.NoData);
},
// (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
onAction: function (notification) {
console.log('ACTION:', notification.action);
console.log('NOTIFICATION:', notification);
// process the action
},
// (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
onRegistrationError: function (err) {
console.error(err.message, err);
},
// IOS ONLY (optional): default: all - Permissions to register.
permissions: {
alert: true,
badge: true,
sound: true,
},
// Should the initial notification be popped automatically
// default: true
popInitialNotification: true,
/**
* (optional) default: true
* - Specified if permissions (ios) and token (android and ios) will requested or not,
* - if not, you must call PushNotificationsHandler.requestPermissions() later
* - if you are not using remote notification or do not have Firebase installed, use this:
* requestPermissions: Platform.OS === 'ios'
*/
requestPermissions: true,
});
// PushNotification 채널 생성 함수
const createNotificationChannel = (
channelId: string,
channelName: string,
channelDescription: string,
) => {
PushNotification.createChannel(
{
channelId,
channelName,
channelDescription,
importance: Importance.HIGH,
soundName: 'default',
vibrate: true,
},
(created: boolean) =>
console.log(`channel ${channelName} 생성, ${created}`),
);
};
// 여러 채널 생성
const channels = [
{id: 'TODO', name: '할 일', description: '할 일 알림'},
{id: 'PLAN', name: '일정', description: '일정 알림'},
{id: 'COMMENT', name: '댓글', description: '댓글 알림'},
{id: 'POST', name: '게시글', description: '게시글 알림'},
{id: 'CHATROOM', name: '채팅', description: '채팅 알림'},
{id: 'MOIM', name: '모임', description: '모임 알림'},
{id: 'REVIEW', name: '리뷰', description: '리뷰 알림'},
{id: 'EVENT', name: '이벤트', description: '이벤트 알림'},
];
// 채널 생성 반복
channels.forEach(channel => {
createNotificationChannel(channel.id, channel.name, channel.description);
});
function App() {
return (
<AppSetupContainer>
<GestureHandlerRootView>
<RootNavigator />
<Toast config={toastConfig} />
{/*<DevToolsBubble />*/}
</GestureHandlerRootView>
</AppSetupContainer>
);
}
let AppEntryPoint = App;
if (process.env.STORYBOOK_ENABLED) {
AppEntryPoint = require('./.ondevice').default;
}
if (__DEV__) {
require('./ReactotronConfig');
}
export default AppEntryPoint;
아래와 같이 flag를 세워 재귀를 해결하는 방식을 채택해서 코드를 수정해보았습니다.
import {LogBox, Platform} from 'react-native';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {DevToolsBubble} from 'react-native-react-query-devtools';
import messaging from '@react-native-firebase/messaging';
import PushNotification, {Importance} from 'react-native-push-notification';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import Toast, {
BaseToast,
BaseToastProps,
ErrorToast,
} from 'react-native-toast-message';
import AppSetupContainer from './src/containers/AppSetupContainer.tsx';
import RootNavigator from './src/navigators/root/RootNavigator.tsx';
LogBox.ignoreLogs(['Sending']);
const toastConfig = {
success: (props: BaseToastProps) => (
<BaseToast
{...props}
style={{borderLeftColor: 'pink'}}
contentContainerStyle={{paddingHorizontal: 15}}
text1Style={{
fontSize: 15,
fontWeight: '400',
}}
/>
),
error: (props: BaseToastProps) => (
<ErrorToast
{...props}
text1Style={{
fontSize: 17,
}}
text2Style={{
fontSize: 15,
}}
/>
),
};
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Messaging handled in the background', remoteMessage);
console.log('underground', remoteMessage);
});
let isNotificationHandled = false; // 플래그 초기화
// Must be outside of any component LifeCycle (such as `componentDidMount`).
PushNotification.configure({
// (optional) Called when Token is generated (iOS and Android)
onRegister: function (token) {
// 기기마다 고유한 토큰
console.log('TOKEN:', token);
},
// (required) Called when a remote is received or opened, or local notification is opened
onNotification: function (notification: any) {
if (isNotificationHandled) {
// 이미 처리된 알림이면 종료
return;
}
console.log('NOTIFICATION:', notification);
// iOS에서 foreground 상태이고, 알림이 로컬 알림에서 온 것이 아니라면만 로컬 알림을 생성
if (
Platform.OS === 'ios' &&
notification.foreground &&
!notification.userInteraction // 로컬 알림으로 인한 상호작용이 아닌 경우
) {
PushNotification.localNotification({
title: notification.title,
message: notification.message,
playSound: true,
soundName: 'default',
priority: 'high',
vibrate: true,
});
}
// 알림 처리 완료 후 플래그 설정
isNotificationHandled = true;
// 알림 처리 완료 후 마무리
notification.finish(PushNotificationIOS.FetchResult.NoData);
},
// (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
onAction: function (notification) {
console.log('ACTION:', notification.action);
console.log('NOTIFICATION:', notification);
// process the action
},
// (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
onRegistrationError: function (err) {
console.error(err.message, err);
},
// IOS ONLY (optional): default: all - Permissions to register.
permissions: {
alert: true,
badge: true,
sound: true,
},
// Should the initial notification be popped automatically
// default: true
popInitialNotification: true,
/**
* (optional) default: true
* - Specified if permissions (ios) and token (android and ios) will requested or not,
* - if not, you must call PushNotificationsHandler.requestPermissions() later
* - if you are not using remote notification or do not have Firebase installed, use this:
* requestPermissions: Platform.OS === 'ios'
*/
requestPermissions: true,
});
// PushNotification 채널 생성 함수
const createNotificationChannel = (
channelId: string,
channelName: string,
channelDescription: string,
) => {
PushNotification.createChannel(
{
channelId,
channelName,
channelDescription,
importance: Importance.HIGH,
soundName: 'default',
vibrate: true,
},
(created: boolean) =>
console.log(`channel ${channelName} 생성, ${created}`),
);
};
// 여러 채널 생성
const channels = [
{id: 'TODO', name: '할 일', description: '할 일 알림'},
{id: 'PLAN', name: '일정', description: '일정 알림'},
{id: 'COMMENT', name: '댓글', description: '댓글 알림'},
{id: 'POST', name: '게시글', description: '게시글 알림'},
{id: 'CHATROOM', name: '채팅', description: '채팅 알림'},
{id: 'MOIM', name: '모임', description: '모임 알림'},
{id: 'REVIEW', name: '리뷰', description: '리뷰 알림'},
{id: 'EVENT', name: '이벤트', description: '이벤트 알림'},
];
// 채널 생성 반복
channels.forEach(channel => {
createNotificationChannel(channel.id, channel.name, channel.description);
});
function App() {
return (
<AppSetupContainer>
<GestureHandlerRootView>
<RootNavigator />
<Toast config={toastConfig} />
{/*<DevToolsBubble />*/}
</GestureHandlerRootView>
</AppSetupContainer>
);
}
let AppEntryPoint = App;
if (process.env.STORYBOOK_ENABLED) {
AppEntryPoint = require('./.ondevice').default;
}
if (__DEV__) {
require('./ReactotronConfig');
}
export default AppEntryPoint;
이렇게 onNotification 내부에 선언하여 해결하는 방식이 맞는지, 조금 더 좋은 깔끔한 방식이 있는지 궁금합니다.
그리고, 해당 방식으로 했을 떄, foreground 상태가 아닐때 푸시알림을 클릭하고, 다시 푸시알림을 보낼때 false로 계속 인식하여, 푸시알림이 오지 않는 현상이 발생합니다. 강사님은 어떤 식으로 이러한 문제점을 해결할려고 접근하셨는지 궁금합니다!
2. 상단바에 보여줄 알림 개수 같은 경우는 원래는 소켓같은 것으로 구현할려했으나
PushNotification.getApplicationIconBadgeNumber()와, setApplicationBadgeNumber() 메서드로 뭔가 구현 할 수 있을 것 같은데, 실제로도 이걸 사용해서 많이 구현하는지 궁금합니다.
onNotification에서 호출 될 떄, 백엔드에서 주는 count 개수로, set을 한 후, 해당 알림을 보여주는 스크린에 들어왔을떄 useEffect로 다시 0으로 초기화하는, 이런식으로 구현해도 괜찮을지 더 좋은 방법이 있는지 궁금합니다!
0
안녕하세요, 인프런 AI 인턴입니다.
김용민님의 질문에 대한 답변으로, React Native에서 포그라운드 상태일 때 푸시 알림을 수신하여 특정 형태(예: 상단 알림 또는 토스트)로 사용자에게 보여주는 방법에 대해 궁금하신 것 같습니다.
React Native에서 포그라운드 상태에서 알림을 표시하는 방법에 대한 논의가 있었습니다. 이와 관련하여 아래의 링크에서 유사한 상황에 대한 답변을 찾을 수 있습니다:
“remote push notification이 포그라운드에서 팝업되지 않아요”에서는 react-native-push-notification
라이브러리 사용 시 포그라운드에서 알림이 팝업되지 않는 문제와 관련된 해결 방법이 제시되어 있습니다. 질문자는 포그라운드 상태에서 알림 팝업을 원했고, 이에 대하여 importance
와 priority
옵션을 high
로 설정하는 것으로 해결할 수 있음이 제시되었습니다.
“안녕하세요 다이나믹하게 foreground 알림 조절 관련 질문드립니다.”에서는 포그라운드 상태에서 알림을 다이나믹하게 제어하고자 하는 방법에 대한 질문에 대해, foreground_notification
옵션을 통한 조절 방법을 설명하고 있습니다. 이는 특정 조건 하에 포그라운드 알림을 사용자 정의로 제어하고자 할 때 유용할 수 있습니다.
이 정보들은 react-native-push-notification
라이브러리를 사용하여 포그라운드 상태에서 사용자에게 알림을 보여주는 다양한 방식에 대해 설명하고 있습니다. 따라서, 특정 알림 형태(예: 알림창, 토스트 등)로 직접 구현하기보다는 라이브러리에서 제공하는 설정을 조절하여 원하는 결과를 얻을 수 있을 것입니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
제가 이해한 방식이 맞는지 체크 부탁드립니다!!
1. 프론트엔드에서 PushNotification.createChannel로 알림 채널을 생성
여기서 importance high를 하면, foreground 상태에서도, 푸시알림이 오게됨
2. 프론트엔드에서 백엔드 통신시 (이미, FCM 토큰을 전달했다는 가정) 만약, Todo를 만든다는 가정
POST: /api/v1/todo
서버 -> 프론트
POST: /api/v1/todo
해당 엔드포인트로, 응답이 올 때, 푸시 알림을 전송해줌 (채널: todo)로
안드는 channel 개념이 있고
iOS는 channel 개념이 없으나, category가 이를 대신. 이게 맞을까요?
todo라는 채널로, 데이터를 보내줍니다.
프론트
받은 푸시알림은
이 쪽에서 데이터가 들어오므로, 채널별로 분기 처리를 하여 작업해주면 되나요??
채널이 여러개일 경우, 계속 위의 작업을 반복하면 되는게 맞는지 궁금합니다!
이렇게 백엔드와 프론트엔드가 동작하는게 맞는지 AI 답변으로도 잘 이해가 되지 않아 확인차 다시 답글 남깁니다!!
제가 원하는 형태는 아래와 같이 foreground 형태로 나오는 것 입니다