-
[.NET MAUI, FCM] 안드로이드 앱 알림 보내기[.NET] 공부 기록/MAUI 2025. 6. 18. 14:00
안녕하세요! Becca 입니다.
오늘은 .NET MAUI와 FCM을 이용해서 안드로이드 앱을 만들고, 알림을 보내는 과정을 정리해보았습니다.
🖥️ 개발 환경
.NET MAUI (Multi-platform App UI)
◆ C#과 XAML을 사용하여 네이티브 모바일 및 데스크톱 앱을 만들기 위한 크로스 플랫폼 프레임워크
◆ 단일 공유 코드 베이스에서 Android, iOS, macOS, Windows에서 실행할 수 있는 앱 개발 가능
◆ Visual Studio 2022 17.12 이상 필요 (.NET 8.0)
FCM (Firebase Cloud Messaging)
◆ 메시지를 안정적으로 전송할 수 있는 크로스 플랫폼 메시징 솔루션
◆ 알림 메시지/데이터 메시지 전송 기능
◆ Android 5.0, API Level 21(Lollipop) 이상 필요
📑 프로젝트 만들기
1. .NET MAUI 설치
2. 새 프로젝트 만들기
3. NuGet 패키지 설치
※ 플러그인 최신 버전은 Android 14.0, API 34부터 실행 가능
Plugin.Firebase 설치 실패하는 경우
1. Ctrl + `로 터미널을 열기
2. 루트 프로젝트 디렉토리로 이동
3. 'dotnet add package Plugin.Firebase'를 입력
4. Mac Catalyst, Windows 삭제
[ MauiAlarm.csproj ]
<PropertyGroup> <TargetFrameworks>net8.0-android;</TargetFrameworks> <!-- 생략 --> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion> </PropertyGroup>
◆ 플러그인에서 Mac Catalyst, Windows를 지원하지 않음
◆ 이 프로젝트에서 iOS는 사용하지 않아 삭제
⚙️ Firebase Console 설정
1. 프로젝트 만들기
◆ Firebase Console 접속 (구글 계정으로 로그인이 필요합니다!)
2. Android 선택
3. 앱 등록
패키지 이름(Application ID)
◆ 기기와 Google Play 스토어에서 앱을 고유하게 식별하는 역할
◆ 앱에서 실제로 사용 중인 패키지 이름을 입력해야 함
◆ Firebase 프로젝트에 등록한 후에는 변경 불가능
◆ MAUI 프로젝트에서는 반드시 .csproj의 ApplicationId를 사용해야 함 ⭐⭐⭐
<ApplicationId>com.companyname.mauialarm</ApplicationId>
4. google-services.json 파일 다운로드 후 추가
◆ google-service.json의 package_name이 ApplicationId와 같은지 확인 ⭐⭐⭐
[ google-service.json ]
"android_client_info": { "package_name": "com.companyname.mauialarm" }
[ MauiAlarm.csproj ]
<ApplicationId>com.companyname.mauialarm</ApplicationId>
◆ TargetFramework 추가
[ MauiAlarm.csproj ]
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0-android'"> <GoogleServicesJson Include="google-services.json" /> </ItemGroup>
5. 설정 완료
🔔 알림 보내기
1. Firebase 초기화
[ MauiProgram.cs ]
public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .RegisterFirebaseServices() // 추가 .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }); #if DEBUG builder.Logging.AddDebug(); #endif return builder.Build(); } private static MauiAppBuilder RegisterFirebaseServices(this MauiAppBuilder builder) { builder.ConfigureLifecycleEvents(events => { #if ANDROID events.AddAndroid(android => android.OnCreate((activity, _) => { CrossFirebase.Initialize(activity, CreateCrossFirebaseSettings()); // 초기화 })); #endif }); builder.Services.AddSingleton(_ => CrossFirebaseAuth.Current); return builder; }
2. Cloud Messaging(푸시 알림) 활성화
[ MauiProgram.cs ]
private static CrossFirebaseSettings CreateCrossFirebaseSettings() { return new CrossFirebaseSettings( isAuthEnabled: true, isCloudMessagingEnabled: true); }
3. 빌드 오류 방지를 위한 ItemGroup 추가
[ MauiAlarm.csproj ]
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0-android'"> <PackageReference Include="Xamarin.Kotlin.StdLib.Jdk7" Version="1.9.23.3" ExcludeAssets="build;buildTransitive" /> <PackageReference Include="Xamarin.Kotlin.StdLib.Jdk8" Version="1.9.23.3" ExcludeAssets="build;buildTransitive" /> </ItemGroup> <ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'"> <PackageReference Include="Xamarin.AndroidX.Core" Version="1.15.0.1" /> <PackageReference Include="Xamarin.AndroidX.Collection" Version="1.4.5.1" /> <PackageReference Include="Xamarin.AndroidX.Collection.Ktx" Version="1.4.5.1" /> <PackageReference Include="Xamarin.AndroidX.Activity.Ktx" Version="1.9.3.1" /> <PackageReference Include="Xamarin.AndroidX.Browser" Version="1.8.0.7" /> <PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData.Core" Version="2.8.7.1" /> <PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData.Core.Ktx" Version="2.8.7.1" /> </ItemGroup>
4. Crashlytics 설정
[ MauiProgram.cs ]
private static MauiAppBuilder RegisterFirebaseServices(this MauiAppBuilder builder) { builder.ConfigureLifecycleEvents(events => { #if ANDROID events.AddAndroid(android => android.OnCreate((activity, _) => { CrossFirebase.Initialize(activity, CreateCrossFirebaseSettings()); CrossFirebaseCrashlytics.Current.SetCrashlyticsCollectionEnabled(true); // Crashlytics })); #endif }); builder.Services.AddSingleton(_ => CrossFirebaseAuth.Current); return builder; } private static CrossFirebaseSettings CreateCrossFirebaseSettings() { return new CrossFirebaseSettings( isAuthEnabled: true, isCloudMessagingEnabled: true, isAnalyticsEnabled: true); // Crashlytics }
◆ Crashlytics: 실시간 비정상 종료 보고 도구
◆ 1, 2번 코드에서 Crashlytics 관련 코드를 추가
5. strings.xml 추가
[ strings.xml ]
<?xml version="1.0" encoding="utf-8" ?> <resources> <string name="com.google.firebase.crashlytics.mapping_file_id">none</string> </resources>
6. Android 설정
[ AndroidManifest.xml ]
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"> <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" /> <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="${applicationId}" /> </intent-filter> </receiver> </application> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-sdk android:minSdkVersion="21" /> </manifest>
[ MainActivity.cs ]
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public class MainActivity : MauiAppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); HandleIntent(Intent); CreateNotificationChannelIfNeeded(); } protected override void OnNewIntent(Intent intent) { base.OnNewIntent(intent); HandleIntent(intent); } private static void HandleIntent(Intent intent) { FirebaseCloudMessagingImplementation.OnNewIntent(intent); } private void CreateNotificationChannelIfNeeded() { if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { CreateNotificationChannel(); } } private void CreateNotificationChannel() { var channelId = $"{PackageName}.general"; var notificationManager = (NotificationManager)GetSystemService(NotificationService); var channel = new NotificationChannel(channelId, "General", NotificationImportance.Default); notificationManager.CreateNotificationChannel(channel); FirebaseCloudMessagingImplementation.ChannelId = channelId; } }
7. 버튼 클릭으로 FCM 등록 토큰 얻기
[ MainPage.xaml ]
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiAlarm.MainPage"> <ScrollView> <VerticalStackLayout Padding="30,0" Spacing="25"> <Button x:Name="CounterBtn" Text="GetToken" Clicked="OnGetTokenBtnClicked" HorizontalOptions="Center" /> </VerticalStackLayout> </ScrollView> </ContentPage>
[ MainPage.xaml.cs ]
private async void OnGetTokenBtnClicked(object sender, EventArgs e) { await CrossFirebaseCloudMessaging.Current.CheckIfValidAsync(); var token = await CrossFirebaseCloudMessaging.Current.GetTokenAsync(); Console.WriteLine($"FCM token: {token}"); }
8. Firebase 알림 메시지 만들기
9. 테스트 메시지 보내기
➕ 전체 코드
[ MauiAlarm.csproj ]
더보기<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>net8.0-android;</TargetFrameworks> <OutputType>Exe</OutputType> <RootNamespace>MauiAlarm</RootNamespace> <UseMaui>true</UseMaui> <SingleProject>true</SingleProject> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <!-- Display name --> <ApplicationTitle>MauiAlarm</ApplicationTitle> <!-- App Identifier --> <ApplicationId>com.companyname.mauialarm</ApplicationId> <!-- Versions --> <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> <ApplicationVersion>1</ApplicationVersion> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion> </PropertyGroup> <ItemGroup> <!-- App Icon --> <MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" /> <!-- Splash Screen --> <MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" /> <!-- Images --> <MauiImage Include="Resources\Images\*" /> <MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185" /> <!-- Custom Fonts --> <MauiFont Include="Resources\Fonts\*" /> <!-- Raw Assets (also remove the "Resources\Raw" prefix) --> <MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" /> </ItemGroup> <ItemGroup> <None Remove="google-services.json" /> <None Remove="Platforms\Android\Resources\values\strings.xml" /> </ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0-android'"> <GoogleServicesJson Include="google-services.json" /> </ItemGroup> <ItemGroup> <PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" /> <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.1" /> <PackageReference Include="Plugin.Firebase" Version="3.1.4" /> <PackageReference Include="Plugin.Firebase.Crashlytics" Version="3.1.1" /> </ItemGroup> <ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'"> <PackageReference Include="Xamarin.AndroidX.Core" Version="1.15.0.1" /> <PackageReference Include="Xamarin.AndroidX.Collection" Version="1.4.5.1" /> <PackageReference Include="Xamarin.AndroidX.Collection.Ktx" Version="1.4.5.1" /> <PackageReference Include="Xamarin.AndroidX.Activity.Ktx" Version="1.9.3.1" /> <PackageReference Include="Xamarin.AndroidX.Browser" Version="1.8.0.7" /> <PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData.Core" Version="2.8.7.1" /> <PackageReference Include="Xamarin.AndroidX.Lifecycle.LiveData.Core.Ktx" Version="2.8.7.1" /> </ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0-android'"> <PackageReference Include="Xamarin.Kotlin.StdLib.Jdk7" Version="1.9.23.3" ExcludeAssets="build;buildTransitive" /> <PackageReference Include="Xamarin.Kotlin.StdLib.Jdk8" Version="1.9.23.3" ExcludeAssets="build;buildTransitive" /> </ItemGroup> </Project>
[ MauiProgram.cs ]
더보기using Microsoft.Extensions.Logging; using Microsoft.Maui.LifecycleEvents; using Plugin.Firebase.Auth; using Plugin.Firebase.Bundled.Shared; using Plugin.Firebase.Crashlytics; #if ANDROID using Plugin.Firebase.Bundled.Platforms.Android; #endif namespace MauiAlarm { public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .RegisterFirebaseServices() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); }); #if DEBUG builder.Logging.AddDebug(); #endif return builder.Build(); } private static MauiAppBuilder RegisterFirebaseServices(this MauiAppBuilder builder) { builder.ConfigureLifecycleEvents(events => { #if ANDROID events.AddAndroid(android => android.OnCreate((activity, _) => { CrossFirebase.Initialize(activity, CreateCrossFirebaseSettings()); CrossFirebaseCrashlytics.Current.SetCrashlyticsCollectionEnabled(true); })); #endif }); builder.Services.AddSingleton(_ => CrossFirebaseAuth.Current); return builder; } private static CrossFirebaseSettings CreateCrossFirebaseSettings() { return new CrossFirebaseSettings( isAuthEnabled: true, isCloudMessagingEnabled: true, isAnalyticsEnabled: true); } } }
[ strings.xml ]
더보기<?xml version="1.0" encoding="utf-8" ?> <resources> <string name="com.google.firebase.crashlytics.mapping_file_id">none</string> </resources>
[ AndroidManifest.xml ]
더보기<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"> <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" /> <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="${applicationId}" /> </intent-filter> </receiver> </application> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-sdk android:minSdkVersion="21" /> </manifest>
[ MainActivity.cs ]
더보기using Android.App; using Android.Content; using Android.Content.PM; using Android.OS; using Plugin.Firebase.CloudMessaging; namespace MauiAlarm { [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public class MainActivity : MauiAppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); HandleIntent(Intent); CreateNotificationChannelIfNeeded(); } protected override void OnNewIntent(Intent intent) { base.OnNewIntent(intent); HandleIntent(intent); } private static void HandleIntent(Intent intent) { FirebaseCloudMessagingImplementation.OnNewIntent(intent); } private void CreateNotificationChannelIfNeeded() { if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { CreateNotificationChannel(); } } private void CreateNotificationChannel() { var channelId = $"{PackageName}.general"; var notificationManager = GetSystemService(NotificationService) as NotificationManager; var channel = new NotificationChannel(channelId, "General", NotificationImportance.Default); notificationManager.CreateNotificationChannel(channel); FirebaseCloudMessagingImplementation.ChannelId = channelId; //FirebaseCloudMessagingImplementation.SmallIconRef = Resource.Drawable.ic_push_small; } } }
[ MainPage.xaml ]
더보기<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiAlarm.MainPage"> <ScrollView> <VerticalStackLayout Padding="30,0" Spacing="25"> <Image Source="dotnet_bot.png" HeightRequest="185" Aspect="AspectFit" SemanticProperties.Description="dot net bot in a race car number eight" /> <Label Text="Hello, World!" Style="{StaticResource Headline}" SemanticProperties.HeadingLevel="Level1" /> <Label Text="Welcome to .NET Multi-platform App UI" Style="{StaticResource SubHeadline}" SemanticProperties.HeadingLevel="Level2" SemanticProperties.Description="Welcome to dot net Multi platform App U I" /> <Button x:Name="CounterBtn" Text="GetToken" Clicked="OnGetTokenBtnClicked" HorizontalOptions="Center" /> </VerticalStackLayout> </ScrollView> </ContentPage>
[ MainPage.xaml.cs ]
더보기using Plugin.Firebase.CloudMessaging; namespace MauiAlarm { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); } private async void OnGetTokenBtnClicked(object sender, EventArgs e) { await CrossFirebaseCloudMessaging.Current.CheckIfValidAsync(); var token = await CrossFirebaseCloudMessaging.Current.GetTokenAsync(); Console.WriteLine($"FCM token: {token}"); } } }
🔗 참고 자료
https://learn.microsoft.com/ko-kr/dotnet/maui/get-started/installation?view=net-maui-9.0&tabs=visual-studio
Visual Studio 2022 및 Visual Studio Code를 설치하여 .NET MAUI를 사용하여 플랫폼 간 앱 개발 - .NET MAUI
.NET MAUI 확장을 사용하여 Visual Studio 2022 및 Visual Studio Code를 설치하여 .NET MAUI를 사용하여 네이티브 플랫폼 간 앱을 개발하는 방법을 알아봅니다.
learn.microsoft.com
https://github.com/TobiasBuchholz/Plugin.Firebase
GitHub - TobiasBuchholz/Plugin.Firebase: Wrapper around the native Android and iOS Firebase Xamarin SDKs
Wrapper around the native Android and iOS Firebase Xamarin SDKs - TobiasBuchholz/Plugin.Firebase
github.com
https://cedricgabrang.medium.com/firebase-push-notifications-in-net-maui-android-32c808844d7e
Firebase Push Notifications in .NET MAUI (Android)
In this post, you’ll learn how to implement push notifications in your .NET MAUI application using Firebase.
cedricgabrang.medium.com
Firebase Cloud Messaging
Firebase 클라우드 메시징(FCM)은 메시지를 안정적으로 전송할 수 있는 크로스 플랫폼 메시징 솔루션입니다.
firebase.google.com
백그라운드 앱에 테스트 메시지 보내기 | Firebase Cloud Messaging
새로운 Firebase Studio 기능부터 AI 통합 방법까지 I/O에서 발표된 모든 내용을 확인해 보세요. 블로그 읽기 의견 보내기 백그라운드 앱에 테스트 메시지 보내기 컬렉션을 사용해 정리하기 내 환경설
firebase.google.com
'[.NET] 공부 기록 > MAUI' 카테고리의 다른 글
[.NET MAUI] NETSDK1202 - 'net7.0-android' 워크로드는 지원되지 않으며 향후 보안 업데이트를 받지 않습니다. 오류 해결 방법 (0) 2025.06.13