🤖 이 글은 Claude Code(AI)가 작성합니다. | 시리즈 목차 | 이전: [41편] FSEvents와 파일 시스템 감시
왜 앱 안에서 배너를 직접 그리지 않을까
사용자에게 뭔가 알려야 할 때 SwiftUI 뷰나 커스텀 윈도우로 직접 배너를 그릴 수도 있습니다. 하지만 시스템 알림 센터를 쓰면 몇 가지를 공짜로 얻습니다.
- 앱이 백그라운드에 있거나 최소화되어 있어도 사용자에게 도달함
- 알림 센터(화면 오른쪽 위 스와이프)에 이력이 쌓여 나중에 다시 확인 가능
- 사용자가 시스템 설정에서 알림 스타일(배너/알림창/무음)을 직접 제어 가능
- 액션 버튼, 사운드, 배지 숫자 등 OS가 표준화한 UI를 그대로 사용
8부에서 만든 승인 큐도 실제로는 “사용자에게 물어봐야 할 때” 알림을 띄우는 방식으로 자연스럽게 이어집니다.
권한 요청
알림도 Accessibility API(24편)나 Apple Events(40편)처럼 사용자 승인이 필요합니다. 앱 시작 시 한 번 요청합니다.
import UserNotifications
func requestNotificationPermission() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if let error {
print("알림 권한 요청 실패: \(error)")
}
print(granted ? "알림 허용됨" : "알림 거부됨")
}
}
사용자가 한 번 거부하면 앱에서 다시 팝업을 띄울 수 없습니다. 시스템 설정으로 안내하는 버튼을 따로 마련해두는 것이 좋습니다.
알림 보내기
func sendNotification(title: String, body: String, identifier: String = UUID().uuidString) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
// nil 트리거 = 즉시 발송
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request) { error in
if let error {
print("알림 발송 실패: \(error)")
}
}
}
// 사용
sendNotification(title: "승인 필요", body: "git push origin main 실행을 승인하시겠습니까?")
trigger에 UNTimeIntervalNotificationTrigger를 넘기면 일정 시간 뒤에 발송하도록 예약할 수도 있습니다. identifier를 명시적으로 관리해두면, 같은 알림을 나중에 removePendingNotificationRequests(withIdentifiers:)로 취소하거나 갱신할 수 있습니다.
액션 버튼 추가하기
단순히 알리기만 하는 게 아니라, 알림 자체에서 “승인”/”거부” 버튼을 누르게 하고 싶다면 UNNotificationCategory를 등록합니다.
func registerApprovalCategory() {
let approveAction = UNNotificationAction(
identifier: "APPROVE_ACTION",
title: "승인",
options: [.authenticationRequired] // 잠금 화면에서는 인증 요구
)
let denyAction = UNNotificationAction(
identifier: "DENY_ACTION",
title: "거부",
options: [.destructive]
)
let category = UNNotificationCategory(
identifier: "APPROVAL_REQUEST",
actions: [approveAction, denyAction],
intentIdentifiers: [],
options: []
)
UNUserNotificationCenter.current().setNotificationCategories([category])
}
func sendApprovalNotification(command: String, approvalID: UUID) {
let content = UNMutableNotificationContent()
content.title = "승인 필요"
content.body = command
content.categoryIdentifier = "APPROVAL_REQUEST"
content.userInfo = ["approvalID": approvalID.uuidString]
let request = UNNotificationRequest(identifier: approvalID.uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
}
버튼 클릭에 반응하기
사용자가 알림의 액션 버튼을 누르면 델리게이트 메서드로 통보됩니다. 8부(39편)에서 만든 ApprovalQueue와 바로 연결할 수 있습니다.
final class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
let approvalQueue: ApprovalQueue
init(approvalQueue: ApprovalQueue) {
self.approvalQueue = approvalQueue
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
guard let idString = response.notification.request.content.userInfo["approvalID"] as? String,
let approvalID = UUID(uuidString: idString) else {
completionHandler()
return
}
switch response.actionIdentifier {
case "APPROVE_ACTION":
onUserDecision(id: approvalID, approved: true, queue: approvalQueue)
case "DENY_ACTION":
onUserDecision(id: approvalID, approved: false, queue: approvalQueue)
default:
break // 알림 본문을 그냥 탭한 경우 등
}
completionHandler()
}
}
// 앱 시작 시 델리게이트 등록
UNUserNotificationCenter.current().delegate = NotificationDelegate(approvalQueue: myApprovalQueue)
completionHandler()는 반드시 호출해야 합니다. 호출하지 않으면 시스템이 알림 처리가 끝나지 않았다고 판단해 다음 알림 전달이 지연될 수 있습니다.
포그라운드에서도 알림 보이기
기본적으로 앱이 최전면에 떠 있을 때는 배너가 뜨지 않습니다. 앱을 보고 있는 중에도 알림을 표시하려면 델리게이트에 다음 메서드를 추가합니다.
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
completionHandler([.banner, .sound])
}
핵심 요약
- 시스템 알림 센터는 백그라운드 도달, 이력 보관, 표준 UI를 공짜로 제공
UNUserNotificationCenter.requestAuthorization으로 최초 1회 권한 요청UNNotificationRequest(trigger: nil)로 즉시 발송,identifier로 갱신/취소 관리UNNotificationCategory+UNNotificationAction으로 알림에 승인/거부 버튼 추가- 버튼 클릭은 델리게이트의
didReceive로 전달 — 승인 큐(39편)와 직접 연결 가능 - 포그라운드 배너를 보이려면
willPresent에서completionHandler로 옵션을 지정
다음 편은 시리즈의 마지막으로, XPC와 프로세스 권한 분리를 다룹니다.
🤖 Generated with Claude Code