- 인쇄
- PDF
푸시
- 인쇄
- PDF
Classic/VPC 환경에서 이용 가능합니다.
개발자가 서버 또는 클라우드에서 모바일 장치로 신뢰할 수 있고 효율적으로 메시지를 전송할 수 있게 합니다. 푸시를 사용하면 알림 메시지(시스템이 사용자에게 표시할 수 있는 메시지)와 데이터 메시지(앱이 처리하는 키-값 쌍의 데이터)를 모바일 앱과 웹 앱에 전송할 수 있습니다. 푸시는 iOS, Android, 웹 애플리케이션을 포함한 다양한 플랫폼을 지원합니다.
주요 기능
- 다양한 메시지 전송: 알림 메시지와 데이터 메시지를 포함하여 다양한 유형의 메시지를 전송할 수 있습니다.
- 대규모 메시징: 수백만 명의 사용자에게 동시에 메시지를 보낼 수 있어, 대규모 애플리케이션의 효율적인 메시지 전송이 가능합니다.
- 다양한 플랫폼 지원: iOS, Android, 웹 애플리케이션 등 다양한 플랫폼에서 메시지를 받을 수 있습니다.
- 고급 메시징 옵션: 메시지 우선순위 설정, 수명(lifetime) 설정, 주제 기반 구독 및 조건부 메시지 전송 등 고급 기능을 제공합니다.
대시보드 설정
FCM 푸시 키 생성 방법
Firebase Console > 프로젝트 설정 > 클라우드 메시징 > 서비스 계정 관리를 클릭해 주십시오.
Google Cloud > 서비스 계정 > 생성되어 있는 이메일 계정을 선택해 주십시오.
키 > 키 추가 > 새 키 만들기 > 키 유형은 'JSON'으로 선택 후 [만들기] 버튼을 클릭해 주십시오.
- 기존에 만들어 놓은 키가 있다면, 해당 파일을 사용해도 됩니다.
- 기존에 만들어 놓은 키가 있다면, 해당 파일을 사용해도 됩니다.
APNS 인증키 생성 방법
애플 개발자 사이트 로그인 > Certificates, IDs & Profiles > 키 선택 > [+] 버튼을 클릭하여 신규 키를 생성해 주십시오.
- 푸시 인증키는 최대 2개까지만 생성할 수 있습니다.
- 푸시 인증키는 최대 2개까지만 생성할 수 있습니다.
Key Name을 입력하고, Apple Push notifications service (APNs)를 활성화해 주십시오.
[Register] 버튼을 누르고 인증키를 발급해 주십시오.
Key ID를 확인하고 다운로드해 주십시오.
- 다운로드 된 파일은 확장자가 .p8 입니다.
- Key 파일은 한번만 다운로드 가능합니다.
대시보드 키 설정 방법
프로젝트 설정 - 연동 - Google Android (FCM) Configuration
- SenderID: Firebase 콘솔 > 프로젝트 설정 > 클라우드 메시징 > 발신자 ID
- Private Key (json file): FCM 푸시 키 생성 방법에서 생성한 JSON 파일을 메모장 혹은 텍스트 편집기와 같은 프로그램으로 연 뒤 전체 내용을 복사하여 등록합니다.
프로젝트 설정 - 연동 - Apple iOS (APNs) Configuration
- Certificate: APNS 인증키 생성 방법 에서 생성한 .p8 파일을 메모장 혹은 텍스트 편집기와 같은 프로그램으로 연 뒤 전체 내용을 복사하여 등록합니다.
- Key ID: APNS 인증키 생성 방법 에서 생성한 .p8 파일의 Key ID를 등록합니다.
- Team ID: 애플 개발자 사이트 '멤버십 세부사항'의 팀 ID를 입력합니다.
- Bundle ID: 앱 패키지의 Bundle Identifier 값을 입력합니다.
푸시 설정 방법 - 모바일
Firebase 프로젝트 설정
- Firebase 콘솔(console.firebase.google.com)에서 프로젝트를 생성합니다.
- 생성된 프로젝트에 애플리케이션(안드로이드 또는 iOS)을 추가합니다.
- 애플리케이션에 필요한 Firebase 구성 파일(
google-services.json
또는GoogleService-Info.plist
)을 다운로드하고 게임팟 대시보드에 설정에 추가합니다.
FCM SDK 추가
- 안드로이드의 경우, (Module : app)과 (Module : project) 각각의 build.gradle.kt 파일에 정의를 추가합니다.
// (Module : app)의 build.gradle.kts
plugins {
...
id("com.google.gms.google-services")
}
dependencies {
...
implementation("com.google.firebase:firebase-messaging-ktx:23.2.1")
}
// (Module : project)의 build.gradle.kts
plugins {
...
id("com.google.gms.google-services") version "4.3.15" apply false
}
- iOS의 경우, CocoaPods을 사용하여
Firebase/Messaging
pod를 설치합니다.
푸시 알림을 위한 권한 요청 (iOS)
- iOS 앱의 경우, 사용자로부터 푸시 알림을 받을 수 있는 권한을 요청해야 합니다.
UNUserNotificationCenter
를 사용하여 요청할 수 있습니다.
- iOS 앱의 경우, 사용자로부터 푸시 알림을 받을 수 있는 권한을 요청해야 합니다.
디바이스 토큰 등록 및 메시지 수신
- 앱이 설치되고 실행될 때, FCM SDK는 앱 인스턴스에 대한 고유한 토큰을 생성합니다. 이 토큰을 서버에 등록하여 특정 디바이스에 메시지를 전송할 수 있습니다.
- 메시지 수신을 위해 애플리케이션에 리스너를 구현합니다.
FCM을 사용하면 애플리케이션의 사용자 참여도를 높이고, 중요한 정보를 신속하게 전달하며, 다양한 메시지 전송 시나리오를 지원하는 맞춤형 알림 시스템을 구축할 수 있습니다. FCM은 개발자가 클라우드 메시징을 쉽게 통합하고 관리할 수 있도록 다양한 도구와 API를 제공합니다.
iOS 코드 추가
AppDelegate 에 아래 코드를 추가합니다.
import NBase
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// 푸시 사용여부 퍼미션 허가 요청
registerForRemoteNotifications()
return true
}
func registerForRemoteNotifications() {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in
if granted {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
} else {
print("The push notification permission has been denied")
}
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
// 토큰을 서버로 전송하여 저장하거나 사용합니다.
NBase.setPushToken(token: token)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
// 푸시 알림 등록에 실패했습니다. 오류: \(error.localizedDescription)
NBase.setPushToken(token: "")
}
Objective-C 프로젝트의 경우 AppDelegate 에 아래 코드를 추가합니다.
#import "[ProjectName]-Swift.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] registerForRemoteNotifications];
});
} else {
NSLog(@"Push permission denied");
}
}];
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSString *token = [self stringWithDeviceToken:deviceToken];
[NBaseBridge.shared setPushToken:token];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"Failed to register for remote notifications: %@", error);
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
completionHandler(UNNotificationPresentationOptionBanner |
UNNotificationPresentationOptionSound |
UNNotificationPresentationOptionBadge);
}
- (NSString *)stringWithDeviceToken:(NSData *)deviceToken {
NSUInteger length = deviceToken.length;
if (length == 0) {
return nil;
}
const unsigned char *buffer = deviceToken.bytes;
NSMutableString *hexString = [NSMutableString stringWithCapacity:(length * 2)];
for (int i = 0; i < length; ++i) {
[hexString appendFormat:@"%02x", buffer[i]];
}
return [hexString copy];
}
Unity 프로젝트에 구성 파일 추가
Firebase Console 설정 워크플로에서 플랫폼별 Firebase 구성 파일을 가져옵니다.
- iOS의 경우, GoogleService-Info.plist 를 다운로드 합니다.
- Android의 경우, google-services.json 를 다운로드 합니다.
구성 파일을 Unity 프로젝트의 Assets 폴더에 이동합니다.
이미지 푸시 설정 방법
iOS 앱에서 알림 이미지를 수신하고 처리하기 위해 알림 서비스 확장을 추가해야 합니다.
이미지 푸시 - iOS 코드 추가
- Swift:
- Xcode 프로젝트의 TARGETS 에서 Notification Service Extension 을 추가합니다
- 생성된 Notification Service Extension 모듈의 NotificationService.swift 파일을 아래와 같이 수정합니다.
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
// didReceive 내용 추가 [START]
guard let bestAttemptContent = bestAttemptContent,
let userInfo = request.content.userInfo as? [String: Any] else {
contentHandler(request.content)
return
}
let imageURL = userInfo["imageUrl"] as? String ??
userInfo["gcm.notification.image"] as? String
guard let imageURL = imageURL, !imageURL.isEmpty else {
contentHandler(bestAttemptContent)
return
}
downloadAndAttachImage(urlString: imageURL) { attachment in
if let attachment = attachment {
bestAttemptContent.attachments = [attachment]
}
}
// didReceive 내용 추가 [END]
}
// downloadAndAttachImage 내용 추가 [START]
private func downloadAndAttachImage(urlString: String, completionHandler: @escaping (UNNotificationAttachment?) -> Void) {
guard let url = URL(string: urlString.trimmingCharacters(in: .whitespaces)) else {
completionHandler(nil)
return
}
let session = URLSession(configuration: .ephemeral)
let task = session.downloadTask(with: url) { (location, response, error) in
if let error = error {
completionHandler(nil)
return
}
guard let location = location else {
completionHandler(nil)
return
}
let fileManager = FileManager.default
let tmpDirectory = NSTemporaryDirectory()
let tmpFile = "image_\(Date().timeIntervalSince1970).png"
let tmpPath = (tmpDirectory as NSString).appendingPathComponent(tmpFile)
let tmpURL = URL(fileURLWithPath: tmpPath)
do {
if fileManager.fileExists(atPath: tmpPath) {
try fileManager.removeItem(atPath: tmpPath)
}
try fileManager.moveItem(at: location, to: tmpURL)
let attachment = try UNNotificationAttachment(
identifier: UUID().uuidString,
url: tmpURL,
options: [UNNotificationAttachmentOptionsTypeHintKey: "public.png"]
)
completionHandler(attachment)
} catch {
completionHandler(nil)
}
}
task.resume()
DispatchQueue.main.asyncAfter(deadline: .now() + 25) {
task.cancel()
}
}
// downloadAndAttachImage 내용 추가 [END]
override func serviceExtensionTimeWillExpire() {
// 기존 내용 유지
}
}
- Objective-C:
- Xcode 프로젝트의 TARGETS 에서 Notification Service Extension 을 추가합니다
- 생성된 Notification Service Extension 모듈의 NotificationService.m 파일을 아래와 같이 수정합니다.
#import "NotificationService.h"
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// didReceiveNotificationRequest 내용 수정 [START]
NSDictionary *userInfo = request.content.userInfo;
NSString *imageUrl = userInfo[@"imageUrl"];
if (!imageUrl) {
imageUrl = userInfo[@"gcm.notification.image"];
}
if (imageUrl && imageUrl.length > 0) {
[self downloadAndAttachImage:imageUrl completionHandler:^(UNNotificationAttachment *attachment) {
if (attachment) {
self.bestAttemptContent.attachments = @[attachment];
}
self.contentHandler(self.bestAttemptContent);
}];
} else {
self.contentHandler(self.bestAttemptContent);
}
// didReceiveNotificationRequest 내용 수정 [END]
}
// downloadAndAttachImage 내용 추가 [START]
- (void)downloadAndAttachImage:(NSString *)urlString completionHandler:(void(^)(UNNotificationAttachment *attachment))completionHandler {
NSURL *url = [NSURL URLWithString:[urlString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
if (!url) {
completionHandler(nil);
return;
}
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
[[session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (error) {
completionHandler(nil);
return;
}
if (!location) {
completionHandler(nil);
return;
}
NSString *tmpDirectory = NSTemporaryDirectory();
NSString *tmpFile = [NSString stringWithFormat:@"image_%f.png", [[NSDate date] timeIntervalSince1970]];
NSString *tmpPath = [tmpDirectory stringByAppendingPathComponent:tmpFile];
NSURL *tmpURL = [NSURL fileURLWithPath:tmpPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *moveError = nil;
if ([fileManager fileExistsAtPath:tmpPath]) {
[fileManager removeItemAtPath:tmpPath error:nil];
}
[fileManager moveItemAtURL:location toURL:tmpURL error:&moveError];
if (moveError) {
completionHandler(nil);
return;
}
NSError *attachmentError = nil;
UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:[[NSUUID UUID] UUIDString]
URL:tmpURL
options:@{UNNotificationAttachmentOptionsTypeHintKey: @"public.png"}
error:&attachmentError];
if (attachmentError) {
completionHandler(nil);
return;
}
completionHandler(attachment);
}] resume];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[session invalidateAndCancel];
});
}
// downloadAndAttachImage 내용 추가 [END]
- (void)serviceExtensionTimeWillExpire {
// serviceExtensionTimeWillExpire 내용 수정 [START]
if (self.contentHandler && self.bestAttemptContent) {
self.contentHandler(self.bestAttemptContent);
}
// serviceExtensionTimeWillExpire 내용 수정 [END]
}
푸시 상태 변경
푸시 수신 여부 설정을 변경하려면 아래 코드를 호출해 주십시오.
- C#:
NBaseSDK.NBase.setPushState(push, night, ad, token, (pushState, error) => {
if (error != null)
{
// failed.
// Display the message using error.message.
}
else
{
// succeeded.
}
});
- Kotlin:
val pushToken = NBase.getPushToken()
val pushState = com.nbase.sdk.model.PushState(
push = enable,
night = night,
ad = ad,
token = pushToken
)
NBase.setPushState(pushState) { status, e ->
if (error != null) {
// failed.
// Display the message using error.message.
} else {
// succeeded.
}
};
- Java:
String pushToken = _NBase.getPushToken();
com.nbase.sdk.model.PushState pushState = new com.nbase.sdk.model.PushState(
Boolean.parseBoolean(enable),
Boolean.parseBoolean(night),
Boolean.parseBoolean(ad),
pushToken
);
NBase nBase = NBase.INSTANCE;
nBase.setPushState(pushState, (status, e) -> {
if (e != null) {
// failed.
// Display the message using e.getMessage.
} else {
// succeeded.
}
return null;
});
- Swift:
NBase.setPushState(enable: enable, ad: ad, night: night, token: NBase.getPushToken()) { result in
switch result {
case .success(let data):
// succeeded.
case .failure(let error):
// failed.
}
}
- Objective-C:
[NBaseBridge.shared setPushState:enable night:night ad:ad token:token :^(NSDictionary * _Nullable result, NSError * _Nullable error) {
if (error) {
// failed.
// Display the message using error.localizedDescription.
} else {
// succeeded.
}
}];
푸시 수신 여부 설정을 확인하려면 아래 코드를 호출해 주십시오.
- C#:
NBaseSDK.NBase.getPushState((pushState, error) => {
if (error != null)
{
// failed.
// Display the message using error.message.
}
else
{
// succeeded.
}
});
- Kotlin:
NBase.getPushState() { state, e ->
if (error != null) {
// failed.
// Display the message using error.message.
} else {
// succeeded.
}
};
- Java:
NBase nBase = NBase.INSTANCE;
nBase.getPushState((state, e) -> {
if (e != null) {
// failed.
// Display the message using e.getMessage.
} else {
// succeeded.
}
return null;
});
- Swift:
NBase.getPushState() { result in
switch result {
case .success(let data):
// succeeded.
case .failure(let error):
// failed.
}
}
- Objective-C:
[NBaseBridge.shared getPushState:^(NSDictionary * _Nullable result, NSError * _Nullable error) {
if (error) {
// failed.
// Display the message using error.localizedDescription.
} else {
// succeeded.
}
}];
푸시 테스트 방법(모바일)
푸시 테스트는 세 가지 방법으로 검증이 가능합니다.
게임팟 대시보드에서 확인 방법
대시보드 > 메시지 > 푸시 알림 메뉴에서 푸시 발송이 가능합니다.(AOS) Firebase 콘솔에서 테스트 방법
Firebase Console - 프로젝트 선택 - Messaging - 캠페인에서 새 캠페인 버튼을 클릭하여 메시지 발송을 테스트를 할 수 있습니다.
앱에서 가져온 토큰을 복사하여 붙여넣기 후 +버튼을 눌러 토큰을 입력하여 테스트 버튼을 누르면 완료되며, 해당 토큰으로 발송이 되는지 확인 가능합니다.
(iOS) CloudKit Console 에서 테스트 방법
CloudKit Console -> Push Notifications 에 진입하여 새로운 Notifications을 생성해 주십시오.
Device Token 입력과 Payload 입력 후 해당 토큰으로 발송이 되는지 확인 가능합니다.
문제 해결
Q. java.lang.IllegalStateException: Default FirebaseApp is not initialized in this process {패키지 이름}. Make sure to call FirebaseApp.initializeApp(Context) first.
A. 해당 오류는 gradle 파일에 푸시를 위한 설정이 누락된 경우입니다. 아래 사항을 확인해 주시기 바랍니다.
프로젝트 최상단 build.gradle.kts을 확인해 주십시오.
plugins {
id("com.google.gms.google-services") version "4.4.1" apply false
}
app 내에 build.gradle.kts를 확인해 주십시오.
plugins {
id("com.google.gms.google-services")
}
Q. org.gradle.api.GradleException: File google-services.json is missing. The Google Services Plugin cannot function without it.
A. 해당 오류는 google-services.json을 찾을 수 없는 경우 발생합니다. 해당 파일을 모듈 : 앱 단위의 루트 폴더에 올바르게 위치했는지 확인해 주십시오.
(예시) ./project folder/app/google-services.json