[ReactNative] 푸쉬알림 공식문서 따라하기 - (Firebase Cloud Messaging, @react-native-firebase, notification) 안드로이드

권준혁·2021년 1월 21일
11

ReactNative

목록 보기
2/5
post-thumbnail

리액트 네이티브에서 푸쉬알림을 위해 FCM을 사용했다.
서버측에서 클라이언트측으로 전송을 하기 위해선 소켓을 이용해야 하지만, FCM을 이용하면 소켓을 이용하지 않고도 요청할 수 있다.
특히 무료라는 점이 강점이다

참고 링크

react native firebase 라이브러리
firebase docs: cloud messaging
firebase console

git

테스트 코드 주소

핸드폰에서의 푸쉬알림기능이다.
FCM과 @react-native-firebase 라이브러리를 이용해 푸쉬알림을 구현해보자.
윈도우 환경이라서 안드로이드만 진행해보려고 한다.
iOS는 몇가지 다른점과 주의사항이 추가된다.

yarn add @react-native-firebase/app @react-native-firebase/messaging

코어기능을 제공하는 @react-native-firebase/app를 기본적으로 설치하고, firebase에서 제공하는 다양한 기능들에 맞춰 각각 추가 라이브러리를 설치해준다.
FCM을 이용할 것이기 때문에 @react-native-firebase/messaging를 설치한다.

테스트해보고자 하는 시나리오는 3가지이다.

  • 앱을 다운받은 전체 유저에게 push보내기
  • topic을 구독한 사람에게 push보내기
  • 특정 유저에게 push보내기

파이어베이스 프로젝트 생성

파이어베이스 공식문서는 AWS에 비해 친절한 느낌이든다. 읽어보면 충분히 이해할 수 있는 내용이다.

먼저 파이어베이스 콘솔에서 프로젝트를 추가한다.

3단계의 짧은 과정을 거치면 프로젝트가 완성되는데, 여기에 안드로이드 버튼을 눌러서 앱을 추가해준다.

안드로이드 앱 추가하기

단순히 테스트 목적이라면 Android패키지 이름만 잘 작성해주면 된다.
파이어베이스에서 생성하는 Android패키지 이름은 프로젝트 내에서의 패키지이름과 같아야하니 주의하자

google-service.json을 다운받아 지정된 위치에 넣어준다.
사진에서 보이는 것 처럼 [프로젝트이름] > android > app에 넣어주면 된다.

Firebase SDK추가

파이어베이스에 친절하게 설명되어있어서 굳이 설명을 덧붙일 필요가 있을까 싶지만, android폴더 내부를 살펴보면

  • [project] 에서의 build.gradle
  • [project]>app 에서의 build.gradle

두 개의 파일이 있다.
각각 다르다는 것을 인지하고 추가해야 한다.


별다른 에러가 발생하지 않았다면, 앞서 설치했던 @react-native-firebase/app, @react-native-firebase/messaging을 이용해 푸쉬 기능을 구현해볼 차례다

iOS의 경우엔 추가적으로 진행해야할 작업이 있다.
해당 라이브러리 공식문서의 주의사항을 요약했다.

진행하기에 앞서 필요한 ReactNative와 FirebaseProject가 갖춰져있다.
아까 말했던 것처럼 파이어베이스 패키지이름과 프로젝트 패키지이름을 다시 확인한다.
하이라이트 표시 된 경로에서 확인할 수 있다.


Cloud Messaging 이해하기

react-native-firebase 공식문서
iOS의 경우에는 추가적인 사용자의 승인이 필요하기 때문에 코드가 추가된다. 이 부분은 넘어가고 Receiving messages부터 보면 되겠다.

일반적인 사용법은 아래와 같다.

알림을 보여주고, message로 전달된 데이터를 비동기 저장소에 저장하고, UI를 업데이트 할 수 있다.
먼저 유저 디바이스의 상태를 세 가지로 분류할 수 있다.

활성화 된 Foreground 상태, 앱을 이용 중일 때,
비 활성화된 Background 상태, 홈버튼 을 누르는 등 상태 (종료된 상태와 다르다. 작업관리자에서 확인 가능한 경우)
앱이 종료된 상태 Quit 로 나누어 생각할 수 있다.

윗 내용은 쉽게 생각하면 전달하는 메시지 객체에 대해 notification속성과 data속성이 있거나 없을 때에 대한 내용이다.
위 표를 요약하면 Foreground 상태에서는 이벤트 핸들러의 onMessage를 이용해 핸들링 할 수 있고, BackgroundQuitsetBackgroundMessageHandler를 이용할 수 있다.

사용 법을 간단히 적어봤다.

