티스토리 뷰

반응형

안녕하십니까 

이번에 모바일 채팅 기능을 위한 Push Server를 구현을 해야 하는 일이 생겼습니다.

스마트폰을 사용하면서 Push기능에 대해 들어만 봤지 정확한 개념이나 원리에 대해서는 잘 알지 못하여

이번 기회에 공부를 했습니다.

 

위키 백과에서는 다음과 같이 정의를 하고 있습니다.

 

푸시 기법 또는 서버 푸시(server push)는 인터넷 상에서 어떤 전송 요청이 중앙 서버에서 시작되는 정보 전달 방식이다. 이것은 전송 요청이 클라이언트에서 시작되는 풀 기법과 대비되는 것이다. 

푸시 기법은 사용자가 원하든 원치 않든 방송처럼 뉴스를 제공하는 기술이다. 사용자가 원하는 정보를 직접 찾는 풀 기법과는 상반되는 개념이다. 그런데 푸시 방식으로 정보를 제공하던 기존의 TV나 라디오와 다른 점은 사용자가 미리 원하는 범위를 지정할 수 있다는 점이다.  - 위키 백과

 

 

밑의 사진처럼 알림 창이 오는 경우를 많이 보시지 않았나요?

이런 게 다 Push Server를 통해 Client가 Message를 받아서 표시를 한 거예요.

출처 : http://www.itdaily.kr/news/articleView.html?idxno=58684

 

밑은 대표적으로 Push Server를 위해 사용하는 애플의 APNs와  구글의 FCM입니다.

                                                                                     아이폰 APNs (왼), 구글 GCM(오)                                                                                     출처 : http://www.itdaily.kr/news/articleView.html?idxno=58684

 

Client가 Push Server에게 Message data 및 Push 알람을 받을 Client의 Auth 정보를 보내주면

Push Server가 해당 Client에게 Message를 보내주는 그런 형식입니다. 

 

Push Server에 대해 많은 검색을 해봤는데 Push Server를 직접 구현하는 것은 성능 이슈가 발생할 수 있다고 하네요.

그러면 다른 방법이 있을까요?

 

물론 방법이 있습니다.

FCM이라고 (Firebase Cloud Message) Google의 GCM (Google Cloud Message)의 모듈들을 탑재하여 좀 더 쉽고 간편하게 Push메시지를 보낼 수 있게 만들었습니다. - GCM은 2018년 4월 10일에 지원이 중단되어 FCM 사용을 권장함.

 

FCM은 완전 무료 기능이며, Android & IOS지원을 할 뿐만 아니라 매뉴얼이 잘 되어 있어 구현하기가 그렇게 어렵지 않습니다.

 

 

 

FCM 구현에는 송수신을 위한 두 가지 주요 구성요소가 포함됩니다.

  1. Firebase용 Cloud Functions 또는 앱 서버와 같이 메시지를 작성, 타겟팅, 전송할 수 있는 신뢰할 수 있는 환경
  2. 메시지를 수신하는 iOS, Android 또는 웹(자바스크립트) 클라이언트 앱

Firebase Admin SDK 또는 FCM 서버 프로토콜을 통해 메시지를 보낼 수 있습니다. 강력한 타겟팅 및 분석 기능이 내장되어 마케팅 또는 참여 메시지를 테스트하거나 전송하는 데 알림 작성기를 사용할 수도 있습니다.

 

 

 

 

 

FCM은 Firebase Admin SDK나 FCM 서버 프로토콜을 이용하여 구현할 수 있습니다.

자세한 사항은 위의 링크를 참고해 주세요!

 

 

저는 Firebase Admin SDK를 이용하여 구현을 해보겠습니다.

 

 

 

우선 FireBase 프로젝트를 만들고 저 화면에서 새 비공개 키를 생성하면 JSON 파일로 해서 Server secret key가 생깁니다. 물론 Server에서만 알아야 하니 공유를 하거나 하면 안되겠죠?

그리고 예시에 있는 firebase-admin 모듈을 import 해줍니다.

npm install --save firebase-admin <- 설치하면 @type을 따로 설치할 필요가 없음

 

그럼 이제 모듈을 초기화 해보겠습니다.  (저는 typescript를 이용하여 개발하니 참고하세요.)

SDK 추가 가이드 그대로 등록을 하면되서 그렇게 어렵진 않습니다.

