[iOS] iOS 앱 실행 및 라이프사이클 완벽 가이드: 시작부터 메모리 로딩까지
iOS 앱이 아이콘을 탭하는 순간부터 화면에 표시되고 사용자와 상호작용하기까지의 과정은 복잡하면서도 체계적인 단계로 이루어져 있습니다. 앱 개발자라면 이 프로세스를 이해하는 것이 메모리 관리, 성능 최적화, 그리고 사용자 경험 향상에 필수적입니다.
이 글에서는 iOS 앱이 실행되어 메모리에 로드되고, 앱의 라이프사이클이 시작되는 전체 과정을 순차적으로 상세히 설명하겠습니다. 각 단계에서 어떤 일이 발생하는지, 시스템과 앱이 어떻게 상호작용하는지 명확히 이해할 수 있을 것입니다.
앱 실행 프로세스: 사용자 탭부터 메모리 로딩까지
1. 앱 아이콘 탭과 시스템 응답
사용자가 홈 스크린에서 앱 아이콘을 탭하면 iOS 시스템은 다음과 같은 단계로 응답합니다:
- SpringBoard 활성화: iOS의 홈 스크린 관리자인 SpringBoard가 탭 이벤트를 감지합니다.
- 앱 정보 확인: SpringBoard는 탭된 앱의 정보(번들 ID, 실행 권한 등)를 확인합니다.
- 실행 가능성 확인: 시스템은 앱이 실행 가능한 상태인지 확인합니다(앱 무결성, 권한 등).
// 시스템 내부적으로 다음과 같은 프로세스가 진행됩니다
// (개념적 표현, 실제 코드는 아님)
func handleAppLaunchRequest(bundleIdentifier: String) {
guard verifyAppIntegrity(bundleIdentifier) else {
displayError("앱 무결성 검증 실패")
return
}
launchApp(bundleIdentifier)
}
2. 프로세스 생성 및 바이너리 로딩
앱 실행이 결정되면 다음 단계로 진행됩니다:
- 프로세스 생성: 운영체제는 앱을 위한 새로운 프로세스를 생성합니다.
- 바이너리 로딩: 앱의 실행 파일(Mach-O 바이너리 파일)이 메모리에 로드됩니다.
- 동적 링커 실행: dyld(동적 링커)가 실행되어 앱이 필요로 하는 모든 프레임워크와 라이브러리를 로드합니다.
- 시스템 프레임워크(UIKit, Foundation 등)
- 앱이 사용하는 외부 라이브러리
- Swift 런타임
3. 정적 초기화 및 Runtime 설정
바이너리와 라이브러리가 로드된 후:
- 정적 초기화: 코드 내의 정적 변수와 상수가 초기화됩니다.
- Objective-C/Swift Runtime 설정: 클래스 계층 구조, 메서드 테이블 등이 설정됩니다.
- 이미지 로딩 알림: dyld는 dyld_image_added 알림을 발생시킵니다.
4. main() 함수 호출
모든 준비가 완료되면:
- main 진입점: 앱의 main() 함수가 호출됩니다.
- UIApplicationMain 호출: main() 함수는 UIApplicationMain() 함수를 호출합니다.
// @main이 표시된 클래스가 없는 경우의 전통적인 진입점
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
Swift 5 이상에서는 @main 속성을 사용하는 경우가 많습니다:
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// 앱 델리게이트 구현
}
앱 라이프사이클 시작: UIKit의 초기화부터 상태 전환까지
1. UIApplication 객체 생성
UIApplicationMain() 함수는:
- UIApplication 싱글턴 생성: 앱 전체를 관리하는 UIApplication 싱글턴 객체를 생성합니다.
- 앱 델리게이트 생성: AppDelegate 클래스의 인스턴스를 생성합니다.
- 이벤트 루프 설정: 메인 런 루프와 이벤트 처리 메커니즘을 설정합니다.
2. AppDelegate 초기화 및 호출
UIApplication 객체가 생성된 후:
- Info.plist 파싱: 앱의 설정 정보가 로드됩니다.
- 앱 델리게이트 설정: UIApplication 객체는 AppDelegate를 자신의 델리게이트로 설정합니다.
- 델리게이트 메서드 호출: application(_:didFinishLaunchingWithOptions:) 메서드가 호출됩니다.
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 앱 초기화 코드
// 데이터 구조 설정, 서비스 연결 등
return true
}
3. UI 초기화 및 화면 표시
앱 델리게이트 초기화 후:
- 메인 스토리보드 로드: Info.plist의 "Main storyboard file base name" 키에 지정된 스토리보드 파일을 로드합니다.
- 초기 뷰 컨트롤러 인스턴스화: 스토리보드의 초기 뷰 컨트롤러가 인스턴스화됩니다.
- 윈도우 생성: UIWindow 인스턴스가 생성되고 화면 크기로 설정됩니다.
- 루트 뷰 컨트롤러 설정: 초기 뷰 컨트롤러가 윈도우의 루트 뷰 컨트롤러로 설정됩니다.
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
// 코드로 초기 뷰 컨트롤러 설정 시
let initialViewController = MainViewController()
window?.rootViewController = initialViewController
window?.makeKeyAndVisible()
return true
}
SwiftUI 앱에서는 다음과 같이 진행됩니다:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
4. 뷰 컨트롤러 라이프사이클 시작
루트 뷰 컨트롤러가 설정되면 뷰 컨트롤러의 라이프사이클이 시작됩니다:
- viewDidLoad(): 뷰 컨트롤러의 뷰가 메모리에 로드된 후 호출됩니다.
- viewWillAppear(_:): 뷰가 화면에 표시되기 직전에 호출됩니다.
- viewDidLayoutSubviews(): 뷰의 서브뷰 레이아웃이 조정된 후 호출됩니다.
- viewDidAppear(_:): 뷰가 화면에 표시된 후 호출됩니다.
override func viewDidLoad() {
super.viewDidLoad()
// 뷰 초기화, 데이터 로딩 등 일회성 설정
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 화면이 표시되기 전 준비 (상태 업데이트 등)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// 화면이 표시된 후 추가 작업 (애니메이션, 네트워크 요청 등)
}
5. 앱 상태 전환
앱의 UI가 표시된 후, 앱은 다음 상태로 전환됩니다:
- Not Running → Inactive → Active: 앱이 전경에서 활성화됩니다.
- 상태 변화 알림: UIApplication.didBecomeActiveNotification 알림이 발송됩니다.
- 델리게이트 메서드 호출: applicationDidBecomeActive(_:) 메서드가 호출됩니다.
func applicationDidBecomeActive(_ application: UIApplication) {
// 앱이 활성 상태가 되었을 때 실행할 코드
// 일시 중지된 작업 재개, UI 업데이트 등
}
iOS 13 이상의 Scene 기반 라이프사이클에서는:
func sceneDidBecomeActive(_ scene: UIScene) {
// Scene이 활성 상태가 되었을 때 실행할 코드
}
앱 상태 전환과 메모리 관리
앱 상태 다이어그램
iOS 앱은 생명주기 동안 다음과 같은 상태를 거치게 됩니다:
- Not Running: 앱이 실행되지 않았거나 시스템에 의해 종료된 상태
- Inactive: 앱이 전경에서 실행 중이지만 이벤트를 받지 않는 상태 (예: 알림 센터 열림)
- Active: 앱이 전경에서 실행 중이고 이벤트를 받는 상태
- Background: 앱이 백그라운드에서 코드를 실행 중인 상태
- Suspended: 앱이 백그라운드에 있지만 코드를 실행하지 않는 상태
![앱 상태 다이어그램 (개념적 표현)]
메모리 관리 및 해제
iOS의 메모리 관리는 다음과 같이 작동합니다:
- 메모리 압박 시: 시스템이 메모리 부족을 감지하면 백그라운드 앱부터 종료합니다.
- didReceiveMemoryWarning: 메모리 부족 경고 시 이 메서드가 호출됩니다.
- 종료 우선순위: Not Running < Suspended < Background < Inactive < Active 순으로 종료됩니다.
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// 메모리 확보를 위한 작업 수행
// 캐시 정리, 불필요한 객체 해제 등
}
주요 단계별 최적화 팁
1. 앱 시작 최적화
- 불필요한 정적 초기화 줄이기: 앱 시작 시 필요하지 않은 복잡한 객체의 정적 초기화를 피합니다.
- 지연 로딩 활용: 모든 기능을 한번에 초기화하지 말고 필요할 때 로드합니다.
2. 앱 델리게이트 최적화
- 부팅 시간 단축: didFinishLaunchingWithOptions 메서드에서 필수적인 작업만 수행합니다.
- 비동기 초기화: 가능한 작업은 백그라운드 스레드로 이동합니다.
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 필수 초기화만 여기서 수행
setupAppAppearance()
// 무거운 작업은 비동기로 처리
DispatchQueue.global(qos: .utility).async {
self.prefetchImportantData()
}
return true
}
3. 뷰 컨트롤러 최적화
- viewDidLoad에서 무거운 작업 피하기: 이미지 처리, 네트워크 요청 등은 비동기로 처리합니다.
- 적절한 라이프사이클 메서드 활용: 각 상황에 맞는 라이프사이클 메서드를 활용합니다.
자주 묻는 질문 (FAQ)
Q: 앱이 백그라운드에서 종료되는 주요 이유는 무엇인가요?
A: 메모리 부족, 시스템 리소스 확보, 백그라운드 실행 시간 초과, 사용자의 강제 종료, 앱 충돌 등이 있습니다.
Q: SwiftUI 앱의 라이프사이클은 UIKit과 어떻게 다른가요?
A: SwiftUI는 App 프로토콜과 Scene 프로토콜을 사용하여 앱 구조를 정의합니다. AppDelegate와 SceneDelegate 대신 App 구조체와 Scene 관련 모디파이어를 사용하여 라이프사이클 이벤트를 처리합니다.
Q: 앱의 Cold Start와 Warm Start의 차이점은 무엇인가요?
A: Cold Start는 앱이 처음부터 시작되는 경우로, 모든 초기화 과정을 거칩니다. Warm Start는 앱이 백그라운드나 서스펜드 상태에서 다시 활성화되는 경우로, 초기화 과정이 줄어들어 더 빠르게 로드됩니다.
Q: iOS 13 이후 Scene 기반 라이프사이클과 기존 방식의 주요 차이점은 무엇인가요?
A: Scene 기반 라이프사이클은 여러 UI 인스턴스(예: iPad의 분할 화면)를 지원하기 위해 도입되었습니다. 앱 상태 관리가 UIApplicationDelegate에서 UISceneDelegate로 일부 이동했으며, 하나의 앱이 여러 UI 상태를 가질 수 있게 되었습니다.
iOS 앱의 실행 과정과 라이프사이클을 이해하면 더 효율적이고 안정적인 앱을 개발할 수 있습니다. 특히 시작 시간 최적화, 메모리 관리, 상태 전환 처리는 사용자 경험에 직접적인 영향을 미치는 중요한 요소입니다. 이 가이드가 여러분의 iOS 앱 개발에 도움이 되길 바랍니다.