ReactNative 에서 LocalPushNotification 기능을 개발하면서 있던 과정들을 기록하려고 한다.
처음에 시나리오와 요구사항을 들었을땐 FCM 밖에 생각이 나지 않았다. 왜냐면 나는 직접 알림시스템을 제로베이스 에서 개발해본 경험도 없었고 항상 "알림" 이라는 키워드가 나오면 "FCM" 으로 해야지~ 라고 파블로프의 개 처럼 학습 되어 있었달까..
우선 주요 문서링크는 이러하다. rnfirebase 설정에 Native 수정 부분이 꽤 있어서 꼼꼼히 읽어 보면서 셋팅하였다. 설정이 잘 되었다면 token 의 값이 잘 나오는걸 확인할수 있다.
(요즘 코드 구조와 구성은 AI 에게 "작성해줘" 한마디면 너무 훌륭하게 나온다. 때문에 앞으로 코드의 기록은 최소한으로 가져가려고 한다.)
import messaging, {FirebaseMessagingTypes} from '@react-native-firebase/messaging';
// FCM 토큰 가져오기
const getFCMToken = async () => {
try {
const token = await messaging().getToken();
setState(prev => ({...prev, fcmToken: token}));
return token;
} catch (error) {
setState(prev => ({...prev, error: error as Error}));
return null;
}
};
그런 다음 FCM 콘솔로 들어가서 Messaging 항목에 첫 캠페인 만들기를 생성하고 메세지 유형을 Firebase 알림 메세지 로 선택한다.
그럼 다음 테스트 알림의 제목과 내용을 작성하고 "테스트메세지 전송" 버튼을 누른다.
그런 다음 위에서 획득한 FCM 토큰을 "FCM 등록 토큰 추가" 에 추가후 체크한 다음에 "테스트" 버튼 누르면 테스트 메세지를 받아 볼수 있다.
위의 코드에서 발급받은 FCM Token 은 하나의 기기에 고유한 id 값 처럼 작용한다. 앱 삭제후 재설치 때는 같은 기기라도 새로운 FCM Token 이 발급된다. 때문에 처음에는 유저와 FCM Token 을 1:1 로 매칭하고, 로그인과 회원가입 할때 유저의 id 와 FCM Token 을 전달하여 oldToken 과 newToken 만 비교하여 다를때만 업데이트 처리 하도록 하면 된다고 생각했다.
근데 생각해보니 한명의 유저가 여러개의 디바이스에서 로그인 하게 된다면 어떻게 될까?
위의 로직대로 처리한다면 이는 업데이트로 인식되어 처리가 된다. 그렇게 되면 가장 최근에 로그인한 디바이스만 알림을 받게 되고 과거 디바이스의 토큰은 유실된다.
때문에 디바이스의 고유한 값인 디바이스 ID 또한 필요할것이라 생각했다. 서버 입장에서 유저의 id, 디바이스 id 두가지 값으로 FCMToken을 관리해야 한다.
추가로 유저의 알림 수신여부도 비교하여 알림 발송 시스템을 관리해야 할것이다.
짧은 고민은 여기서 마치도록 하겠다. 나머지 토큰 관리는 서버가 알아서 하겠지...
위에 FCM 테스트로 알림 데이터를 받아보면 크게 notification, data 2가지의 객체가 존재한다.
{
notification: {
title: "알림 제목",
body: "알림 내용"
},
data: {
// 추가 커스텀 데이터
type: "ORDER",
orderId: "123"
}
}
notification 객체 안에 title, body 정보가 담겨와 이를 알림 시스템에 즉시 표시해주며, data 객체는 커스텀한 추가 정보들을 전달해 준다. 이렇게 notification 객체의 유무를 인지하여 앱이 백그라운드/종료 상태에서도 바로 알림 시스템을 트리거하여 유저에게 노출 시키는 것이 Remote Push 방식이다.
앱이 포그라운드 상태라면 onMessage 핸들러에서 처리도 가능하다
messaging().onMessage(async remoteMessage => {
// notification 객체가 있는 경우 자동으로 시스템 알림 표시
console.log('Received foreground message:', remoteMessage);
});
- 백그라운드/종료 상태
✅ 시스템 알림 자동 표시
❌ onMessage 동작하지 않음
- 포그라운드 상태
✅ onMessage 동작
❌ 시스템 알림 자동 표시되지 않음 (직접 구현 필요)
그럼 만약 notification 객체가 없다면 어떻게 동작할까?
notification 객체 없이 data 객체만 존재한다면 알림 시스템은 이를 유저에게 노출 시키지 않는다. 즉시 알림이 노출 되지 않지만 onMessage 리시버에 data 객체는 전달되고 onMessage 내부에 작성된 로직은 실행 가능하다. 이를 data-only 방식이라고 부른다.
data-only 방식의 장점은 onMessage 의 내부 로직을 실행할수 있다는 점이다. 예를 들면 data 에 단순히 Action 값만 전달하여 내부로직에 설정된 내용을 가공하여 알림 시스템으로 보낼수 있다. 이때 알림 시스템에 노출 시키기 위해서 사용하는것이 Local push 방식이다.
하지만 가장큰 단점은 백그라운드/종료 상태에서는 처리가 불가하다!
messaging().onMessage(async remoteMessage => {
if (remoteMessage.data?.action === 'SHOW_TICKET_ALERT') {
// Local Push Notification 생성
PushNotification.localNotification({
title: "티켓 알림",
message: remoteMessage.data.message,
// 추가 커스텀 설정
});
}
});
- 백그라운드/종료 상태
❌ 시스템 알림 표시되지 않음
❌ onMessage 동작하지 않음
- 포그라운드 상태
✅ onMessage 동작
✅ Local Push를 통한 커스텀 알림 표시 가능
✅ 데이터 가공 후 알림 표시 가능
FCM 설정을 하면서 몇 가지 중요한 사실을 알게 되었는데, 우선 가장 중요한 것은 FCM 서비스는 언제 쓰냐는 것이다.
FCM 은 원하는 시간에, 원하는 내용을, 대용량으로(서버가) 알림을 보낼수 있다는 장점 그리고 모바일 디바이스가 어떤 상태에 있어도 (온그라운드, 백그라운드, 종료) 알림 트리거를 발생시킬수 있다는점이 있다.
FCM 의 장점과 언제 쓰이는지를 알게되다 보니 내가 구현하려는 티켓 알림 기능이 FCM 의 기능이 전혀 없어도 된다는걸 깨닫게 되었다.
원하는 시간? 필요 없다. 왜냐면 "티켓 사용일 전날 오후 2시" 로 고정되어있다.
원하는 내용? 필요 없다. 내용은 "내일 티켓 써라" 라는 맥락으로 고정된 텍스트다.
대용량 전송및 토큰 등록? 필요 없다
알람이 설정 되는 이벤트는 티켓을 구매 했을때로 한정되어 있기 때문에 티켓 결제가 성공 했을때 발생하는 이벤트에 단순히 LocalPush 를 등록해주면 되기 때문이다. 그리고 결제 취소시에는 LocalPush 알림 예약을 취소하면 된다.
결론은 이 알림 기능은 FCM 을 이용한 remote 알림 시스템이 필요한것이 아니라 더욱 단순한 로컬 알림설정 정도의 기능 이란것을 알게되었다.
원래는 서버가 활성화된 티켓을 조회하고, 알림 허용여부를 필터하고, 전날 14시에 배치 작업을 돌려서 유저당 등록된 FCMToken 을 기준으로 remotepush 를 날리고... 이런 작업들을 생각했었는데 나의 고찰(?) 덕분에 서버 개발자의 엄청난 Task 를 하나 줄여주게 되었다!
라고 하기엔 나도 좀만더 생각해보면 될것을.. 아무튼 결론은 당장은 FCM 없이 LocalPush 로 구현하기로 결정했다.
물론 단점은 존재하는데
유저가 앱을 삭제하면 당연하게도 예약된 LocalPush 알림은 모두 증발된다!
추가로 유저가 직접결제 취소 하는 이외의 케이스들이 발생했을때 "알림 예약 취소" 이벤트를 발생시킬수가 없다!
하지만 뭐 티켓 리스트를 조회해서 활성화된 티켓만 골라내서 다시 알림을 등록 또는 취소 하면 어떻게든 될것같다.
원래 LocalPush 구현까지 한번에 정리하려고했는데 "LocalPush 로 충분하겠다" 라고 확신하게된 이전의 과정이 꽤나 길었구나..! 하고 되돌아보게 된다. 다음에는 자세한 LocalPush 구현 방법에 대해서 정리 하려고한다.