푸시

개발자가 서버 또는 클라우드에서 모바일 장치로 효율적으로 메시지를 전송할 수 있게 합니다. 푸시를 사용하면 알림 메시지를 모바일 앱과 웹 앱에 전송할 수 있습니다. 푸시는 iOS, Android 애플리케이션을 포함한 다양한 플랫폼을 지원합니다.

2024년 5월 15일부터 Firebase Cloud Messaging(FCM)은 270일 이상 비활성 상태였던 Android 기기에서 토큰이 만료됩니다.

주요 기능

  • 대규모 메시징: 수백만 명의 사용자에게 동시에 메시지를 보낼 수 있어, 대규모 애플리케이션의 효율적인 메시지 전송이 가능합니다.

  • 다양한 플랫폼 지원: iOS, Android 등 다양한 플랫폼에서 메시지를 받을 수 있습니다.

푸시 사용 방법 (모바일)

  1. Firebase 프로젝트 설정

    • Firebase 콘솔(console.firebase.google.com)에서 프로젝트를 생성합니다.

    • 생성된 프로젝트에 애플리케이션(안드로이드 또는 iOS)을 추가합니다.

  2. 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를 설치합니다.

  3. 푸시 알림 아이콘 설정

    푸시 메시지를 수신했을 때 알림 바에 표시할 작은 아이콘을 설정할 수 있습니다. 별도로 설정하지 않을 경우 SDK에 포함된 기본 이미지를 사용하며, 게임에 맞는 아이콘을 직접 설정할 수 있습니다.

    Android Asset Studio를 이용해서 제작하면 자동으로 폴더별로 이미지가 제작되어 편리합니다.

    푸시 알림 아이콘을 설정하는 방법은 다음과 같습니다.

    a. 아래 경로에 res/drawable 폴더들 각각 생성한 후 그 크기에 맞는 아이콘 이미지 파일을 추가합니다.

    폴더명
    크기

    res/drawable-mdpi/

    24x24

    res/drawable-hdpi/

    36x36

    res/drawable-xhdpi/

    48x48

    res/drawable-xxhdpi/

    72x72

    res/drawable-xxxhdpi/

    96x96

    b. 이미지 파일의 이름을 ic_stat_nbase_small로 변경합니다.

  4. Unity FCM SDK 추가

    • Unity의 경우, /Assets/NBaseSDK/Editor/NBaseSDKDependencies.xml 경로 내 firebase-messaging-ktx 라이브러리를 추가합니다.

     <dependencies>
    	<androidPackages>
            <androidPackage spec="io.nbase:nbasesdk:3.0.xx"/>
            ...
            <androidPackage spec="com.google.firebase:firebase-messaging-ktx:23.2.1" />
    	</androidPackages>
    	<iosPods>
    		...
    	</iosPods>
    </dependencies>
    • Assets > External Dependency Manager > Android Resolver > Force Resolve 를 클릭합니다.

  5. Unity 푸시 알림 아이콘 설정

    푸시 메시지를 수신했을 때 알림 바에 표시할 작은 아이콘을 설정할 수 있습니다. 별도로 설정하지 않을 경우 SDK에 포함된 기본 이미지를 사용하며, 게임에 맞는 아이콘을 직접 설정할 수 있습니다.

    Android Asset Studio를 이용해서 제작하면 자동으로 폴더별로 이미지가 제작되어 편리합니다.

    유니티 엔진 202x 이후 버전의 푸시 알림 아이콘을 설정하는 방법은 다음과 같습니다.

    a. 아래 경로에 res/drawable 폴더들 각각 생성한 후 그 크기에 맞는 아이콘 이미지 파일을 추가합니다.

    폴더명
    크기

    /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-mdpi/

    24x24

    /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-hdpi/

    36x36

    /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-xhdpi/

    48x48

    /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-xxhdpi/

    72x72

    /Assets/Plugins/Android/GamePotResources.androidlib/res/drawable-xxxhdpi/

    96x96

    b. mainTemplate.gradle에 해당 라이브러리를 추가합니다.

    ...
        implementation project('GamePotResources.androidlib')
    ...

    c. 이미지 파일의 이름을 ic_stat_nbase_small로 변경합니다.

  6. 푸시 알림을 위한 권한 요청 (iOS)

    • iOS 앱의 경우, 사용자로부터 푸시 알림을 받을 수 있는 권한을 요청해야 합니다. UNUserNotificationCenter를 사용하여 요청할 수 있습니다.

  7. 디바이스 토큰 등록 및 메시지 수신

    • 앱이 설치되고 실행될 때, FCM SDK는 앱 인스턴스에 대한 고유한 토큰을 생성합니다. 이 토큰을 서버에 등록하여 특정 디바이스에 메시지를 전송할 수 있습니다.

    • 메시지 수신을 위해 애플리케이션에 리스너를 구현합니다.

