본문 바로가기
Programming/Android

[Android] FCM(Firebase Cloud Message) 서버에서 메시지 보내기

by hyunipad 2021. 3. 25.
반응형
본 포스팅은 주제 메시지를 기반으로 한 서버 구현 방법을 설명합니다.
기본적은 앱 설정과 주제 메세징은 이전 포스팅을 참조해주시기 바랍니다.

2021.03.24 - [Android] - [Android] FCM(Firebase Cloud Messaging), 클라우스 메시징

 

[Android] FCM(Firebase Cloud Messaging), 클라우스 메시징

본 포스팅은 안드로이드 스튜디오에서 FCM(Firebase Cloud Messsaging)를 사용하기 위한 방법을 설명합니다. 보다 자세한 내용은 https://firebase.google.com/docs/cloud-messaging?hl=ko를 참조해주시기 바랍니..

hyunipad.tistory.com

2021.03.24 - [Android] - [Android] FCM(Firebase Cloud Messaging) 주제 메시징

 

[Android] FCM(Firebase Cloud Messaging) 주제 메시징

본 포스팅은 FCM(Firebase Cloud Messaging)의 주제 구독 및 주제로 메시지 보내는 방법을 설명합니다. 기본적인 앱 설정과 테스트 메시지 전송은 앞선 포스팅을 참조해주세요. 2021.03.24 - [Android] - [Android.

hyunipad.tistory.com

 

FCM 서버에 관하여

FCM의 서버측 구성요소는 두가지입니다.

  • Google에서 제공하는 FCM 백앤드
  • 앱 서버 또는 신뢰할 수 있는 서버 환경(개발자 측)

앱 서버 또는 신뢰할 수 있는 서버 환경에서 FCM 백앤드측으로 메세지 요청을 보내고, FCM 백앤드에서 클라이언트 앱에 메시지를 보냅니다.

 

 

HTTP 프로토콜 vs XMPP 서버 프로토콜

현재 FCM에서는 HTTP 프로토콜과 XMPP 서버 프로토콜 두가지의 원시 서버 프로토콜을 제공합니다.

두가지의 프로토콜은 아래와 같은 항목에 있어서 다릅니다.

 

  • 업스트림/다운스트림 메시지
    • HTTP: 클라우드에서 기기로 전송하는 다운스트림 전용
    • XMPP: 기기에서 클라우드로 전송하는 업스트림 및 클라우드에서 기기로 전송하는 다운스트림
  • 메시징(동기 또는 비동기)
    • HTTP: 동기 메시징입니다. 앱 서버가 HTTP POST 요청으로 메시지를 보내고 응답을 기다립니다. 이 방식은 동기 메시징이며 응답이 수신될 때까지 발신자가 다른 메시지를 보내지 못하도록 차단합니다.
    • XMPP: 비동기 메시징입니다. 앱 서버가 영구 XMPP 연결을 통해 최대 회선 속도로 모든 기기에서 양방향으로 메시지를 주고받습니다. XMPP 연결 서버가 확인 또는 실패 알림을 비동기 방식으로 보냅니다. 확인 형식은 JSON으로 인코딩된 특수한 ACK 및 NACK XMPP 메시지입니다.
  • JSON
    • HTTP: JSON 메시지는 HTTP POST로 전송됩니다.
    • XMPP: JSON 메시지는 XMPP 메시지에 캡슐화됩니다.
  • 일반 텍스트
    • HTTP: 일반 텍스트 메시지는 HTTP POST로 전송됩니다.
    • XMPP: 지원되지 않습니다.
  • 여러 등록 토큰에 전송되는 멀티캐스트 다운스트림
    • HTTP: JSON 메시지 형식으로 지원됩니다.
    • XMPP: 지원되지 않습니다.

가능하다면 여러 플랫폼에 메시지를 보낼 수 있는 유연한 프로토콜인 HTTP v1 을 사용하는 것이 좋지만 클라이언트 앱에서 서버로 메세지를 보내는 업스트림 메세지를 보낼 필요가 있는 경우에만 XMPP 프로토콜을 사용합니다.

자세한 내용은 firebase.google.com/docs/cloud-messaging/server?hl=ko 를 참고해주세요.

 

FCM 서버에 메세지 요청

 

FCM 서버에 메세지를 요청하기 위해서는 크게 세 가지의 단계가 있습니다.

  1. FCM 백앤드에 사용자 인증 정보 제공
  2. 모듈 수준(앱 수준)의 build.gradle에 dependencies 추가
  3. 코드 작성

 

FCM 백앤드에 사용자 인증 정보 제공

 

FCM 백앤드에 요청을 보낼 때 신뢰할 수 있는 요청인지 확인하기 위해 사용자 인증 정보를 제공해야 합니다.Google에서는 Firebase 서버 API 를 호출하는 데 사용할 수 있는 Google 서비스 계정에서 사용자 인증 정보가 담긴 JSON 형태의 비공개키 파일을 제공합니다.

 

서비스 계정의 비공개 키 파일을 생성하시려면 아래의 단계를 따라주시기 바랍니다.

  1. Firebase Console에서 설정>서비스 계정을 클릭하세요.
  2. 새 비공개 키 생성을 클릭한 다음 키 생성을 클릭하여 확인합니다.
  3. JSON파일을 안전한 곳에 저장합니다.

클라이언트 앱에서 이 JSON파일에 접근할 수 있도록 JSON파일을 앱 내부 저장소에 저장해야 합니다.

  1. Data File Explorer 오픈
  2. Data>Data>[패키지 명]>files 에 JSON파일 저장

 

모듈 수준(앱 수준)의 build.gradle에 dependencies 추가

코드 작성시 GoogleCredential 또는 GSON 등을 사용하기 위해 dependencies를 추가합니다.필자는 해당 implementation을 최신 버젼으로 업데이트하려고 했으나 업데이트 시 기존의 클래스들과 충돌이 일어나 해당 버젼을 사용하고 있습니다. 만약 업데이트하고 충돌이 없을 시에는 사용하셔도 무방합니다.

 

코드 작성

사용자 인증 정보를 사용하여 액세스 토큰 발급

앱 내부 저장소에 저장한 비공개 키 파일을 사용하여 액세스 토큰을 발급해야 합니다.

만약 파일의 위치가 올바르지 않다면 context.getFileDir()를 통해 앱 내부 저장소의 위치를 확인해주시기 바랍니다.

    private static String getAccessToken() throws IOException {
        Context context = MainActivity.context;
        GoogleCredential googleCredential = GoogleCredential
                .fromStream(new FileInputStream(new File(context.getFilesDir(), "service-account.json")))
                .createScoped(Arrays.asList(SCOPES));
        googleCredential.refreshToken();
        return googleCredential.getAccessToken();
    }

 

액세스 토큰 HTTP 헤더에 추가

발급 받은 액세스 토큰을 HTTP 헤더에 추가하여 FCM 백앤드에 메세지를 요청합니다.

private static HttpURLConnection getConnection() throws IOException {
        // [START use_access_token]
        URL url = new URL(BASE_URL + FCM_SEND_ENDPOINT);
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken());
        httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");
        return httpURLConnection;
        // [END use_access_token]
    }

 

