Android

[Android] Firebase 푸시 알림 테스트(1) - 세팅 및 Client 소스코드 작성

이주여이 2025. 5. 20. 15:02

0. Firebase 회원가입

https://console.firebase.google.com/에서 ‘Firebase 프로젝트 만들기’를 클릭한다.

프로젝트를 만든 후 나오는 ‘google-services.json’을 다운로드 받은 후 프로젝트 내 app 경로에 넣는다.

1. Firebase 의존성 주입

없는 부분은 추가하도록 하자.

1. lib.versions.toml

[version]
firebaseBom = "33.13.0" # firebase 통합 버전 관리용
googleGmsGoogleServices = "4.4.2" # google services 플러그인 버전
firebaseMessagingKtx = "24.1.1" # firebase messaging KTX 라이브러리 버전

[libraries]
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } # firebase 버전 충돌 방지
firebase-messaging = { module = "com.google.firebase:firebase-messaging" }
firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
firebase-messaging-ktx = { group = "com.google.firebase", name = "firebase-messaging-ktx", version.ref = "firebaseMessagingKtx" }

[plugins]
google-gms-google-services = { id = "com.google.gms.google-services", version.ref = "googleGmsGoogleServices" } # google-services.json 파일을 읽어 firebase 초기화를 자동으로 해준다.

2. build.gradle.kts(project)

plugins {
    alias(libs.plugins.google.gms.google.services) apply false
}

프로젝트 전체에서 사용할 수 있는 플러그인을 미리 정의해놓는다.

실제 적용은 build.gradle.kts(APP)에서 진행한다.

3. build.gradle.kts(app)

plugins {
    alias(libs.plugins.google.gms.google.services)
}

android {
    # 생략 ...
}

dependencies {
    implementation(platform(libs.firebase.bom)) # firebase 전용 통합 버전 설정(없을 경우 의존성 충돌 가능성이 있다)
    implementation(libs.firebase.messaging) # FCM 푸쉬 알림 기능을 사용하기 위한 라이브러리
    implementation(libs.firebase.analytics) # firebase 앱 분석 기능(선택이지만 보통 같이 넣는다)
}

Firebase 연동을 위해 libs.versions.toml 파일에 Firebase BOM, Messaging, Analytics 라이브러리를 정의한다.

이후, build.gradle.kts에서 해당 라이브러리들을 불러오고 google-services 플러그인을 통해 google-services.json을 자동으로 인식시켜 Firebase 서비스를 연결한다.

2. AndroidManifest.xml 수정

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- (필수) Android 13 이상부터 필수로 알림을 사용자에게 보여주려면 권한 요청이 필요하다. 해당 퍼미션이 없을 경우 푸시 알림이 와도 상단에 표시되지 않는다. -->
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.STOCK"
        tools:targetApi="31">

        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        **<service
            android:name=".service.MyFirebaseMessagingService"
            android:exported="true">
            <intent-filter>
                <action android:name="com.google.firebase.MESSING_EVENT" />
            </intent-filter>
        </service>**
    </application>
</manifest>
<!-- 안드로이드에서 백그라운드 서비스를 등록한다. -->
<!-- 푸시 알림을 수신하는 서비스인 MyFirebaseMessagingService를 앱에 연결한다. -->
<service
    android:name=".service.MyFirebaseMessagingService"
    android:exported="true">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
  • android:exported="true"
    • Android12(API 31) 이상에서 반드시 선언해야하는 속성으로 외부에서 이 서비스 실행을 허용한다는 의미다.
    • FMC이 Google Play 서비스를 통해 외부에서 메세지를 보내기 때문에 true가 꼭 필요하다.
  • <intent-filter><action android:name="com.google.firebase.MESSING_EVENT" /></intent-filter>
    • FCM에서 푸시 메세지를 보낼 때 실행되는 이벤트 이름을 설정하여 FCM 메세지가 오면 이 클래스로 전달해라는 의미다.

3. FMC 토큰 가져오기

[경로] app.util.FmcTokenHelper.kt

object FmcTokenHelper {
    fun getFCMToken(context: Context) {
        FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
            if(!task.isSuccessful) {
                Log.w("FCM", "Fetching FCM registration token failed", task.exception)

                return@addOnCompleteListener
            }

            val token = task.result
            val msg = "FCM Token : $token"

            Log.d("FCM", msg)

            Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
        }
    }
}

 

[경로] com.project.project_name.MainActivity.kt

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val navController = findNavController(R.id.nav_host_fragment_activity_main)

        val navView : BottomNavigationView = binding.navView
        navView.setupWithNavController(navController)

        // 생략 ...

        // FMC 토큰 가져오기
        FmcTokenHelper.getFCMToken(this)
    }
}
2025-05-19 11:46:04.052 12954-12954 FCM                     com.project.stock                    D  FCM Token : 여기에 토큰 값이 뜬다.

4. MyFirebaseMessagingService.kt 작성

[경로] app.service.MyFirebaseMessagingService.kt

// FirebaseMessagingService를 상속 받는다.
class MyFirebaseMessagingService : FirebaseMessagingService() {
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        // 알림 메세지 수신 시
        remoteMessage.notification?.let {
            val title = it.title ?: "제목 없음"
            val body = it.body ?: "내용 없음"

            sendPushNotification(title, body)
        }

        // 데이터 메세지 수신 시
        if(remoteMessage.data.isNotEmpty()) {
            val title = remoteMessage.data["title"] ?: "데이터 제목 없음"
            val body = remoteMessage.data["body"] ?: "데이터 내용 없음"

            sendPushNotification(title, body)
        }
    }

    // 실제 푸시 알림을 생성하고 띄우는 함수
    private fun sendPushNotification(title: String, body: String) {
        val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           val channel = NotificationChannel(
               "default_channel", // Channel ID
               "Default Channel", // Channel NAME
               NotificationManager.IMPORTANCE_DEFAULT
           )

            manager.createNotificationChannel(channel)
        }

        // 알림 내용 구성(제목, 내용, 아이콘 등)
        val notification = NotificationCompat.Builder(this, "default_channel")
            .setContentTitle(title)
            .setContentText(body)
            .setSmallIcon(R.drawable.ic_notifications_black_24dp)
            .setAutoCancel(true)
            .build()

        manager.notify(1, notification) // 푸시 알림 표시
    }
}