진행중인 프로젝트에서 알림 기능을 구현할 일이 있어서 이번 글에서는 알림 기능 방법 중 하나인 로컬 알림(Local Notification)을 구현하는 방법에 대해 알아볼 예정입니다. 그전에 먼저 알림 기능 방법인 로컬 알림과 푸시 알림에 대해 알아보겠습니다.
로컬 알림은 앱 내부에서 사용자에게 보내는 알림입니다. 특정 이벤트나 일정 시간 후에 보낼 수 있으며, 위치를 기반으로 특정 장소에 도착했을 때도 보낼 수 있습니다. 네트워크 연결 없이 기기 내에서 동작하므로 푸시 토큰이 필요 없으며, 빠르게 설정하고 사용할 수 있습니다. iOS 시스템에서 직접 알림을 처리하기 때문에 배터리 소모가 적은 편이며 백그라운드 또는 종료된 상태에서도 알림을 표시할 수 있습니다.
앱에서 보내다 보니 미리 예약해둔 시간이 아닌 실시간 알림 전송은 불가능합니다.
푸시 알림은 서버에서 사용자의 기기로 원격으로 보내는 알림입니다. iOS에서는 APNs (Apple Push Notification Service)를 통해 동작하며, 로컬 알림과 마찬가지로 앱이 백그라운드 또는 종료된 상태에서도 사용자에게 메시지 전송 가능합니다. 로컬 알림과 다른 점으로는 네트워크 연결이 필요하며, 보통 Firebase Cloud Messaging(FCM) 또는 자체 서버를 통해 관리됩니다.
서버에서 사용자의 기기로 알림을 보내기 위해서는 푸시 토큰이 필요한데 사용자 기기가 초기화되거나 앱을 재설치하면 새로운 푸시 토큰을 받아야 합니다. 또한 서버를 사용하다보니 인터넷 연결이 끊긴 경우 알림을 받을 수 없으며, 푸시 트래픽이 많거나 서버가 지연되면 알림이 늦게 도착할 수 있습니다.
둘의 특징을 정리하면 다음과 같습니다.
| 항목 | 로컬 알림 (Local Notification) | 푸시 알림 (Push Notification) |
|---|---|---|
| 전송 지연 시간 | ✅ 즉시 실행 (ms 단위) | ⚠️ 서버 & 네트워크 지연 발생 (수 초 ~ 수 분) |
| CPU & 메모리 사용 | ✅ 앱 내부에서 실행 → 영향 거의 없음 | ⚠️ 백그라운드에서 실행되며, 시스템 리소스 사용 |
| 네트워크 사용량 | ❌ 네트워크 불필요 | ⚠️ 서버 요청 및 데이터 패킷 사용 |
| 배터리 영향 | ✅ 낮음 (기기 내부 실행) | ⚠️ 높음 (APNs & 네트워크 필요) |
| 알림 트리거 방식 | ⏳ 타이머, 위치, 이벤트 기반 | 🌍 서버에서 원격 트리거 |
| 백그라운드 동작 | ✅ 가능 | ✅ 가능 (하지만 서버 연결 필요) |
| 실시간 알림 가능 | ❌ 예약된 시간에만 가능 | ✅ 가능 (예: 채팅, 이메일) |
| 사용자별 맞춤 메시지 가능 여부 | ❌ 불가능 (모든 사용자 동일) | ✅ 가능 (서버에서 개별 처리) |
위 특징들을 고려했을 때 상황별 추천 알림 방식은 다음과 같습니다.
| 사용 사례 | 추천 알림 방식 | 설명 |
|---|---|---|
| ⏳ 일정 & 타이머 | ✅ 로컬 알림 | 네트워크 없이 즉시 알림 표시 |
| 📍 위치 기반 알림 | ✅ 로컬 알림 | 특정 지역 도착 시 알림 (ex. 리마인더) |
| 🎮 앱 내부 이벤트 (게임, 목표 도달 등) | ✅ 로컬 알림 | 앱에서 바로 트리거 가능 |
| 📩 새로운 메시지 수신 (채팅, 이메일 등) | ✅ 푸시 알림 | 서버에서 변경 사항을 전달해야 함 |
| 🏦 거래 내역, 금융 알림 | ✅ 푸시 알림 | 서버에서 유저 상태를 업데이트해야 함 |
| 📢 광고 & 마케팅 알림 | ✅ 푸시 알림 | 서버에서 유저 타겟팅 가능 |
제가 하는 프로젝트는 특정 시간이 되면 정해진 형식의 알림을 보내면 되기 때문에 로컬 알림을 사용하기로 했습니다. iOS에서는 로컬 알림 및 푸시 알림을 처리하기 위해 UserNotification라는 프레임워크를 제공해줘서 이를 사용해 볼 예정입니다.

