[React-native] webview앱에 push 알림 기능 설정

react native를 이용한 webview앱에 푸시 알림 기능을 넣는 포스트를 찾지 못해서 많이 헤맷다. 사실 조금만 생각해보면 알수있는 답들은 다 나와있었는데 적용을 못시킨 내 잘못도 크다 ㅠㅠ..

알림을 받기위해 필요한 것은 각 기기를 구분하는 device token값과 알림을 처리해주는 서버다. 삽질을 겁나 하다가 겨우 방법을 찾아냈다.

  처음 기능을 구현할때는 react native는 webview화면만 띄워주는 것이라고 생각해서 모든 기능 구현은 웹 페이지를 구현한 node.js 환경에서 해야한다고 생각하고 node.js FCM만 100번은 검색하고 온갖자료를 본 것 같다. 하지만 모든 글에서 요구하는게 기기의 token 값이었고 이것을 위해서는 react-native에서 작업이 필요하다는 것을 오랜 삽질 끝에 깨달았다...

expo 환경에서 푸시 알림 기능 구현법은 공식 도큐먼트에도 굉장히 잘 나와있다. 나는 다른 블로그 포스팅에 굉장히 많은 도움을 받았다.

내가 구현하는 푸시 알림은 글이 등록되면 다른 사용자들에게 알림이 가기만 하면 되는 간단한 기능이었다. 푸시 알림으로 할 수 있는 다양한 기능들이 있는 것 같지만 내가 필요로하는 최소한의 기능만 구현했다.

1. 공식 문서
https://docs.expo.io/guides/push-notifications/

2. 참고한 블로그들


(작성자 분들 정말 감사합니다 .. ㅠㅠ)

메세지를 보내줄 서버가 필요한데 expo는 알림을 관리해주는 expo 서버가 중간에 위치하고 안드로이드는 FCM으로, IOS는 APNS를 통해 알림이 전송된다. 나는 안드로이드 앱 개발 중이었기 때문에 IOS 알림은 여기서 다루지 않는다.


React-native에서 설정해줘야 할 것은 사용자 기기에서 토큰값을 받아오는 것이다. 리액트에서 모든 개발을 한다면 서버에서 토큰 값을 다루는 설정도 리액트에서 하면 되지만 나는 백엔드 처리가 원래 서버에서 이루어지므로 토큰값만 얻어내면 해결이다.

import { WebView } from 'react-native-webview';
import React from 'react';
import {
  Vibration
} from 'react-native';
import { Notifications } from 'expo';
import * as Permissions from 'expo-permissions';
import Constants from 'expo-constants';
const PUSH_REGISTRATION_ENDPOINT = '토큰 처리할 서버 주소';
export default class App extends React.Component {
  state = {
    notification: null,
    messageText: ''
  }
  tokenValue = '';
  registerForPushNotificationsAsync = async () => {
    if (Constants.isDevice) {
      const { status: existingStatus } = await Permissions.getAsync(Permissions.NOTIFICATIONS);
      let finalStatus = existingStatus;
      if (existingStatus !== 'granted') {
        const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
        finalStatus = status;
      }
      if (finalStatus !== 'granted') {
        alert('푸시 알림이 기능이 해제됩니다');
        return;
      }
      token = await Notifications.getExpoPushTokenAsync();
      console.log(token);
      this.tokenValue = token;
      this.setState({ expoPushToken: token });
    } else {
      alert('푸시 알림은 기기에서만 이용가능합니다.');
    }
    if (Platform.OS === 'android') {
      Notifications.createChannelAndroidAsync('default', {
        name'default',
        sound: true,
        priority: 'max',
        vibrate: [0250250250],
      });
    }
    //node 서버로 토큰 전송
    return fetch(PUSH_REGISTRATION_ENDPOINT,{
      method:'POST',
      headers: {
        'Accept''application/json',
        'Content-Type''application/json',
      },
      body: JSON.stringify({
        token: this.tokenValue
      })
    })
  };
  componentDidMount(){
    this.registerForPushNotificationsAsync();
     this._notificationSubscription = Notifications.addListener(this._handleNotification);
  }
  _handleNotification = notification => {
    Vibration.vibrate();
    console.log(notification);
    this.setState({notification: notification});
  }
  render() {
    return <WebView 
    source={{ uri: '웹뷰 할 서버 주소' }} 
    style={{ paddingTop:20}} 
    />;
  } 
}
cs

사실 Expo 공식문서가 잘 돼있어서 지금 다시보면 그것만 보고 해도 충분히 간단하게 만들 수 있을 것 같다. 원래는 서버로 fetch 하는 부분에서 토큰 정보 뿐만 아니라 많은 정보를 ㅏ담을 수 있으니 문서를 참고하면 좋을 듯 하다.

안드로이드 알림 설정은 Channel을 생성해야 할 수 있다. priority가 max로 되어있어야 푸시 알림이 미리 보기로 뜬다는데 서버에서 priority=max 설정이 안되고 high가 최대라는 경고가 뜬다.

여기까지 소스를 작성하고 안드로이드 기기에서 앱을 실행하면 콘솔창에 기기의 토큰 값이 출력된다. 이것을 이용해 expo에서 미리 테스트 해 볼 수 있다.
https://expo.io/notifications
위 사이트에 접속해 얻어낸 토큰값, title, body 부분만 작성해 send 버튼을 누르면 기기로 알림이 올 것이다!