FCM을 사용하면 애플리케이션의 사용자 참여도를 높이고, 중요한 정보를 신속하게 전달하며, 다양한 메시지 전송 시나리오를 지원하는 맞춤형 알림 시스템을 구축할 수 있습니다. FCM은 개발자가 클라우드 메시징을 쉽게 통합하고 관리할 수 있도록 다양한 도구와 API를 제공합니다

푸시를 설정하기 위한 코드 추가

Tools -> GamePotSDK -> Edit Settings 에 Use Notification 를 체크해 주세요.

Firebase Console 설정 워크플로에서 플랫폼별 Firebase 구성 파일을 가져옵니다.

  • Android의 경우, google-services.json 를 다운로드 합니다.

구성 파일을 Unity 프로젝트의 Assets 폴더에 이동합니다.

(선택) 이미지 푸시 설정 방법

  • iOS 앱에서 알림 이미지를 수신하고 처리하기 위해 알림 서비스 확장을 추가해 주십시오.

  1. Xcode 프로젝트의 TARGETS 에서 Notification Service Extension 을 추가합니다.

  2. 생성된 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() {
        // 기존 내용 유지
    }
}

푸시 상태 변경

  • 푸시 수신 여부 설정을 변경하려면 아래 코드를 호출해 주십시오.

NBaseSDK.NBase.setPushState(enable, night, ad, token, (pushState, error) => {
    if (error != null)
    {
        // failed.
        // Display the message using error.message.
    }
    else
    {
        // succeeded.
    }
});
  • 푸시 수신 여부 설정을 확인하려면 아래 코드를 호출해 주십시오.

NBaseSDK.NBase.getPushState((pushState, error) => {
    if (error != null)
    {
        // failed.
        // Display the message using error.message.
    }
    else
    {
        // succeeded.
    }
});

푸시 테스트 방법(모바일)

푸시 테스트는 세 가지 방법으로 검증이 가능합니다.

  1. 게임팟 대시보드에서 확인 방법 대시보드 -> 회원 목록 -> 회원 정보(자세히) 푸시 발송 버튼을 클릭하면 해당 회원에게 개별로 푸시 발송이 가능합니다.

  1. (AOS) Firebase 콘솔에서 테스트 방법

Firebase Console - 프로젝트 선택 - Messaging - 캠페인에서 새 캠페인 버튼을 클릭하여 메시지 발송 테스트를 할 수 있습니다.

앱에서 가져온 토큰을 복사하여 붙여넣기 후 +버튼을 눌러 토큰을 입력하여 테스트 버튼을 누르면 완료되며, 해당 토큰으로 발송이 되는지 확인 가능합니다.

  1. (iOS) CloudKit Console 에서 테스트 방법

CloudKit Console -> Push Notifications 에 진입하여 새로운 Notifications을 생성합니다.

Device Token 입력과 Payload 입력 후 해당 토큰으로 발송이 되는지 확인 가능합니다.

문제해결

Q. java.lang.IllegalStateException: Default FirebaseApp is not initialized in this process com.nbase.main. 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

Last updated