Notification Payload는 서버가 APNS를 통해 iOS 기기로 보내는 JSON 구조로, push 알림의 내용과 수신 시 동작을 정의합니다. 페이로드에는 텍스트, 소리, 배지, 미디어 첨부 및 백그라운드 처리를 제어하는 필수 및 선택 키가 포함됩니다. Apple Developer Documentation, 2026에 따르면, 일반 알림의 경우 최대 페이로드 크기는 4096 바이트, VoIP push의 경우 5120 바이트로, 전송되는 데이터에 엄격한 제한을 부과합니다.
주요 요점
Notification Payload는 서버가 APNS(Apple Push Notification Service)에 보내여 iOS 기기로 전달하는 JSON 객체입니다. 페이로드에는 시스템이 알림을 표시하는 데 필요한 모든 데이터가 포함됩니다: 제목, 텍스트, 소리, 배지 및 백그라운드 처리를 위한 메타데이터입니다. 페이로드 구조는 Apple이 엄격히 규제하며, 시스템이 올바르게 처리할 수 있도록 필수 키가 포함됩니다.
서버가 APNS HTTP/2 API를 통해 push 알림을 보낼 때, 요청에는 인증 헤더와 JSON 본문—페이로드가 포함됩니다. APNS는 페이로드를 검증합니다. JSON이 올바르지 않거나 크기 제한을 초과하면 Apple 서버가 400 Bad Request 오류를 반환합니다. 검증 후, APNS는 페이로드를 기기에 전달하고, iOS가 것을 분석하여 알림—배너 표시, 백그라운드 태스크 실행, 소리 재생—을 처리하는 방법을 결정합니다.
APNS 페이로드 형식은 iOS 2의 간단한 텍스트 페이로드에서 현대 버전의 다중 구성요소 JSON 구조로 진화했습니다. iOS 10은 mutable-content를 통한 미디어 첨부 지원을 도입하였고, iOS 12는 thread-id를 통한 알림 그룹핑을 추가했으며, iOS 15는 Live Activities를 위한 supports-live-activities를 도입했습니다. 오늘날 페이로드는 필요한 알림 동작에 따라 최대 15개의 다양한 키를 포함할 수 있습니다.
루트 객체에는 aps 딕션너리와 상위 레벨의 선택적 커스텀 필드가 포함됩니다. aps 딕션너리는 유일한 필수 요소이지만, 그 내부에는 알림 유형에 따라 alert, badge, sound, content-available, mutable-content, interruption-level 등 여러 키 조합이 있을 수 있습니다.
| aps 키 | 타입 | 목적 |
|---|---|---|
| alert | String 또는 Dictionary | 알림 텍스트 또는 title, subtitle, body 및 로컬라이지션을 포함한 객체 |
| badge | Number | 앱 아이콘의 숫자; 0은 배지 제거 |
| sound | String | 음성 파일 이름 또는 시스템 소리의 경우 default |
| content-available | Number (1) | 백그라운드 활성화 플래그; 1 = 사일럸트 push |
| mutable-content | Number (1) | 콘텐츠 수정을 위한 Service Extension 활성화 플래그 |
| category | String | 버튼 및 Content Extension의 카테고리 식별자 |
| thread-id | String | 알림 그룹핑을 위한 그룹 식별자 |
| interruption-level | String | 중단 레벨: passive, active, time-sensitive, critical |
| relevance-score | Number (0–1) | 스마트 랜킹 시스템의 알림 우선순위 |
alert 키는 간단한 문자열(알림 본문이 되는) 또는 title, subtitle, body 필드가 있는 딕션너리일 수 있습니다. 딕션너리 형식을 사용하면 주요 텍스트와 별도로 제목과 부제목을 설정할 수 있습니다. 로컬라이즈된 알림에는 title-loc-key, title-loc-args, loc-key 및 loc-args 키를 사용하며, 이들은 앱의 Localizable.strings를 참조합니다. 이를 통해 특정 언어의 텍스트 없이 페이로드를 보낼 수 있습니다—앱이 번역을 대체합니다.
iOS 15부터 Apple은 집중 모드(Focus Mode)를 도입하여, 개발자가 알림의 중단 레벨을 지정하도록 요구합니다. interruption-level은 passive(소리 없음, 화면 안 밝음), active(표준 동작), time-sensitive(집중 모드 봉깍, 특별 권한 필요) 및 critical(의료/비상 상황) 값을 받습니다. relevance-score(0–1) 키는 Focus 시스템이 동일 카테고리 내에서 알림을 순위 매기는 데 도움을 주방습니다.
thread-id 키는 알림 센터에서 알림을 그룹화합니다. 동일한 thread-id를 가진 모든 알림이 사용자가 펼칠 수 있는 하나의 그룹으로 표시됩니다. 이것은 때싰글에서 동일한 연락처의 메시지가 그룹화되는 경우나, 동일 유형의 다양한 알림을 보내는 앱에 특히 유용합니다.
커스텀 필드는 aps 딕션너리 밖의 키로, 개발자가 기기에 추가 데이터를 전달하기 위해 추가합니다. 서버는 이들을 페이로드의 루트 JSON 객체에 포함하고, 앱은 UNNotificationContent의 userInfo를 통해 검색합니다. 커스텀 필드는 분석 충돌을 피하기 위해 aps와 키 이름이 중복되어서는 안 됩니다.
주요 제한은 총 페이로드 크기가 4096 바이트를 초과하지 않아야 한다는 것입니다. 커스텀 필드 는 필수 aps 키와 이 제한을 경합하므로, 전송되는 데이터의 크기를 최소화하는 것이 중요합니다. 빠른 키 이름을 사용하고(예: “uid” 대신 “user-id” 대신), 큰 JSON 구조를 피하며, 완전한 데이터 객체 대신 식별자만 전송합니다.
커스텀 필드는 서버로부터 오며, 확인 없이 신뢰해서는 안 됩니다. 파싱 시 항상 타입과 값을 검증하세요: 옵션밌 바인딩으로 키 존재 여부를 확인하고, as? String/Int/Dictionary로 기대되는 타입으로 변환하며, 값이 없는 경우를 처리합니다. 페이로드의 데이터에 대해 force unwrap(!)을 절대 사용하지 마세요—서버가 유효하지 않은 데이터를 보낼 수 있어 앱이 그리스 날 위험이 있습니다.
{
"aps": {
"alert": {
"title": "새 메시지",
"body": "안녕하세요! 어떤 신경이세요?"
},
"badge": 5,
"sound": "default",
"category": "message",
"thread-id": "chat_4521",
"mutable-content": 1
},
"sender-id": "user_789",
"chat-id": "chat_4521",
"message-type": "text",
"image-url": "https://cdn.example.com/img.jpg"
}
프로젝트의 모든 페이로드에서 커스텀 필드에 일관된 명명 스타일을 사용하세요. kebab-case(message-type) 또는 camelCase(messageType) — 둘 다 허용되지만 프로젝트 내에서의 일관성이 중요합니다. 긴 이름을 피하세요: “uid” 대신 “user-identifier” 대신, “img” 대신 “profile-image-url” 대신입니다. 키 이름의 각 문자는 4096 제한의 1 바이트를 소비합니다.
다양한 시나리오의 push 알림에는 페이로드에서 다양한 키 조합이 필요합니다. 몇 가지 전형적인 예를 보겠습니다: 간단한 텍스트 알림, 로컬라이즈된 알림, 사일럸트 push 및 미디어 첨부가 있는 리치 알림입니다.
텍스트와 소리가 있는 기본 페이로드—사용자에게 알림을 표시하는 데 필요한 최소 구성입니다. 문자열로서의 alert는 빠른 메시지를 제공하고, sound default는 표준 시스템 소리를 재생합니다. Badge는 선택적이며 앱 아이콘에 카운터를 설정합니다. category와 thread-id는 그룹핑과 대화형을 위해 추가됩니다.
{
"aps": {
"alert": "리마인더: 15분 후 회의",
"badge": 3,
"sound": "default"
}
}
다양한 언어의 기기로 알림을 보내려면, 하드코딩된 텍스트 대신 로컬라이지션 키를 사용하세요. title-loc-key는 앱의 Localizable.strings의 키를 참조하고, title-loc-args는 인수를 제공합니다. 이를 통해 모든 기기에 단일 페이로드를 보낼 수 있고, 앱이 적합한 언어로 텍스트를 표시합니다.
{
"aps": {
"alert": {
"title-loc-key": "NEW_MESSAGE_TITLE",
"title-loc-args": ["안나"],
"loc-key": "NEW_MESSAGE_BODY",
"loc-args": ["안녕하세요!"]
},
"sound": "message.caf"
}
}
알림을 표시하지 않고 백그라운드 동기화를 하려면 alert 없이 content-available: 1을 사용하세요. 커스텀 필드는 처리를 위한 작업 유형과 데이터를 지정합니다. 시스템은 백그라운드에서 앱을 활성화하고, fetchCompletionHandler로 didReceiveRemoteNotification을 호출하며, 앱이 동기화를 수행합니다.
{
"aps": {
"content-available": 1
},
"sync-type": "invalidate-cache",
"timestamp": "2026-07-03T12:00:00Z"
}
미디어 첨부를 표시하려면 Service Extension을 활성화하는 mutable-content: 1과 커스텀 필드의 이미지 URL이 필요합니다. mutable-content: 1은 UNNotificationServiceExtension을 시작하도록 시스템에 신호를 보내서 URL에서 이미지를 다운로드하고 UNNotificationAttachment로 추가합니다. category 키는 작업 버튼을 표시하기 위한 등록된 카테고리를 지정합니다.
{
"aps": {
"alert": {
"title": "신제품",
"body": "새 컬렉션을 확인하세요"
},
"category": "product",
"mutable-content": 1
},
"media-url": "https://cdn.example.com/product.jpg"
}
UNNotificationContent.userInfo에는 시스템 처리 후 수신된 페이로드의 완전한 딕션너리가 포함됩니다. 앱은 UNUserNotificationCenter 데리게이트에서 알림 수신 시(포그라운드), 알림 탭 시, 그리고 Service Extension 및 Content Extension에서 페이로드에 액세스합니다. 커스텀 데이터를 추출하고 다음 작업을 결정하려면 올바른 파싱이 필수적입니다.
사용자가 알림을 탭하면, 시스템은 UNUserNotificationCenterDelegate의 didReceive response 메소드를 호출합니다. response.notification.request.content.userInfo에 완전한 페이로드가 포함됩니다. 개발자는 커스텀 필드를 추출하고, 작업 유형(예: 채팅 열기, 제품으로 이동)을 결정하며, 앱에서 적합한 네비게이션을 트리거합니다.
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification
.request.content.userInfo
guard let chatId = userInfo["chat-id"] as? String
else {
completionHandler()
return
}
let messageType = userInfo["message-type"]
as? String ?? "text"
NavigationRouter.shared.navigate(
to: .chat(chatId: chatId,
messageType: messageType))
completionHandler()
}
Service Extension은 알림이 표시되기 전에 페이로드를 수신하고 수정할 수 있습니다. 페이로드 검증은 didReceive에서 첫 단계입니다: 필수 커스텀 필드를 확인하고, 첨부 URL을 검증하고, 데이터 타입을 확인합니다. 페이로드가 유효하지 않으면, 필요 없는 처리에 시간을 낭비하지 않도록 원본 콘텐츠로 즉시 completion handler를 호출합니다.
프로덕션에서 push 알림을 디버그하려면 구조화된 페이로드 로깅을 사용하세요. OSLog를 사용하면 debug 레벨에서 “notifications” 카테고리로 페이로드를 로깅할 수 있습니다. 서버 측에서 APNS 응답을 모니터링하세요: 성공 응답에는 보내진 페이로드와 매칭하기 위한 apns-id가 있고, 400 오류는 잘못된 JSON 또는 크기 초과를 나타냅니다.
자주 묻는 질문
최대 페이로드 크기는 일반 push 알림의 경우 4096 바이트, VoIP push(PushKit)의 경우 5120 바이트입니다. 이 제한를 초과하면 APNS가 400 Bad Request 오류를 반환합니다. 크기는 문자가 아닌 바이트로 계산됩니다—UTF-8 인코딩을 고려하세요.
alert 내에서 loc-key, title-loc-key, loc-args 및 title-loc-args 키를 사용하세요. 앱이 기기 언어에 따라 Localizable.strings에서 번역을 대체합니다. 이를 통해 언어에 관계없이 모든 기기에 단일 페이로드를 보낼 수 있습니다.
content-available은 알림을 표시하지 않고 데이터 처리(사일럸트 push)를 위해 백그라운드에서 앱을 활성화합니다. mutable-content는 표시 전에 콘텐츠를 수정하기 위한 Service Extension을 활성화합니다. 두 키를 함께 사용하여 백그라운드 처리 및 이후 알림 수정을 할 수 있습니다.
테스트를 위해 APNS Sandbox를 사용하고 Apple 서버의 HTTP 응답을 확인하세요: 200 OK는 성공적인 전달을 의미합니다. 구조 검증을 위해 CI/CD 파이프라인에서 JSON 스키마를 사용하세요. Xcode에서 xcrun simctl push를 사용하여 시뮬레이터를 통해 테스트 알림을 보냄니다.
apns-id는 APNS 시스템에서 push 알림의 고유 식별자로, 성공적인 전달 시 응답에서 반환됩니다. Logs API를 통한 전달 추적 및 디버그에 사용됩니다. 서버는 보내진 각 알림에 대해 apns-id를 저장해야 합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.