Tags :

Date :

iOS 푸시알림 FCM, APNS

iOS에서 푸시 종류는 2가지가 있다.

  1. 로컬에서 직접 보내는 알림
  2. 서버에서 보내는 알림
    • 서버에서 보내는 APNS(Apple Push Notification Service)
    • FCM(Firebase Cloud Messaging) -> 이번 포스팅에서는 FCM을 활용한 2번 방식을 진행할 예정이다.

APNS 란?

APNS (Apple Push Notification Service)
iOS의 모든 원격 알림이 반드시 거쳐야 하는 애플 공식 푸시 전달 서버이다..

즉, 푸시 메시지를 내가 직접 사용자 기기로 보내는 게 아니라
APNS에 요청
APNS가 사용자 기기로 전송
하는 방식이다.


FCM 란?

FCM은 Google에서 제공하는 클라우드 기반 메시징 서비스로,
iOS의 APNS를 포함해 Android, Web 등 다양한 플랫폼에 푸시 메시지를 전송할 수 있도록 중간 서버 역할을 해준다.
iOS에서 FCM을 통해 푸시 알림을 보내면, FCM 내부적으로 APNS를 거쳐서 사용자 기기에 도달한다.

FCM이 APNS 대신 해주는 것들

  • 애플 인증서 등록 및 관리
  • APNS에 메시지 전송 요청
  • 안드로이드, iOS 둘 다 지원 (멀티 플랫폼)
  • 클라우드 환경에서 손쉽게 운영

왜 FCM을 쓰는가?

  • APNS 서버 직접 구축이 복잡하고 부담스러울 때
  • iOS & Android를 동시에 지원하는 일관된 환경이 필요할 때
  • 푸시 알림 시스템을 빠르게 구축하고 싶을 때
  • Firebase Analytics, A/B 테스트 같은 연계 기능을 활용할 때

푸시 알림의 기본 동작 흐름

  1. 앱이 APNS에 Device Token 요청
    • 이 토큰은 앱이 설치된 기기의 주소 역할을 한다.
  2. APNS가 앱에게 Device Token 전달
  3. 앱이 Device Token을 푸시 서버(FCM 등)에 전달
    • 이 서버가 실제 푸시 메시지를 보낼 주체이다.

APNS를 통한 푸시 알림 동작 구조

  1. 푸시 서버(예: FCM)가 메시지와 Device Token을 APNS에 전송
    • 이때 APNS와 푸시 서버는 TLS 인증으로 보안 통신
    • 서버에는 APNS 인증 정보(p8 키 or 인증서)가 등록되어 있어야 함
  2. APNS는 해당 디바이스로 푸시 메시지를 전송
  3. 디바이스에서 푸시 알림 수신

실습

이미지
identifiers에서 해당 프로젝트 누른 후 push notification 체크박스 체크 해준다.

이미지
keys에서 키를 발급 해준다. 완료하면 Download버튼이 생긴다 이를 통해 p8파일을 다운로드 한다.

이미지
firebase 프로젝트 -> 프로젝트 설정 -> 클라우드 메시징에서 APNS인증키를 등록해준다.

이미지
업로드를 눌러서 완료한다.

커스텀셀1 커스텀셀
Push Notifications Background Modes

xcode -> target -> Push Notifications, Background Mode 추가한다.

이미지
Remote Notifications 체크해준다.


AppDelegate.swift

import UIKit
import FirebaseCore
import FirebaseMessaging
import UserNotifications

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        // Firebase 초기화
        FirebaseApp.configure()
        
        // 알림 권한 요청
        UNUserNotificationCenter.current().delegate = self
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert, .badge, .sound]
        ) { granted, error in
            print("✅ 알림 권한 요청 결과: \(granted)")
            if granted {
                DispatchQueue.main.async {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
        }
        
        // FCM 설정
        Messaging.messaging().delegate = self
        
        return true
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    
    // APNs로부터 device token 받았을 때 FCM에 등록
    func application(_ application: UIApplication,
                     didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        print("📮 APNS Token 수신: \(deviceToken)")
        Messaging.messaging().apnsToken = deviceToken
    }

    // 앱이 실행 중일 때 알림 수신 (배너로 표시되게 설정)
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.list, .banner, .sound])
    }
}

extension AppDelegate: MessagingDelegate {
    
    // FCM 토큰 갱신될 때 호출
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        print("📌 FCM Token: \(fcmToken ?? "nil")")
        
        // 필요 시 NotificationCenter로 다른 컴포넌트에 전달
        NotificationCenter.default.post(
            name: Notification.Name("FCMToken"),
            object: nil,
            userInfo: ["token": fcmToken ?? ""]
        )
    }
}


  • 빌드하면 나오는 FCM을 복사한다.

FCM.py

import firebase_admin
from firebase_admin import credentials, messaging

# 1. 서비스 계정 키 경로 (.json)
cred = credentials.Certificate("파이어베이스 설정 -> 서비스 계정 -> 새 비공키 키 생성시 .json파일 다운 가능 -> 해당 경로 입력")
firebase_admin.initialize_app(cred)

# 2. 대상 유저의 FCM 토큰
fcm_token = "빌드하면 나오는 FCM 입력"

# 3. 전송할 메시지
title = "새 댓글이 달렸어요!"
body = "당신의 게시글에 누군가 댓글을 남겼습니다."

# 4. 알림 메시지 구성
message = messaging.Message(
    token=fcm_token,
    notification=messaging.Notification(
        title=title,
        body=body,
    )
)

# 5. 메시지 전송
try:
    response = messaging.send(message)
    print("✅ 메시지 전송 성공:", response)
except Exception as e:
    print("❌ 메시지 전송 실패:", e)
커스텀셀1 커스텀셀
앱이 종료되어있을 때 앱 실행중일 때

Reference

  • https://bannavi.tistory.com/558
  • https://dokit.tistory.com/49
  • https://dokit.tistory.com/50
  • https://velog.io/@niro/Firebase-Push-Notification-알림-보내기-with-SwiftUI
  • https://yagom.net/forums/topic/push-알림을-보낼-때-fcm-과-apns-의-차이점이-있나요/

Leave a comment