[ iOS ] 로컬 PUSH 와 APNS 푸시 차이점 완벽 정리
- Coding/스위프트 iOS
- 2021. 3. 28. 10:41
아이폰 개발 시 서버에서 발송되는 APNS는 대체적으로 거의 모든 상용 앱에 사용됩니다. 하지만 로컬 푸시는 어떻게 작동되는지 잘 모르고 있다가 이번에 개인 프로젝트를 만들면서 완전히 애플의 푸시 서비스를 이해할 수 있게 되어서 정리합니다. 아마 iOS 개발을 오래 하신 분들도 잘 이해하지 못하고 반사적으로 사용하는 경우가 많을 것 같습니다. 이번에 HTTP/2 서버 변경 이슈와 로컬 노티피케이션 덕분에 푸시에 대해서 완전히 이해하게 되었습니다.
애플의 푸시서비스
우선 애플의 푸시 서비스는 두 개로 분리됩니다.
- 서버에서 보내는 APNS 푸시
- 로컬에서 개발자가 직접(?) 보내는 푸시
이 밖에도 FCM이라는 파이어 베이스 기반의 푸시 서비스도 있지만 결국에는 구글이 APNS를 발송하는 거라서 별다른 의미는 없습니다.
그리고 APNS 푸시 서비스를 사용하기 위해서는 두 가지 인증서가 있습니다.
- p12
- p8
이 인증서 부분은 끝 부분에 설명드리고 로컬 푸시에 대해서 정리를 하고 APNS를 이해하도록 하겠습니다.
로컬 노티피케이션
우선 푸시서비스를 만들기 위해서는 다음과 같은 코드가 불려야 합니다. 주로 appDelegate의 didFinishLaunching에서 호출하는데 이 부분을 뒤로 미룰 수 있습니다. 대부분의 앱들을 보면 앱이 시작하자마자 알림 허용 창이 뜨지만 알림 창이 인트로가 끝나고 특정 시점에 호출되게 할 수도 있습니다. 단 applicaion 객체를 알고 있어야 합니다. 그 이유는 디바이스 토큰을 얻기 위해서 인데 해당 토큰을 얻으려면 applicaion 객체의 메서드인 registerUserNotificationSettings를 호출해야 합니다. 이 값은 앱을 지우기 전에는 바뀌지 않습니다.
configure(application: application, delegate: self)
public class func configure(application: UIApplication, delegate: UNUserNotificationCenterDelegate?) {
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = delegate
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: { (granted, error) in
if granted {
print("사용자가 푸시를 허용했습니다")
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
} else {
print("사용자가 푸시를 거절했습니다")
}
})
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
}
중요한 사실은 노컬 푸시의 경우 이 값이 중요하지 않습니다. 특정 타깃에 푸시를 해야 할 이유가 없기 때문이죠. 자기 자신만 불리면 되기 때문에 토큰이 필요하지 않습니다.
또한 iOS 10 이상부터는 권한에 대한 핸들러가 포함되어 있어서 사용자가 푸시를 허용했는지, 거절했는지를 알 수 있습니다. 분기를 하면 좋지만 요즘 앱들은 거의 iOS 10 이상에서 실행되기 때문에 이 역시 의미가 없습니다.
정리하자면 디바이스 토큰은 APNS에만 필요하기 때문에 application의 registerUserNotificationSettings 메서드를 호출할 이유가 없다.입니다.
// 디바이스 토큰
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = MyLib().getDeviceTokenString(deviceToken: deviceToken)
addToken(deviceTokenString: deviceTokenString)
}
하지만 로컬 및 APNS 푸시 델리게이터 설정은 반드시 해줘야 합니다. 푸시 후에 전달되는 userInfo 값을 받을 곳은 필요하니까요. 보통 AppDelegate 전역에 설정합니다.
UNUserNotificationCenter.current().delegate = delegate
이제 푸시 서비스가 설정되었으면 노컬 푸시를 보내보겠습니다. 아래의 코드를 순서대로 보면 총 4단계로 이루어졌습니다.
- 컨텐츠 객체를 설정합니다. userInfo는 푸시를 눌러서 들어왔을 때의 값을 의미합니다.
- 트리거는 시간, 캘린더, 위치서비스의 총 3개의 타입으로 생성이 가능합니다. UITimerIntervalNotificationTriger의 경우 0초는 에러가 납니다 최소 0.1 초로 발송해야 합니다.
- 요청을 만들 때는 위의 두 개의 객체를 포함하고 추가적으로 id를 생성합니다. 푸시는 추후에 취소될 가능성이 있다면 이 값이 중요합니다. 해당 푸시를 제거하기 위해서 필요합니다.
- 마지막으로 요청을 추가합니다. 클로저에 실패에 대한 에러 처리를 추가할 수 있네요.
var x = 1
// 컨텐츠 생성
let content = UNMutableNotificationContent()
content.title = "제목"
content.body = "푸시 \(x)"
content.badge = NSNumber(value: x)
let soundName = UNNotificationSoundName("test.aiff")
content.sound = UNNotificationSound(named: soundName)
content.userInfo = ["ok":"you"]
// 트리거 생성
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: seconds, repeats: false)
// UNLocationNotificationTrigger
// UNCalendarNotificationTrigger
let pushID = "notification \(x)"
// 요청 생성
let request = UNNotificationRequest(identifier: pushID,
content: content,
trigger: trigger)
// 요청 추가
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Notification Error: ", error)
}
}
APNS 서비스
지금까지 로컬 푸시에 대해서 알아봤습니다. APNS의 가장 큰 차이점이라면 토큰 값이 있어야 한다는 점입니다. 위에서 설명했듯이 application의 registerUserNotificationSettings 메서드를 호출하면 앱의 토큰 값을 알 수 있습니다. 그리고 서버로 이 값을 전달해서 서버 측에서 해당 사용자의 토큰을 관리합니다. 만약 FCM(Firebase Cloud Message) 서비스를 사용한다면 이 토큰 값을 구글로 보내게 되고 구글에서 반환하는 키를 기준으로 안드로이드와 애플에 동시에 푸시를 보낼 수 있습니다.
또한 로컬 푸시는 인증서를 발급받을 필요가 없지만 APNS는 인증서를 발급받아야 한다는 것입니다. 그리고 한 가지 더 다른 게 있습니다. 설정 파일에서 Capability를 추가해줘야 한다는 겁니다. 그럼 Entitlement 파일이 생성되고 앱 단에서는 APNS 노티피케이션 서비스의 모든 준비가 끝납니다.
서버에서는 푸시 발송을 위해서 인증서와 팀 아이디, 앱 아이디, 번들 아이디의 정보를 가지고 해당 단말을 타겟으로 푸시를 보냅니다. 이 모든 처리는 애플의 APNS 서버가 담당하며 최근에 HTTP/2 이슈로 이전에 사용하던 서버 IP를 더 이상 지원하지 않는다고 합니다. 그런데 제가 개발하는 서비스 중에는 아직도 p12인증서를 사용하는 앱이 있는데 다행히 ssl 에 대한 처리는 계속 지원한다고 합니다. p12와 p8의 차이점은 1년마다 갱신하는 게 p12 영구적인 게 p8인증서입니다. 애플 APNS는 워낙에 정보가 많으니 이 정도로 정리합니다. 이제 로컬 푸시와 헷갈리지 않고 사용할 수 있겠네요. 저는 처음에 로컬 푸시 권한은 애플 APNS 푸시와 완전 별 게인 줄 알았는데 그게 아니더군요.
'Coding > 스위프트 iOS' 카테고리의 다른 글
[ 아이폰 개발 ] iOS 13 앱 다크모드 무시하기 설정 (0) | 2021.04.17 |
---|---|
[ iOS ] 앱 스토어 개인, 회사, 엔터프라이즈 차이점 (0) | 2021.04.15 |
[ iOS ] Swift 애플 로그인 처리 및 정책 리젝 사유 (0) | 2021.03.19 |
[ Xcode ] CocoaPods 설치 에러 수정 xcrun: error: invalid active developer path (0) | 2021.03.18 |
[ 스위프트 iOS ] Swift Package Manager (SPM) 사용하기 (0) | 2021.02.06 |