이번 프로젝트에서 맡은 역할 중 FCM을 활용한 알림 서비스 구현 Task가 있어 먼저 자세하게 FCM이 어떤건지.. 어떻게 구현하는건지.. 어떤 방식으로 구성되어있는지 한번 알아보려고 한다!
먼저 FCM은 Firebase Cloud Messaging으로 말 그대로 Firebase 플랫폼에서 제공하는 메세지 전송 서비스로 앱에서 푸시 알림을 전달하는데 주로 사용 되게 된다! 이 서비스는 앱에 실시간 데이터를 전송하거나 사용자에게 알림을 제공하는데 유용하게 사용된다고 한다! 또한, 크로스플랫폼을 지원하는 것도 장점!!
그럼 기본 개념에 대해서 알아보자
- FCM의 구조
- 앱 클라이언트 : 푸시 알림을 수신 하는 앱
- FCM 서버 : 알림을 전송하는 서버 (Firebase에서 관리)
- FCM 전송 API : 푸시 알림을 전송하기 위해 클라이언트가 Firebase 서버와 상호작용하는 방식
먼저 FCM은 사진과 같이 주요 구성요소가 나눠져 있다.
- 메세지 유형
- 알림 메세지(Notification Messages) : 사용자에게 표시되는 메세지이며 이 메세지는 일반적으로 텍스트와 알림의 제목 등을 포함하고 있다.
- 데이터 메세지(Data Messages) : 클라이언트 앱이 알림을 수신하고 데이터를 처리하는데 사용하게 된다. 사용자가 이를 통해서 실시간 데이터를 받아오는 경우가 많다!!
보낼 수 있는 메세지의 유형은 두가지로 나눠진다!
- 메세지 전송 방식
- 주제 기반 메세지(Topic Messaging) : 특정 주제를 구독한 모든 클라이언트에 메세지를 전송한다.
- 디바이스 기반 메세지(Device Group Messaging) : 특정 디바이스 그룹에 메세지를 전송한다.
- 단일 디바이스 메세지(Direct Messaging) : 특정 단일 디바이스로 메세지를 전송한다.
FCM은 메세지를 전송하는 방법에 따라 주요 방식을 나눌 수 있다.
- 푸시 알림 전송 흐름
- 사용자 디바이스는 Firebase 클라우드에 등록되어 고유한 FCM Token을 받게 된다.
- FCM 서버는 클라이언트가 보내는 메세지를 FCM 전송 API를 통해 Firebase Cloud로 전송한다.
- Firebase Cloud는 FCM 서버와 통신하여 메세지를 해당 디바이스로 전송한다.
이제 FCM에 대해서 알아봤으니 그럼 실제 프로젝트에서는 어떻게 적용해야 하는지 예제를 통해서 한번 알아보자
먼저 당연히 Firebase Package를 추가해줘야 한다! 이런건 넘어가도록 하쟈 🙂
Firebase에서 FCM을 사용할 수 있는 환경을 만들어 준 뒤
앱에서 백그라운드 테스크와 Notification을 사진과 같이 넣어준다.
이제 각 구조에서 어떤 방식으로 구현되는건지 예제를 통해서 알아보도록 하쟈
- AppDelegate
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// 1. Firebase 초기화
FirebaseApp.configure()
// 2. FCM 델리게이트 설정
Messaging.messaging().delegate = self
// 3. 알림 권한 요청
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
print("알림 권한: \\(granted)")
}
// 4. 원격 알림 등록
application.registerForRemoteNotifications()
return true
}
}
먼저 왜 AppDelegate에서 실행되어야 하는 이유는 앱이 완전히 로드 되기 전에 Firebase 서비스가 준비되어야 하며, GoogleService-Info.plist 파일을 읽고 Firebase 프로젝트와 연결한다. 그리고 다른 Firebase 서비스들의 기반이 되기 때문에 AppDelegate에서 실행되어야 한다는 점!
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
print("알림 권한: \\(granted)")
}
그리고 사용자가 앱 알림을 받을지 안받을지 설정할 수 있어야 하기 때문에 이 내용을 추적할 수 있도록 Notification을 작성해준다.
그 다음
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
APNs 토큰과 연결할 수 있도록 작성해 줘야 한다. 이렇게 해야 FCM이 Apple 서버를 통해 iOS 기기에 알림을 전송하기 위해 필요하며 기기별로 고유한 식별자 역할을 담당하게 된다.
extension AppDelegate: UNUserNotificationCenterDelegate {
// 포그라운드에서 알림 수신
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
print("포그라운드에서 알림 수신")
completionHandler([.alert, .sound, .badge])
}
// 알림 탭 처리
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
NotificationCenter.default.post(name: .notificationTapped, object: userInfo)
completionHandler()
}
}
그 다음 포그라운드에서도 알림을 받을 수 있도록 설정을 별도로 해줘야 한다. iOS 에서는 앱이 포그라운드에 있다면 푸시 알림을 표시하지 않는게 디폴트이기 때문!!
코드 내 willPresent 메서드는 다음과 같은 역할을 담당한다.
- 앱이 활성 상태일 때도 알림을 표시할지 결정!!
- 사용자 경험 향상(중요한 알림 놓치지 않도록)
- 알림 표시 방식 커스터마이징 가능 (소리, 배지, 알림창 등)
그렇다면 didReceive는 어떤 역할을 담당하게 될까
- 사용자가 알림을 탭했을 때 동작을 정의할 수 있다.
- 딥 링크, 특정 화면 이동 등 보다 세밀한 유저 플로우 구성이 가능해진다.
- 알림 데이터를 앱 로직으로 전달 할 수 있다.
그 다음 간편하게 Manager를 만들어 상태를 관리할 수 있도록 해보자 지금까지 하는 내용은 간단한 예제인 점 참고!!
class NotificationManager: ObservableObject {
@Published var fcmToken: String?
@Published var showAlert = false
@Published var alertMessage = ""
@Published var notificationLogs: [String] = []
init() {
setupNotificationObservers()
refreshFCMToken()
}
}
@Published를 활용해 UI가 데이터 변경에 자동으로 반응하도록 해당 값을 넣어준다.
private func setupNotificationObservers() {
// FCM 토큰 업데이트 관찰
NotificationCenter.default.addObserver(
forName: .fcmTokenUpdated,
object: nil,
queue: .main
) { [weak self] notification in
if let token = notification.object as? String {
self?.fcmToken = token
self?.addLog("FCM 토큰 업데이트됨")
}
}
}
이런식으로 구현을 하게 되면 다음과 같은 아키텍처 데이터 플로우를 가지게 된다.
Firebase Server → APNS → iOS System → AppDelegate → NotificationCenter → NotificationManager → SwiftUI View
↓
[Background Processing]
↓
UNUserNotificationCenter
↓
User Interaction → AppDelegate → NotificationCenter → NotificationManager → SwiftUI View
일단 이정도로 작성하면서 구현해보고 있는데 조금 더 만져보고 실제 적용 방식을 해보면서 추가적으로 FCM을 적용한 알림 서비스를 구현해보도록 할 예정 🙂
오늘은 여기까지!!!
'◽️ Programming > T I L' 카테고리의 다른 글
모듈화에 대한 고민, 최적의 구조를 찾아야한다!! (0) | 2025.05.09 |
---|---|
단방향 데이터 흐름이 왜 좋은데!? (0) | 2025.04.03 |
모듈화 아키텍처를 적용해 재사용성 높히기 (0) | 2025.03.27 |
모듈화 아키텍처를 활용한 프로젝트 관리 (0) | 2025.03.20 |
Moya를 활용해서 API 호출해 챗봇 기능 구현하기 (0) | 2025.03.17 |