생각 정리 - iOS 메모리 관리, UNUserNotificationCenter

Marble·2025년 3월 11일

생각 정리

목록 보기
2/4

이번에 앞서 공부한 Local Notification을 적용하면서 통화 상태나 iOS에 메모리 과부하를 줘서 앱이 강제 종료된 상태 등 정상적이지 않은 상태에서도 알림이 오는지 테스트를 해봤어요

통화 상태에서는 알림이 잘 왔고 추가로 과부하를 테스트하기 위해서 다음과 같이 데이터를 추가하며 앱에 과부하를 줬습니다.

Button {
    for _ in 0..<100 {
        let bigData = Data(count: 10_000_000) // 10MB 할당
        data.append(bigData)
    }
} label: {
    Text("과부하 걸기")
}

테스트 결과는 앱을 백그라운드로 보낸 후 종료(스와이프)했을 때도 잘 왔지만 알림을 추가하고 과부하를 걸어서 iOS가 강제종료 하도록 했을 때는 알림이 안 왔어요. 스와이프를 해서 앱을 종료한 경우와 iOS가 강제종료 했을 때 뭐가 다른지 궁금해서 찾아봤어요.

iOS의 앱 라이프 사이클은 아래와 같아요

앱 상태주요 메서드설명실행 타이밍
앱 시작 (Cold Start)application(_:didFinishLaunchingWithOptions:)앱이 처음 실행될 때 호출됨 (초기 설정, DB 로딩 등)앱 실행시 1회
Active (실행 중)sceneDidBecomeActive(_:)앱이 포그라운드에서 활성화됨앱 실행 or 백그라운드 → 포그라운드 이동
Inactive (일시 정지 상태)sceneWillResignActive(_:)전화, 알림 등으로 앱이 일시적으로 중단됨전화 수신, 알림 센터 열기 등
Background (백그라운드 실행)applicationDidEnterBackground(_:)홈 버튼 or 제스처로 앱이 백그라운드로 감백그라운드 진입 시
Foreground (다시 활성화)sceneWillEnterForeground(_:)백그라운드 → 포그라운드로 돌아올 때 호출됨앱 다시 실행 시
앱 종료applicationWillTerminate(_:)앱이 종료되기 직전에 호출됨사용자가 앱을 스와이프 종료할 때

사용자가 스와이프를 통해 앱을 종료한 경우 Foreground - Background - 앱 종료 이 순으로 상태가 변하죠. 하지만 메모리 부족으로 인해 사용중이던 앱을 iOS가 강제 종료 하게 되면 Foreground - 앱 종료 순으로 상태가 변해요. 이때 차이점은 또 있는데 사용자가 스와이프를 통해 앱을 종료하는 경우 applicationWillTerminate(_ :) 함수가 호출 되지만 iOS가 앱을 강제 종료하는 경우는 호출이 안됩니다.

하지만 applicationWillTerminate(_ :) 함수의 호출 유무 때문에 알림이 오고 안 오는건 아닌거 같아서 좀 더 찾아봤고 그 이유는 다음과 같았습니다.

iOS가 앱을 강제 종료할 때, 앱과 관련된 일부 스케줄링 데이터(알림, 백그라운드 작업 등)도 정리될 가능성이 있다고 합니다. 이는 iOS의 메모리 관리 정책 때문인데요 iOS는 메모리가 부족할 때 LRU (Least Recently Used) 방식으로 앱을 종료하고 관련 데이터를 정리한다고 합니다. 즉, 실행 중이던 앱(가장 최근 사용한 앱)을 종료하며 관련 데이터들이 정리된거죠.

여기서 새로운 의문이 생겼는데 UNUserNotificationCenter를 사용해 등록한 알림은 iOS 시스템에 등록하기 때문에 앱이 종료되며 관련 데이터가 종료되도 알림은 와야 한다는 거죠.

UNUserNotificationCenter.current().add(request) { error in
    if let error = error {
        print("❌ 알림 추가 실패: \(error)")
    } else {
        print("📢 알림이 \(secondsLater)초 후에 예약됨!")
    }
}

위 함수를 통해 알림을 등록했는데 알림이 추가 됐다는 문구가 출력되면 등록이 됐으니 출력한거잖아요? 근데 앱을 종료하며 데이터들이 정리된다해서 등록된 문구까지 날라가는건 이상하다 생각했죠. 그래서 알림을 등록하고 과부하를 주는 시간을 바꿔가며 테스트 해봤어요.

결과는 알림을 등록하고 몇 초 지나지 않았을 때 과부하를 주면 알림이 오지 않았고, 충분한 시간이 지난 후에 과부하를 주면 알림이 왔습니다. 이는 iOS에서 로컬 알림을 관리하는 Notification Daemon(backboardd 프로세스)이 알림을 처리하는 방식 때문에 생긴 일이었는데 위 메서드를 통해 알림이 추가됐다는 문구가 출력되도 실제로는 알림이 등록되지 않았을 수도 있습니다.

iOS에서 알림이 등록되는 과정은 다음과 같아요
1️⃣ UNUserNotificationCenter가 알림을 iOS 시스템(Backboardd)으로 전달
2️⃣ iOS 시스템이 내부 DB에 알림을 스케줄링 (보통 수 초 ~ 수십 초 내 처리)

위 두 과정을 거친 후에 DB에 알림이 등록되는 데, 알림을 추가하고 얼마 지나지 않은 시점에서 과부하를 줬을 때 알림이 오지 않은 이유가 실제로 내부 DB에는 아직 스케줄링이 되지 않은 상태인데 iOS가 관련 데이터들을 삭제하며 일어난 일이었죠

그러면 알림을 등록한 직후 스와이프로 강제 종료했을 때도 DB에 스케줄링 되기 전이니 알림이 등록되지 안 될까요? 이때는 등록이 됩니다. 위에서 말했듯이 알림을 등록하는 과정은 다음과 같죠

  1. 앱이 UNUserNotificationCenter.add(request) 호출
    1-1. 알림 등록 요청을 Notification Daemon(backboardd)에 전달
    1-2. 이때, 알림이 메모리에 먼저 저장됨 (즉시 반영)
  2. iOS가 알림을 백그라운드 시스템 DB에 등록 (비동기 처리)
    2-1. backboardd가 일정 시간이 지나면 알림을 영구 저장
  3. 앱이 종료되어도 iOS 시스템이 알림을 관리
    3-1. 알림이 메모리에 반영된 후 앱이 종료되더라도, 알림은 사라지지 않음

메모리 부족으로 인한 iOS의 강제 종료의 경우 백그라운드에 있던 관련 리소스들도 지우기 때문에 2-1가 실행되기 전이면 알림이 지워질 수 있지만 스와이프 종료의 경우는 백그라운드 데이터들은 지우지 않기 때문에 앱을 종료시켜도 알림이 등록되는 것입니다

다음에는 처음 들어본 Notification Daemon(backboardd 프로세스)과 iOS 시스템(Backboardd)에 대해 공부해볼게요

profile
개발자가 되고 싶은 공돌이

0개의 댓글