import messaging from '@react-native-firebase/messaging';

function App() {
  // Background, Quit 상태일 경우
  messaging().setBackgroundMessageHandler(async (remoteMessage) => {
  //  여기에 로직을 작성한다.
  //  remoteMessage.data로 메세지에 접근가능
  //  remoteMessage.from 으로 topic name 또는 message identifier
  //  remoteMessage.messageId 는 메시지 고유값 id
  //  remoteMessage.notification 메시지와 함께 보내진 추가 데이터
  //  remoteMessage.sentTime 보낸시간
  });
  
  
  // Foreground 상태인 경우
  React.useEffect(() => {
    const unsubscribe = messaging().onMessage(async remoteMessage => {
      Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
    });
    return unsubscribe;
  });
  
  return (<> App </>)
}

RemoteMessage객체의 다양한 사용법은 여기에서 볼 수 있다.

onMessage의 경우 알림을 표시하지 않는다. 위 코드에서 보는 것처럼 onMessage내 에서 상태값을 업데이트 하는 등으로 UI를 변경해서 사용자에게 알려주면 된다.

데이터 전용 메시지의 경우 알림이 무시된다.


백그라운드에서 애플리케이션 상태

백그라운드에서 알림을 수신했을 때, Android와 iOS는 다르게 동작한다.
Android는 HeadlessJS라고 하는 안드로이드 전용 기능이 있어 React요소를 마운트하지 않고도 백그라운드에서 코드를 실행시킨다.
하지만, iOS는 메시지가 수신되면 자동으로 애플리케이션을 실행시키기 때문에 React요소가 사용자가 원하지 않음에도 불구하고 마운트 될 수 있다.
안드로이드에 대해서만 포스팅할 것이므로 iOS에서 이 이슈를 처리하는 방법은 공식문서 내용을 확인하면 되겠다.
추가적으로 iOS에서 푸쉬알림 UI를 위한 라이브러리도 고려해 볼 수 있다.


전체 메시지 보내기

프로젝트의 index.jsapp.tsx등 상위 진입점에 코드를 추가한다.

import messaging from '@react-native-firebase/messaging';

messaging().setBackgroundMessageHandler(async (remoteMessage) => {
  console.log('Message handled in the background!', remoteMessage);
});

에뮬레이터나 실제 디바이스에 USB로 연결하는 등으로 앱을 실행시킨다.
파이어베이스 콘솔로 이동해 프로젝트를 클릭한다.

좌측 네비게이션에서 클라우드 메시징을 클릭하면 푸쉬 테스트를 해볼 수 있다.

새 알림 버튼을 눌러서 푸쉬를 보내면 된다.

첫 번째 스텝에서 먼저 알림제목 알림텍스트를 입력한다.

두 번째 스텝에서 타겟을 지정한다 앱 : [앱 패키지이름]을 선택해서 해당하는 앱을 선택한다.

세 번째 스텝에서는 시간을 설정한다.
바로 확인하기위해 지금보내기를 선택했다.

워딩이 조금 이상한데 검토 버튼을 누르면 푸쉬 알림이 전송된다.

테스트했던 푸쉬 기록이 남는다.

여기까지 했다면 디바이스에 푸쉬 알림이 전송된 것을 볼 수 있다.


topic 구독한 사용자에게 푸쉬보내기

사용자가 특정 주제에 대해서 구독하기를 누른다면, 해당하는 사용자를 별도의 데이터베이스 없이 firebase만을 이용해서 알림을 전송할 수 있다.

이렇게 CAR에 대한 구독과 구독해제 UI만 있으면 된다.

import messaging from '@react-native-firebase/messaging';
import Toast from 'react-native-simple-toast';

// 구독하기
export function callApiSubscribeTopic(topic: string = 'Car') {
  //   return instance.post('/push');
  return messaging()
    .subscribeToTopic(topic)
    .then(() => {
      Toast.showWithGravity(`${topic} 구독 성공!!`, Toast.LONG, Toast.TOP);
    })
    .catch(() => {
      Toast.showWithGravity(`${topic} 구독 실패! ㅜㅜ`, Toast.LONG, Toast.TOP);
    });
}

이런식으로 코드를 작성하고 messaging객체의 subscribeToTopic을 이용해 해당 주제(string타입)을 구독하면 firebase가 알아서 사용자의 고유값을 수집해준다.

여기까지만 진행하면 구독한 사용자의 고유정보를 수집하는 로직이 완성된다. 나머지는 파이어베이스가 알아서 해준다.

이제 해당하는 주제의 사용자에게 알림을 전송해볼 수 있다.