UserNotification 공식문서에서 다음과 같이 정의되어 있으며, 서버에서 사용자 기기로 사용자에게 표시되는 알림을 푸시하거나 앱에서 로컬로 알림을 생성합니다.
UserNotification에서 사용하는 주요 객체는 다음과 같습니다.
알림에 필요한 메세지와 같은 속성을 담는 콘텐츠 역할로 제목, 내용, 뱃지, 사운드 설정등을 담당합니다.
알림 발송 조건(트리거)을 담당하며 알림 등록으로부터 몇 초 뒤에 알림을 보낼 지 설정합니다.
UNTimeIntervalNotificationTrigger과 마찬가지로 알림 발송 조건(트리거)을 담당하며 알림을 보내고자하는 특정 시간을 지정할 때 사용합니다.
앞서 말한 콘텐츠와 트리거를 전달하기 위한 알림 요청 객체입니다.
알림 권한 요청, 알림 스케줄링, 알림 수신 처리, 배지(Badge) 관리 등을 담당합니다.
delegate 등록
포그라운드 알림을 표시하기 위해 delegate를 등록해줘야 합니다.
알림을 보내기 위해서는 권한을 요청해야합니다.
UNUserNotifiactionCenter.requestAuthorization 메서드를 사용해 유저에게 권한을 요청합니다.
로컬 알림을 등록합니다.
UNMutableNotificationContent을 생성한 후 원하는 제목과 내용을 각각 title과 body에 주입해주고 trigger와 함께 UNNotificationRequest를 생성해 등록해줍니다.
예제 코드는 UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval, repeats: Bool)를 사용해서 secondsLater초만큼 시간이 지난 후에 알림이 오도록 트리거를 구현했습니다.
만약 원하는 시간에 알림이 오도록 구현하고 싶다면 UNCalendarNotificationTrigger(dateMatching: DateCompoents, repeats: Bool) 메서드를 사용하면 원하는 시간에 알림이 오도록 트리거를 구현할 수 있습니다.
// NotificationManager.Swift
import UserNotifications
class NotificationManager: NSObject, UNUserNotificationCenterDelegate {
static let shared = NotificationManager()
private override init() {}
func requestPermission() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if let error = error {
print("❌ 알림 권한 요청 실패: \(error)")
} else {
print("✅ 알림 권한 승인 여부: \(granted)")
}
}
}
func addNotification(title: String, body: String, secondsLater: TimeInterval) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: secondsLater, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("❌ 알림 추가 실패: \(error)")
} else {
print("📢 알림이 \(secondsLater)초 후에 예약됨")
}
}
}
func removeAllNotifications() {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
print("🔔 모든 예약된 알림이 삭제됨")
}
// 포그라운드에서도 알림 표시 (필수)
func registerDelegate() {
UNUserNotificationCenter.current().delegate = self
}
// 포그라운드에서도 알림이 보이도록 설정
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .sound])
}
}
// ContentView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Button("🔔 알림 권한 요청") {
NotificationManager.shared.requestPermission()
}
.padding()
Button("📢 2초 후 알림 보내기") {
NotificationManager.shared.addNotification(title: "테스트 알림", body: "2초 후 도착한 알림입니다.", secondsLater: 2)
}
.padding()
Button("❌ 모든 알림 삭제") {
NotificationManager.shared.removeAllNotifications()
}
.padding()
}
.onAppear {
NotificationManager.shared.registerDelegate()
}
}
}
위처럼 모든 알림을 삭제하지 않고 원하는 알림만 삭제하고 싶을 때는 UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [])를 사용해서 알람을 등록할 때 사용한 identifier에 해당하는 알림을 제거할 수 있습니다.
실행화면을 마지막으로 글을 마치겠습니다.