FCM 백앤드에 메세지 요청

PROJECT ID는 firebase Console에서 확인할 수 있습니다.

PROJECT ID

선언된 final 변수 TITLE, BODY, TOPIC 을 통해 메세지를 작성합니다.

적절한 방법을 통해 메세지 작성 메서드를 구현하세요.

import android.content.Context;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Arrays;
import java.util.Scanner;

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;

public class Messaging {

    private static final String MESSAGING_SCOPE = "https://www.googleapis.com/auth/firebase.messaging";
    private static final String[] SCOPES = {MESSAGING_SCOPE};
    private static final String PROJECT_ID = "[YOUR PROJECT ID]";
    private static final String BASE_URL = "https://fcm.googleapis.com";
    private static final String FCM_SEND_ENDPOINT = "/v1/projects/" + PROJECT_ID + "/messages:send";
    private static final String TITLE = "FCM Notification"; // 타이틀 제목
    private static final String BODY = "server message test"; // 메세지 내용
    private static final String TOPIC = "sss"; // 주제
    public static final String MESSAGE_KEY = "message";

    private static String getAccessToken() throws IOException {
        Context context = MainActivity.context;
        GoogleCredential googleCredential = GoogleCredential
                .fromStream(new FileInputStream(new File(context.getFilesDir(), "service-account.json")))
                .createScoped(Arrays.asList(SCOPES));
        googleCredential.refreshToken();
        return googleCredential.getAccessToken();
    }

    private static HttpURLConnection getConnection() throws IOException {
        // [START use_access_token]
        URL url = new URL(BASE_URL + FCM_SEND_ENDPOINT);
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        httpURLConnection.setRequestProperty("Authorization", "Bearer " + getAccessToken());
        httpURLConnection.setRequestProperty("Content-Type", "application/json; UTF-8");
        return httpURLConnection;
        // [END use_access_token]
    }

    private static void sendMessage(JsonObject fcmMessage) throws IOException {
        HttpURLConnection connection = getConnection();
        connection.setDoOutput(true);
        DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
        outputStream.w
    riteBytes(fcmMessage.toString());
        outputStream.flush();
        outputStream.close();

        int responseCode = connection.getResponseCode();
        if (responseCode == 200) {
            String response = inputstreamToString(connection.getInputStream());
            System.out.println("Message sent to Firebase for delivery, response:");
            System.out.println(response);
        } else {
            System.out.println("Unable to send message to Firebase:");
            String response = inputstreamToString(connection.getErrorStream());
            System.out.println(response);
        }
    }

    private static String inputstreamToString(InputStream inputStream) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        Scanner scanner = new Scanner(inputStream);
        while (scanner.hasNext()) {
            stringBuilder.append(scanner.nextLine());
        }
        return stringBuilder.toString();
    }

    private static JsonObject buildNotificationMessage() {
        JsonObject jNotification = new JsonObject();
        jNotification.addProperty("title", TITLE);
        jNotification.addProperty("body", BODY);

        JsonObject jMessage = new JsonObject();
        jMessage.add("notification", jNotification);
        jMessage.addProperty("topic", TOPIC);

        JsonObject jFcm = new JsonObject();
        jFcm.add(MESSAGE_KEY, jMessage);

        return jFcm;
    }

    private static void prettyPrint(JsonObject jsonObject) {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        System.out.println(gson.toJson(jsonObject) + "\n");
    }

    public static void sendCommonMessage() throws IOException {
        JsonObject notificationMessage = buildNotificationMessage();
        System.out.println("FCM request body for message using common notification object:");
        prettyPrint(notificationMessage);
        sendMessage(notificationMessage);
    }
}

 

참조 

github.com/firebase/quickstart-java/blob/9cf1a355682a862b09e1891fc12d5aaa9f89f23b/messaging/src/main/java/com/google/firebase/quickstart/Messaging.java#L45-L51

 

firebase/quickstart-java

Quickstart samples for Firebase Java Admin SDK. Contribute to firebase/quickstart-java development by creating an account on GitHub.

github.com

 

반응형

댓글