마찬가지로 파이어베이스 메시징 테스트 웹에서 새 알림 버튼을 누르고
두 번째 스탭에서 타겟만 특정 topic이용자로 설정한 뒤 테스트를 할 수 있다.
위 내용을 참고해 테스트해보자.


사용자 고유 token 수집하기

사용자의 고유 token을 수집해 특정 사용자들에게 메세지를 보낼 수 있다.
token을 수집하는 법과 token을 이용해 메세지를 보내는 방법만 알면 나머지는 일반적인 데이터베이스를 이용한 CRUD와 같기 때문에 어렵지 않다.

사용자 토큰 수집

아까 화면의 UI에서 getToken을 실행한 뒤 Alert을 띄우는 코드다.
코트에 대해서 이해가 잘 안된다면 여기부분을 읽어보면 된다.


import messaging from '@react-native-firebase/messaging';

function ...
...

  const getFcmToken = useCallback(async () => {
    const fcmToken = await messaging().getToken();
    await Alert.alert(fcmToken);
    await dispatch(actions.requestNotification(fcmToken));
  }, [dispatch]);

...

return (
  ...
  <PushButton title="get Token!!" onPressButton={getFcmToken} />
  ...
  );
}

messaging().getToken()이 Promise를 반환하는데 async함수를 이용해 await으로 그 값을 fcmToken에 담아 Alert를 띄우고 있다.
dispatchredux에서 사용하는 것이고 useCallback은 최적화를 위해 사용하는 것이므로 배제하고 보면 된다.
공식문서에서 messaging에 대한 내용은 여기에서 볼 수 있다.

수집한 토큰들을 이용해 메세지 보내기

데이터베이스에서 특정 사용자들을 추려내 token들을 배열에 담았다고 가정한다.
공식문서

// Node.js
var admin = require('firebase-admin');

// ownerId - who owns the picture someone liked
// userId - id of the user who liked the picture
// picture - metadata about the picture

async function onUserPictureLiked(ownerId, userId, picture) {
  // Get the owners details
  const owner = admin
    .firestore()
    .collection('users')
    .doc(ownerId)
    .get();

  // Get the users details
  const user = admin
    .firestore()
    .collection('users')
    .doc(userId)
    .get();

  await admin.messaging().sendToDevice(
    owner.tokens, // ['token_1', 'token_2', ...]
    {
      data: {
        owner: JSON.stringify(owner),
        user: JSON.stringify(user),
        picture: JSON.stringify(picture),
      },
    },
    {
      // Required for background/quit data-only messages on iOS
      contentAvailable: true,
      // Required for background/quit data-only messages on Android
      priority: 'high',
    },
  );
}

인스타그램에서 게시물이 좋아요 표시될 때마다 업로드한 사용자에게 메세지를 보낸다고 가정하고 있다.
owneruser는 데이터베이스에서 해당하는 정보를 담은 것이다.
contentAvailable과 priority는 데이터 전송의 경우에 사용자에게 알림을 띄우기 위한 중요도 설정이다.
각각 iOS와 aOS에서 필요하다.
데이터 전용 메시지는 우선순위가 낮아 기본적으로 트리거 하지 않기 때문에 별도의 옵션을 설정한 것이다.

iOS에서는 이 것뿐아니라 추가적인 설정이 더 필요하다.

이렇게 전송된 메시지는 아래처럼 받을 수 있다.

function App() {
  useEffect(() => {
    const unsubscribe = messaging().onMessage(async remoteMessage => {
      const owner = JSON.parse(remoteMessage.data.owner);
      const user = JSON.parse(remoteMessage.data.user);
      const picture = JSON.parse(remoteMessage.data.picture);

      console.log(`The user "${user.name}" liked your picture "${picture.name}"`);
    });

    return unsubscribe;
  }, []);
}

useEffect의 return은 React 공식문서에서 clean-up에 대해 찾아보면 된다.


여기까지 ReactNative에서 Android 디바이스에 push알림을 보내는 세 가지 시나리오를 살펴봤다.
파이어베이스가 제공해주는 강력한 기능(무료)과 ReactNative의 강점을 잘 느낄 수 있었다.
앱 개발을 시작하면서 빌드 문제 패키지매니저 문제, 호환성, 디버깅 어려움 등 여러가지문제에 막히는 경우가 많지만 하나씩 익혀가야겠다.

profile
웹 프론트엔드, RN앱 개발자입니다.

2개의 댓글

comment-user-thumbnail
2021년 4월 21일

감사합니다..

답글 달기
comment-user-thumbnail
2023년 7월 9일

안녕하세요 리액트 네이티브 개발 도움 좀 받을 수 있을까요?

답글 달기