본문 바로가기
Node js

[node.js] fcm을 이용해서 기기에 푸쉬 알림 만들기

by 갈잃자 2024. 11. 14.

개요: 기존 푸쉬알림 코드 변경을 해야하는 상황이 생김 (firebase 측에서 코드를 변경함)


 

사용자가 어떠한 동작을 했을 때, 기기에 푸쉬알림이 오는 로직을 수정하게 되었다.

 

기존 fcm 푸쉬 알림에서 달라진 녀석들

 

1. url

2. Authorization

3. body

async function example() {
  // Oauth 토큰을 가져오는 작업
  const oauthToken = await getOauthToken();
  
  const options = {
 	// 변경된 부분 1
    url: 'https://fcm.googleapis.com/v1/projects/{project-id}/messages:send',
    
    headers: {
      'Content-Type': 'application/json',
      
      // 변경된 부분 2
      Authorization: `Bearer ${oauthToken}`
    },
    
    // 변경된 부분 3
    body: {
      message: {
        notification: {
          title: `푸쉬알림 테스트`
        }
      }
    },
    json: true
  };

  await request.post(options);
}

async function getOauthToken() {
  return await getOauthTokenHandler();
}

 

 

이중, Authorization 쪽에서 많이 변경됬는데, 여러 키나 아이디(firebase에서 받을 수 있음)를 이용해서, OauthToken을 받아와야 된다.

 

여기서 또 알아야 할 부분이 있는데,

 

OauthToken은 만료기간이 존재한다!(1시간)

OauthToken을 모든 고객이 이용할 때 마다 뽑아서 이용할 수 있지만, 그건 상당히 비효율적인 작업이다. 꼭 db에 저장하여 1시간 만료가 됬는지 안됬는지 확인 작업이 필요하고, 많은 고객들이 동시다발적으로 이용되는 푸쉬알림이라면 pending 기법을 이용해서 제작을 해야하는 작업!

import { client_email, private_key, token_uri } from 'firebase에서 준 json 위치';
const { google } = require('googleapis');

export const getOauthTokenHandler = async () => {
  console.log('get Oauth start!');
  const OauthToken = await checkOauthToken();

  return OauthToken || null;
};

// 기존 db에 Oauth토큰이 있는지, 또 토큰 만료기간이 괜찮은지 확인하는 코드
async function checkOauthToken() {
  const data = 'db에서 Oauth 조회한 값';
  const now = new Date();
  const expireAt: Date = data?.expires_in ? new Date(data.expires_in.toDate()) : null;
  let OauthToken = '';
  
  // 토큰이 있고 기간이 유효하다면 가져다 쓰세요
  if (expireAt && expireAt > now && data.access_token) {
    console.log('already have Oauth Token!');
    OauthToken = data.access_token;
  } else { // 아니라면 새롭게 발행
    const newToken = await getAccessToken();
    console.log('have to get other Oauth Token!');
    const expiresIn = 3599; // 한시간을 sec 처리
    const expireAt = new Date(now.getTime() + 3599 * 1000);
    console.log(expireAt);
    await saveTokenToDb(newToken, expireAt); // 새로 발행한 토큰은 db에 새롭게 저장

    OauthToken = newToken;
  }

  return OauthToken;
}

 

OauthToken을 발행함에 앞서, google api 라이브러리에서 JSON웹 토큰(jwt)을 사용해 Oauth토큰 요청을 인증해야한다. (1시간짜리 토큰뽑는데 작업이 열라 많음ㅋㅋ) 그 뒤, OauthToken을 발행하면, 이걸 db에 저장!

// firebase에서 준 json 데이터를 이용해서, jwt를 생성하고, jwt를 이용해 OauthToken을 생성
async function getAccessToken() {
  return new Promise<any>(function (resolve, reject) {
    const SCOPES = 'https://www.googleapis.com/auth/cloud-platform';
    const jwtClient = new google.auth.JWT(
      client_email,
      null,
      private_key,
      SCOPES,
      null
    );

    jwtClient.authorize(function (err, tokens) {
      if (err) {
        reject(err);
        return;
      }
      resolve(tokens.access_token);
    });
  });
}

async function saveTokenToDb(token: string, expireAt: Date) {
  // db에 저장하는 코드 작성

  console.log('Token save to db');
}

 

위 작업을 통해, Oauth 토큰을 post 요청 시, 헤더Authorization에 토큰을 집어넣고, 돌리면 기기에 푸쉬알림이 똭 옵니다

 

(중요)

OauthToken이 만료기간이 1시간이다 보니, schedule 함수를 만들어서 매시간 토큰을 발급 받아 이용하실 수 있어요. 근데, 이렇게 개발하면 하자가 생겼을 때 처리하긴 편한데, 비효율적이에요 예를들어 고객들이 자주 안쓰는 새벽시간에도 결국 OauthToken은 서버 내에서 계속 발행되고 있으니깐... 만약 사람들이 새벽 2시부터 7시까지 이용하지 않는다면, 5번의 불필요한 토큰을 발행하게 되는거니깐 5/24 정도의 불필요한 리소스를 가져가게 되겠죠 큰비용이 들진 않겠지만 비율로 치면 무지하게 비효율적인 토큰발행이 됩니다.

매시간 발행 로직 --> (19/24 효율로 토큰발행됨 이게 최대 효율임 더 안좋은 효율이 발생할 수 있음)
요청시 토큰발행 --> (19/19 효율로 토큰발행됨) 개이득 인 부분


 

출처: https://firebase.google.com/docs/cloud-messaging/migrate-v1?hl=ko

 

기존 HTTP에서 HTTP v1로 마이그레이션  |  Firebase Cloud Messaging

의견 보내기 기존 HTTP에서 HTTP v1로 마이그레이션 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. FCM의 기존 HTTP API를 사용하는 앱은 이 가이드의 안내에 따라

firebase.google.com

 

댓글