import firebase from "firebase-admin";
import pushAccount from "../../생성받은키.json";

export const push = firebase.initializeApp({
  credential: firebase.credential.cert(pushAccount as Object),
  databaseURL: "https://pabiisome.firebaseio.com"
});

 

그리고 Push Server 전송 요청 작성 가이드 를 읽어 보며 Client 전성도 마저 구현해 보겠습니다. 

import { push } from "../configure/connection";
import l from "../configure/logger";
import { Utiles } from '../utils/Utiles';
import TokenService from './token.service';
import { PushTokensVO } from '../vo/PushTokensVO';
import { ChattingMessageVO } from '../vo/ChattingMessageVO';

export class FirebaseService{

  async sendPush(ids: Array<{"id": string}>, mes_id: number, mes: string): Promise<any>{
    
    let tokenA: string[] = [];
    const promises = ids.map(i => {
      let token = TokenService.getToken(+i.id).then(r => { 
          tokenA.push(r); 
          return r
      });
      return token;
    })
    await Promise.all(promises);

    const message = {
      data: {
        title: "메세지가 왔습니다.",
        contents: mes,
        id: 3 + ""
      },
      tokens: tokenA,
      notification: {
        title: "메세지가 왔습니다.",
        body: mes
      },
      android: {
        ttl: 3600 * 1000,
        notification: {
          icon: 'stock_ticker_update',
          color: '#f45342',
        },
      },
      apns: { // IOS 설정
        payload: {
          aps: {
            alert : {
              body: mes,
            },
            sound: "default"
          },
        },
      }
    };

    return new Promise((resolve, reject) => {    
      if (tokenA == null ) { 
        reject( Utiles.responseToJson(500, "Push Failed"));
      } 
      push.messaging()
      .sendMulticast(message)
      .then(response => {
        if (response.failureCount > 0) {
          let failedTokens: string[] = [];
          response.responses.forEach((resp, idx) => {
            if (!resp.success) {
              failedTokens.push(tokenA[idx]);
            }
          });
          l.error('List of tokens that caused failures: ' + failedTokens);
          reject( Utiles.responseToJson(500, failedTokens));
        } else {
          ChattingMessageVO.update({
            push_send_time: new Date()
          }, {
            where: {
              message_id: mes_id
            }
          }).then(r => {console.log(r)}).catch(err => {console.log(err)});
          resolve( Utiles.responseToJson(200, "OK") );
        }
      })
      .catch(err => {
        l.error(err);
        reject( Utiles.responseToJson(500, "Push Failed"));
      });
    }) 
  }
}

export default new FirebaseService();

위의 코드에서 유심히 봐야할 곳은 message 부분입니다.

data key는 말 그대로 data고

notification은 백그라운드에서 push 받을 시 표시할 내용 (정확하진 않습니다.)

apns는 IOS에 보내기 위해 사용하는 부분입니다. apns 포멧 형식

 

 

 

테스트를 해봤을 때 제대로 Push를 받는 모습을 볼 수 있었습니다.

 

다만 좀 더 테스트 해야할 부분은 예전 Push Server는 수신률이 높이 않아 실패 했을 때의 로직도 추가 해줘야 하고

100% 믿으면 안된다 하더라고요.

APNs를 이용하면 IOS 부분은 수신률은 90%가 넘는걸로 어느 글을 봤었는데 GCM은 수신률이 그렇게 높지 않았다고 합니다. 

 

하지만 Android & IOS를 FCM으로 구현을 하면서 FCM은 수신률이 어떻게 나올지가 잘 모르겠습니다.

 

나중에 알면 추가 하겠습니다.

 

 

 

 

출처

Push server

http://www.itdaily.kr/news/articleView.html?idxno=58684

https://ko.wikipedia.org/wiki/%ED%91%B8%EC%8B%9C_%EA%B8%B0%EB%B2%95

 

GCM

https://en.wikipedia.org/wiki/Google_Cloud_Messaging

 

FCM

https://firebase.google.com/

반응형

'BackEnd > Node.js' 카테고리의 다른 글

nods.js Request를 이용한 외부 api 호출  (0) 2019.08.17
FCM Silent Push  (0) 2019.08.17
node.js scheduler  (0) 2019.07.05
node.js File upload 하기  (0) 2019.06.04
node.js env파일 분리하기  (0) 2019.05.30
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함