이제 백엔드 서버에서 토큰을 다룰 수 있다. 토큰을 이용해 Expo 서버로 POST 요청을 보내 푸시 알림을 띄울 수 있다.
내가 사용한 백엔드 서버는 node.js이기 때문에 node.js용 expo server sdk를 사용해야 한다. 아래 링크를 눌러 사용법을 볼 수 있다.
https://github.com/expo/expo-server-sdk-node

const { Expo } = require('expo-server-sdk')
이렇게 선언함으로써 Expo 모듈을 사용 할 수 있게 된다. (npm install로 설치 후 사용할 수 있었던 것으로 기억한다...아마도)

 import { Expo } from 'expo-server-sdk';
// 혹은 const {Expo} = require('expo-server-sdk');
// Create a new Expo SDK client
let expo = new Expo();
const handlePushTokens = (message, savedPushTokens) => {
    let notifications = [];
    for (let pushToken of savedPushTokens) {
        if (!Expo.isExpoPushToken(pushToken)) {
            console.error('Push token ${pushToken} is not a valid Expo push token');
            continue;
        }
        notifications.push({
            to: pushToken,
            sound: 'default',
            title: 'Bluecheck',
            body: message,
            data: {
                message
            },
            channelId: "default",
        })
    }
    let chunks = expo.chunkPushNotifications(notifications);
    (async () => {
        for (let chunk of chunks) {
            try {
                let receipts = await expo.sendPushNotificationsAsync(chunk);
                console.log(receipts);
            } catch (error) {
                console.error(error);
            }
        }
    })();
}
cs


공식문서에 있는 형식을 그대로 사용했다. 한 가지 다른 점은 앞서 생성한 안드로이드 channel을 통해 전송하기 위해 channelId 속성을 줬다는 점이다.
사용자들의 토큰은 새로운 토큰 값이 들어와 토큰 처리 라우터가 실행될 때마다 사용자 쿠키에 토큰 값을 넣어두고 사용자가 로그인 할 때 사용자 DB에 토큰 값을 넣어주거나 업데이트 하는 방식으로 관리한다.
메세지를 보내고 싶은 사용자 그룹에 맞게 상황에 따라 토큰 배열에 넣어주고 위 함수에 인자로 넘겨준다.

여기까지 제대로 수행되었다면 Expo 어플에서 테스트 할 때는 제대로 동작 할 것이다. 하지만 지금은 Expo 어플을 통해 앱을 테스팅 하는 것일 뿐, 어플을 실제로 build한 이후에는 맨 위에 첨부한 그림처럼 FCM 서버를 한 번 통하고 메세지가 전달되기 때문에 추가 설정을 해줘야 한다.

https://docs.expo.io/guides/using-fcm/
이또한 공식 문서를 참조하면 간단히 할 수 있다!

Firebase에 들어가 새 프로젝트에 새 안드로이드 앱을 생성한다. 패키지 이름은 앱 설정할 때도 쓰이니 기억해두자. 앱이 생성 되었다면 아래 파일을 다운받는다.


그리고 이 파일을 리액트 앱 root 폴더에 넣는다. 이제 app.json 파일에 설정을 바꿔주면 된다.

"android": {
      "googleServicesFile""./google-services.json",
      "package""Firebase에서 설정한 패키지 이름",
      "versionCode":1
}
cs

android 속성이 없으면 제일 밑부분에 ,를 입력하고 적어주면 된다.
만약 어플이 카메라를 사용한다거나 외부 스토리지를 사용한다면 permission 또한 여기서 설정해줘야 한다. Expo permissions를 참조하여 어떤 설정을 해줘야 하는지 찾아볼 수 있다. (https://docs.expo.io/versions/latest/sdk/permissions/ & https://docs.expo.io/versions/latest/workflow/configuration/#android 참고)

그리고 다시 Firebase로 돌아간다. 생성한 앱의 관리 메뉴로 들어가 '클라우드 메시징' 메뉴로 들어간다. Server key 값이 보일 것이다. 복사하자. 그리고 다시 리액트 개발 환경으로 넘어온다. 커맨드 창에 아래 명령어를 입력해야 한다.

expo push:android:upload --api-key <your-token-here>
cs

<>는 모두 지워줘야 한다. 방금 복사한 서버 키 값을 붙여넣고 명령어를 입력하면 금방 작업이 끝난다. Expo 서버에 내 FCM 서버 키 값을 저장하는 과정같다.

이제 expo build:android 명령어로 앱을 빌드해 푸시 알림이 잘 오는 것을 확인하면 된다.
한가지 주의 할 점은 안드로이드 8 이하로는 아이콘에 badge 표시가 안 된다는 점. 그리고 버전에 따라서는 푸시 알림 미리보기가 뜨지 않고 상단바를 내려야만 알림이 보일 때도 있었다. 최신 안드로이드 기기에 아직 테스트 해볼 수는 없었는데 잘 될 것이라고 생각한다.

처음 푸시 알림이 떴을 때 얼마나 기뻤는지 모르겠다 ㅠㅠ 눈 감고도 할 수 있는 날까지 화이팅!

No comments:

Powered by Blogger.