Front/React Native

[ReactNative] 푸쉬 알람 설정 Expo-notifications FCM

부랴부랴 앱을 만드는 중
강한 생산성이 필요했기에, Expo의 노예가 됐다.
앱 생태계를 잘 모르니까 답답쓰
백엔드만 주구장창 하다가 어플 급하게 만드려니 아휴ㅠ..

그래도 오늘 앱스토어, 구글스토어 모두 승인이 났다 오예~~

일단 푸쉬 알람같은 경우는 IOS는 nodejs랑 expo-notifications 활용해서 잘 가는데,

안드로이드는... 안드로이드는..... 안됐다....

이 문제로 삼일 내내 고통받다가, 알고보니 안드로이드는 expo-notifications말고 FCM도 따로 설정해줘야 한다는 충격적인 비밀을 알았다.
이거 expo-notifications  공식문서에도 제대로 설명 안되어 있었는데, 으으

일단 공식문서를 살펴보자..

 

Using FCM for Push Notifications - Expo Documentation

Expo is an open-source platform for making universal native apps for Android, iOS, and the web with JavaScript and React.

docs.expo.dev


공식문서대로 잘 진행하면 되겠다.

app.json에서 android의 설정만 따로 변경해준다.
app.json

    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#FFFFFF"
      },
      "versionCode": 25,
      "permissions" : ["CAMERA", "NOTIFICATIONS"],
      "package": "com.taejin.inout_webview",
      "useNextNotificationsApi": true,
      "googleServicesFile": "./google-services.json"

 

 

google-service.json

{
  "project_info": {
    "project_number": "asdasdasd",
    "project_id": "inoutpushalarm",
    "storage_bucket": "asdasdasd"
  },
  "client": [
    {
      "client_info": {
        "mobilesdk_app_id": "1:9575657824asd93610aad",
        "android_client_info": {
          "package_name": "com.taejin.inout_webview"
        }
      },
      "oauth_client": [
        {
          "client_id": "asdasd5crhg3etgdorc5hle.apps.googleusercontent.com",
          "client_type": 3
        }
      ],
      "api_key": [
        {
          "current_key": "AasdasdBBE"
        }
      ],
      "services": {
        "appinvite_service": {
          "other_platform_oauth_client": [
            {
              "client_id": "asdasdhle.apps.googleusercontent.com",
              "client_type": 3
            }
          ]
        }
      }
    }
  ],
  "configuration_version": "1"
}

FCM에서 자동으로 설정해주기 때문에, current_key는 건드리지말자.
expo 공식문서에서 뭐 구글 어디에 들어간 키를 넣으라고 해서, 계속 넣었다가, 그냥 FCM에서 자동으로 설정해준 키를 넣으니 됐다.
FCM에서 구글 GA셋팅까지 해주는 것 같다.

일단 이렇게 셋팅해두면 환경설정이 끝난다.

백엔드 파트
Expo-notifications는 100개씩 박에 못보내서, 귀찮다.
그래서 100개씩 끊어서 보내야했다.
사실 서버에서 안보내고 그냥 FCM에서 해도 된다.
그래도 더 디테일한 알람을 위해서 서버쪽에서 전체, 특저유저 등 푸쉬 알람에 관한 모듈을 만들었다.
밑은 모듈화 하기전 실험용 코드다.

exports.pushAlarm = async (req, res, next) => {
  try {
    const { title, content } = req.body;
    const [tokenList] = await mysqlRead.query(`SELECT push_token FROM app_push_token WHERE permission = 'Y'`, []);
    let pushList = [];
    const totalLength = tokenList.length;
    const tokenListLengthForPush100Limit = parseInt(totalLength / 100);
    const expectedOutput = totalLength % 100;

    for (let i = 0; i < tokenListLengthForPush100Limit; i++) {
      let limit = 100 * i;
      for (let j = 0 + limit; j < 100 + limit; j++) {
        pushList.push(tokenList[j].push_token);
      }
      const message = {
        to: pushList,
        sound: 'default',
        title: title,
        body: content,
        data: { someData: 'goes here' },
      };

      await axios('https://exp.host/--/api/v2/push/send', {
        method: 'POST',
        headers: {
          Accept: 'application/json',
          'Accept-encoding': 'gzip, deflate',
          'Content-Type': 'application/json',
        },
        data: JSON.stringify(message),
      });

      pushList = [];
    }

    // 100 나누기 나머지 보내기
    for (let i = tokenListLengthForPush100Limit * 100; i < tokenListLengthForPush100Limit * 100 + expectedOutput; i++) {
      pushList.push(tokenList[i].push_token);
    }

    const message = {
      to: pushList,
      sound: 'default',
      title: title,
      body: content,
      data: { someData: 'goes here' },
    };

    await axios('https://exp.host/--/api/v2/push/send', {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Accept-encoding': 'gzip, deflate',
        'Content-Type': 'application/json',
      },
      data: JSON.stringify(message),
    });

    return res.status(200).json({ msg: 'success' });
  } catch (err) {
    console.log(err);
    return res.status(400).json({ msg: 'fail' });
  }
};

100개씩 나눠서 포문을 돌려서 보냈다.
나중 API 쏘는 부분과 정보들은 모듈화해서 따로 뺐다.

구조는 이렇다.

어드민=> 서버 => expo서버 => 유저

1. 어드민(클라이언트)에서
푸쉬알람을 보낼 내용과, 제목을 작성해서 서버로 API를 쏜다.

2. 서버는 받은 내용과 유저 정보들을 토대로 expo에 API를 쏜다.

푸쉬알람을 보낼 사람들은 RN에서 렌더링이 되자마자 디바이스 토큰 정보를 담아 DB에 보낸다.

3. Expo는 디바이스 토큰을 토대로 유저들에게 푸쉬알람을 보낸다.