Active — iOS 애플리케이션 생명주기의 활성 상태로, 전경에 있으며 터치 이벤트를 수신하고 사용자와 상호작용합니다. Active 상태가 어떻게 작동하는지, UIApplicationDelegate의 어떤 메서드가 이 상태를 담당하는지, Swift에서 Active와 Inactive 간 전환을 올바르게 처리하는 방법을 알아봅니다.
핵심 요약
Active — 모바일 애플리케이션 생명주기의 상태로, 전경에 있고 기기 화면에 표시되며 사용자와 적극적으로 상호작용합니다. 이 상태에서 애플리케이션은 모든 터치 이벤트, 키 입력, 가속도계 및 자이로스코프 데이터를 수신하며, 인터페이스 렌더링을 위해 그래픽 프로세서에 완전히 액세스할 수 있습니다.
iOS에서 Active 상태는 5가지 상태 생명주기 모델의 일부입니다: Not Running → Inactive → Active → Inactive → Background → Suspended → Not Running. Android에서는 onResume 호출 후 Activity가 스택 맨 위에 있고 사용자 입력을 받는 상태와 유사합니다. Active는 UI가 완전히 상호작용 가능한 유일한 상태이며 제스처, 스크롤, 탭 및 애니메이션에 반응합니다.
시스템은 Active 상태의 애플리케이션에 CPU 및 RAM 측면에서 최대 우선순위를 부여합니다. 즉, 리소스가 부족할 때 시스템이 이러한 애플리케이션을 종료하지 않습니다 — 먼저 백그라운드 및 일시 중단된 프로세스가 언로드됩니다. 그러나 애플리케이션은 배터리를 소모하지 않고 CPU 스로틀링을 유발하지 않도록 리소스를 효율적으로 사용해야 합니다.
사용자에게 Active는 애플리케이션 작업의 정상적인 상태입니다. 사용자는 인터페이스를 보고, 버튼을 누르고, 양식을 작성하고, 피드를 스크롤할 수 있습니다. 이 상태의 모든 중단(전화, 알림, Control Center를 위한 위로 스와이프)은 애플리케이션을 Inactive로 전환한 후 Active로 돌아가거나 Background로 이동할 수 있습니다.
iOS는 상태 관리를 위해 UIApplicationMain을 사용합니다. Active로 전환 시 시스템이 applicationDidBecomeActive를 호출합니다. SwiftUI의 유사한 메커니즘은 Environment를 통한 scenePhase 관찰입니다. Android는 전경에서 Activity의 활동 지표로 onResume을 사용합니다. 두 접근 방식 모두 애플리케이션이 상태 변경 알림을 받고 동작을 조정할 수 있도록 보장합니다.
| 플랫폼 | 메서드/이벤트 | Swift (UIKit) | SwiftUI | Android (Kotlin) |
|---|---|---|---|---|
| iOS | Active로 전환 | applicationDidBecomeActive | scenePhase == .active | — |
| iOS | Active에서 벗어남 | applicationWillResignActive | scenePhase == .inactive | — |
| Android | Active로 전환 | — | — | onResume() |
| Android | Active에서 벗어남 | — | — | onPause() |
iOS에서 Active 상태는 UIApplicationDelegate를 통해 처리됩니다. 주요 메서드는 applicationDidBecomeActive(_:)입니다. 이는 애플리케이션 첫 실행 시와 Inactive에서 복귀 시 호출됩니다. 이 메서드는 Inactive로 전환 시 일시 중단된 작업을 재개하기에 이상적인 위치입니다: 애니메이션 시작, 타이머 재개, 센서 재시작, 서버 데이터 업데이트 확인.
iOS 13부터 Apple은 iPad에서 여러 창 지원을 위해 UISceneDelegate를 도입했습니다. 이 경우 applicationDidBecomeActive는 각 개별 씬에 대해 sceneDidBecomeActive로 대체됩니다. 단일 화면만 지원하는 애플리케이션은 계속 UIApplicationDelegate를 사용할 수 있습니다. 두 접근 방식 모두 애플리케이션이나 씬이 활성화되는 순간에 호출됩니다.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// 앱이 활성화되었습니다 — 작업 재개
func applicationDidBecomeActive(_ application: UIApplication) {
resumeAnimations()
restartTimers()
refreshDataIfNeeded()
startObservingSensors()
}
// 앱이 활성 상태를 잃습니다 — 일시 중단
func applicationWillResignActive(_ application: UIApplication) {
pauseAnimations()
stopTimers()
saveDraftData()
}
private func resumeAnimations() {
UIView.animate(withDuration: 0.3) {
// UI 애니메이션 재개
}
}
private func refreshDataIfNeeded() {
let lastRefresh = UserDefaults.standard.object(forKey: "lastRefresh") as? Date ?? .distantPast
if Date().timeIntervalSince(lastRefresh) > 300 {
fetchDataFromServer()
}
}
}코드는 UIKit에서 Active의 올바른 처리를 보여줍니다. applicationDidBecomeActive는 애니메이션, 타이머를 재개하고 데이터 업데이트가 필요한지 확인합니다. applicationWillResignActive는 리소스를 소비할 수 있는 모든 것을 일시 중단하고 초안을 저장합니다. 이러한 메서드 쌍은 애플리케이션이 상태 변경에 올바르게 반응하도록 보장합니다.
SwiftUI에는 AppDelegate가 없습니다 — 상태 관리는 Environment<ScenePhase>를 통해 이루어집니다. .active 값은 씬이 전경에 있고 상호작용 가능할 때 설정됩니다. SwiftUI는 Active로 복귀 시 애니메이션과 업데이트를 자동으로 재개합니다. 개발자는 부작용 수행을 위해 onChange만 구독하면 됩니다.
import SwiftUI
@main
struct ActiveDemoApp: App {
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { oldPhase, newPhase in
switch newPhase {
case .active:
print("씬이 활성화되었습니다")
resumeWork()
case .inactive:
print("씬이 비활성화되었습니다")
pauseWork()
case .background:
print("씬이 백그라운드로 이동했습니다")
saveState()
@unknown default:
break
}
}
}
private func resumeWork() {
// 네트워크 요청, 애니메이션 재개
}
private func pauseWork() {
// 시간에 민감한 작업 일시 중단
}
private func saveState() {
// 앱 상태 저장
}
}SwiftUI에서 scenePhase는 애플리케이션 상태에 대한 유일한 진실 공급원입니다. onChange는 각 전환 시 작업을 수행할 수 있게 합니다. scenePhase는 iOS 14+ 및 SwiftUI Lifecycle에서만 사용 가능합니다. SwiftUI 화면이 있는 UIKit 애플리케이션의 경우 UIApplicationDelegate 접근 방식을 사용하세요.
Active는 여러 경로를 통해 도달합니다. 첫 번째는 콜드 스타트: 사용자가 아이콘을 누르면 애플리케이션이 Not Running에서 Inactive를 거쳐 Active로 전환됩니다. 두 번째는 백그라운드에서 복귀: 사용자가 App Switcher를 통해 애플리케이션으로 돌아오면 Inactive를 거쳐 Active가 됩니다. 세 번째는 일시적 중단에서 복귀: 사용자가 전화를 끝내고, Control Center를 닫거나 알림에 응답하면 Inactive에서 Active로 돌아옵니다.
Not Running → Inactive → Active — 콜드 스타트. Background → Inactive → Active — 백그라운드에서 복귀. Inactive → Active — 일시적 중단에서 복귀. 각 경우 applicationDidBecomeActive가 호출되지만 컨텍스트가 다를 수 있습니다. 콜드 스타트 시 Active 전에 didFinishLaunchingWithOptions가 호출되고, 백그라운드에서 복귀 시 willEnterForeground가 호출됩니다. 개발자는 이러한 차이를 사용하여 상태 복원 전략을 선택할 수 있습니다.
| 시나리오 | 전환 경로 | iOS 콜백 | Android 콜백 |
|---|---|---|---|
| 콜드 스타트 | Not Running → Active | didFinishLaunching → didBecomeActive | onCreate → onStart → onResume |
| 백그라운드에서 복귀 | Background → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| Suspended에서 복귀 | Suspended → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| 중단 후 | Inactive → Active | didBecomeActive | onResume |
중요 참고: Suspended에서 복귀 시 iOS는 didFinishLaunchingWithOptions를 호출하지 않습니다. 애플리케이션이 이미 메모리에 로드되었기 때문입니다. 즉, 이 메서드에 배치된 초기화 코드가 다시 실행되지 않습니다. 개발자는 종종 이를 잊고 두 시나리오 모두를 위해 중요 로직을 applicationWillEnterForeground 또는 applicationDidBecomeActive로 이동합니다.
Android에서 Active의 유사점은 onResume() 호출 후 Activity 상태입니다. Activity는 전경에 있고 사용자 입력을 받을 때 활성 상태로 간주됩니다. 이 상태는 Activity 스택의 맨 위에 해당합니다. 다른 Activity가 위에 나타나면(부분적으로라도) 현재 Activity는 onPause 상태로 전환됩니다 — iOS Inactive와 유사합니다.
Android의 주요 차이점 — multi-window 모드(split screen, freeform)에서 여러 Activity가 동시에 활성화될 수 있습니다. 이 경우 사용자가 상호작용하는 Activity가 활성 상태로 간주되고 옆 Activity는 일시 중지(onPause)됩니다. iOS는 iPhone에서 multi-window를 지원하지 않으며, iPad에서만 UIScene을 통해 지원합니다.
class MainActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
// 앱이 활성화되었습니다 — 작업 재개
resumeCameraPreview()
startLocationUpdates()
activateSensors()
}
override fun onPause() {
super.onPause()
// 앱이 활성 상태를 잃습니다 — 리소스 해제
releaseCamera()
stopLocationUpdates()
deactivateSensors()
}
private fun resumeCameraPreview() {
// 카메라 미리보기 시작 (권한 필요)
cameraProvider?.unbindAll()
cameraProvider?.bindToLifecycle(
this,
cameraSelector,
preview,
imageAnalyzer
)
}
private fun startLocationUpdates() {
val locationRequest = LocationRequest.Builder(
Priority.PRIORITY_HIGH_ACCURACY, 5000
).build()
locationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
}코드는 Android에서 onResume/onPause를 통한 Active 처리를 보여줍니다. onResume은 카메라, 위치 정보 및 센서 작업을 재개합니다 — 앱이 사용자에게 보일 때만 활성화되어야 하는 리소스입니다. onPause는 배터리 소모를 방지하기 위해 이러한 리소스를 해제합니다. CameraX lifecycle-aware API는 onPause 시 미리보기를 자동으로 일시 중단합니다.
첫 번째 규칙 — applicationDidBecomeActive 또는 onResume에서 무거운 작업을 수행하지 마세요. 데이터 로딩, JSON 파싱, 데이터베이스 작업 등은 모두 비동기여야 하며 메인 스레드를 차단하지 않아야 합니다. 백그라운드 작업을 위해 iOS에서는 GCD (DispatchQueue)를, Kotlin에서는 Coroutines를 사용하세요. 메인 스레드는 UI 업데이트와 비동기 작업 시작만 담당해야 합니다.
두 번째 규칙 — Active로 복귀할 때마다 상태를 동기화하세요. 사용자가 시스템 애플리케이션에서 설정을 변경하거나, push 알림을 받거나, 다른 애플리케이션에서 데이터를 업데이트했을 수 있습니다. Active로 전환 시 캐시 유효성을 확인하세요 — 사용자가 없는 동안 데이터가 오래되었을 수 있습니다.
세 번째 규칙 — Active를 유일한 상태로 의존하지 마세요. 애플리케이션이 Active를 건너뛰고 Not Running에서 직접 Background로 전환될 수 있습니다(백그라운드 모드에서 실행되는 경우). iOS에서는 content-available 옵션이 있는 push 알림을 통해 실행 시 발생합니다. Android에서는 BroadcastReceiver를 통해 실행 시 발생합니다. UI 작업 수행 전에 항상 현재 상태를 확인하세요.
네 번째 규칙 — onActivityResult 대신 Android에서 Activity Result API를 사용하세요. 이를 통해 Activity 재생성 시 데이터 손실 없이 Active 상태에서 카메라, 갤러리 또는 권한 호출 결과를 처리할 수 있습니다. iOS의 경우 시스템 대화상자에 UIApplication.shared.open과 함께 async/await를 사용하세요.
import UIKit
final class ActiveStateManager {
static let shared = ActiveStateManager()
private var isActive = false
func setActive(_ active: Bool) {
isActive = active
if active {
NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
}
}
func performWhenActive(_ block: @escaping () -> Void) {
if isActive {
block()
} else {
// Active로 복귀할 때까지 실행 연기
NotificationCenter.default.addObserver(
forName: .appDidBecomeActive,
object: nil,
queue: .main
) { _ in
block()
}
}
}
}
extension Notification.Name {
static let appDidBecomeActive = Notification.Name("appDidBecomeActive")
}코드는 애플리케이션의 다른 구성 요소가 현재 활성 상태를 확인할 수 있게 하는 Active 상태 관리자를 보여줍니다. performWhenActive는 애플리케이션이 활성 상태이면 즉시 블록을 실행하거나 Active로 복귀할 때까지 실행을 연기합니다. 이는 사용자가 애플리케이션으로 돌아온 후 작업을 수행해야 하는 서비스에 유용합니다.
자주 묻는 질문
이 메서드는 애플리케이션이 활성 상태로 전환될 때마다 호출됩니다: 첫 실행 시, 백그라운드에서 복귀 시, Control Center 또는 Notification Center 닫은 후, 전화 종료 후. 일반 세션에서 사용자 작업에 따라 5–10회 호출될 수 있습니다. 이 메서드에 일회성 초기화를 배치하지 마세요.
Visible — 비공식 용어로, 애플리케이션이 화면에 보이지만 이벤트를 받지 못할 수 있음을 의미합니다(예: iPad에서 다른 창에 부분적으로 가려진 경우). Active — 공식 상태로, 애플리케이션이 보이고 상호작용 가능합니다. iPhone에서 Visible 애플리케이션은 항상 Active이며, iPad에서는 Visible + Inactive 상황이 가능합니다.
willEnterForeground는 백그라운드에서 복귀 시 호출되지만 애플리케이션이 아직 활성화되지 않았습니다 — Inactive 상태입니다. didBecomeActive는 애플리케이션이 완전히 상호작용 가능해진 후 호출됩니다. 사용자가 인터페이스를 보기 전에 작업을 수행해야 하는 경우 willEnterForeground를 사용하세요. 표시 후에는 didBecomeActive를 사용하세요.
아니요. Active는 애플리케이션이 전경에 있고 화면에 표시됨을 의미합니다. 보이는 UI 없이 애플리케이션은 Background 또는 Suspended 상태일 수 있습니다. 예외는 iPad multi-window로, 한 창은 활성화되고 다른 창은 비활성화될 수 있지만 둘 다 보입니다. VoiceOver와 받아쓰기는 이 규칙을 변경하지 않습니다.
iOS 시뮬레이터에서 Home Screen으로 이동하려면 Cmd+Shift+H를 누르고(앱이 Background로 이동), 다시 앱 아이콘을 클릭하세요. 화면 잠금(willResignActive) 및 잠금 해제(didBecomeActive)를 위해 Cmd+L을 사용하세요. Inactive 테스트를 위해 Control Center(macOS 키보드의 경우 Cmd+Shift+;) 또는 Notification Center를 호출하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.