IOS

[iOS] iOS 앱 실행 및 라이프사이클 완벽 가이드: 시작부터 메모리 로딩까지

Develup 2025. 3. 6. 22:07
반응형

iOS 앱이 아이콘을 탭하는 순간부터 화면에 표시되고 사용자와 상호작용하기까지의 과정은 복잡하면서도 체계적인 단계로 이루어져 있습니다. 앱 개발자라면 이 프로세스를 이해하는 것이 메모리 관리, 성능 최적화, 그리고 사용자 경험 향상에 필수적입니다.

이 글에서는 iOS 앱이 실행되어 메모리에 로드되고, 앱의 라이프사이클이 시작되는 전체 과정을 순차적으로 상세히 설명하겠습니다. 각 단계에서 어떤 일이 발생하는지, 시스템과 앱이 어떻게 상호작용하는지 명확히 이해할 수 있을 것입니다.

앱 실행 프로세스: 사용자 탭부터 메모리 로딩까지

1. 앱 아이콘 탭과 시스템 응답

사용자가 홈 스크린에서 앱 아이콘을 탭하면 iOS 시스템은 다음과 같은 단계로 응답합니다:

  1. SpringBoard 활성화: iOS의 홈 스크린 관리자인 SpringBoard가 탭 이벤트를 감지합니다.
  2. 앱 정보 확인: SpringBoard는 탭된 앱의 정보(번들 ID, 실행 권한 등)를 확인합니다.
  3. 실행 가능성 확인: 시스템은 앱이 실행 가능한 상태인지 확인합니다(앱 무결성, 권한 등).
// 시스템 내부적으로 다음과 같은 프로세스가 진행됩니다
// (개념적 표현, 실제 코드는 아님)
func handleAppLaunchRequest(bundleIdentifier: String) {
    guard verifyAppIntegrity(bundleIdentifier) else {
        displayError("앱 무결성 검증 실패")
        return
    }
    
    launchApp(bundleIdentifier)
}

2. 프로세스 생성 및 바이너리 로딩

앱 실행이 결정되면 다음 단계로 진행됩니다:

  1. 프로세스 생성: 운영체제는 앱을 위한 새로운 프로세스를 생성합니다.
  2. 바이너리 로딩: 앱의 실행 파일(Mach-O 바이너리 파일)이 메모리에 로드됩니다.
  3. 동적 링커 실행: dyld(동적 링커)가 실행되어 앱이 필요로 하는 모든 프레임워크와 라이브러리를 로드합니다.
    • 시스템 프레임워크(UIKit, Foundation 등)
    • 앱이 사용하는 외부 라이브러리
    • Swift 런타임

3. 정적 초기화 및 Runtime 설정

바이너리와 라이브러리가 로드된 후:

  1. 정적 초기화: 코드 내의 정적 변수와 상수가 초기화됩니다.
  2. Objective-C/Swift Runtime 설정: 클래스 계층 구조, 메서드 테이블 등이 설정됩니다.
  3. 이미지 로딩 알림: dyld는 dyld_image_added 알림을 발생시킵니다.

4. main() 함수 호출

모든 준비가 완료되면:

  1. main 진입점: 앱의 main() 함수가 호출됩니다.
  2. 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() 함수는:

  1. UIApplication 싱글턴 생성: 앱 전체를 관리하는 UIApplication 싱글턴 객체를 생성합니다.
  2. 앱 델리게이트 생성: AppDelegate 클래스의 인스턴스를 생성합니다.
  3. 이벤트 루프 설정: 메인 런 루프와 이벤트 처리 메커니즘을 설정합니다.

2. AppDelegate 초기화 및 호출

UIApplication 객체가 생성된 후:

  1. Info.plist 파싱: 앱의 설정 정보가 로드됩니다.
  2. 앱 델리게이트 설정: UIApplication 객체는 AppDelegate를 자신의 델리게이트로 설정합니다.
  3. 델리게이트 메서드 호출: application(_:didFinishLaunchingWithOptions:) 메서드가 호출됩니다.
func application(_ application: UIApplication, 
                didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 앱 초기화 코드
    // 데이터 구조 설정, 서비스 연결 등
    return true
}

3. UI 초기화 및 화면 표시

앱 델리게이트 초기화 후:

  1. 메인 스토리보드 로드: Info.plist의 "Main storyboard file base name" 키에 지정된 스토리보드 파일을 로드합니다.
  2. 초기 뷰 컨트롤러 인스턴스화: 스토리보드의 초기 뷰 컨트롤러가 인스턴스화됩니다.
  3. 윈도우 생성: UIWindow 인스턴스가 생성되고 화면 크기로 설정됩니다.
  4. 루트 뷰 컨트롤러 설정: 초기 뷰 컨트롤러가 윈도우의 루트 뷰 컨트롤러로 설정됩니다.
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. 뷰 컨트롤러 라이프사이클 시작

루트 뷰 컨트롤러가 설정되면 뷰 컨트롤러의 라이프사이클이 시작됩니다:

  1. viewDidLoad(): 뷰 컨트롤러의 뷰가 메모리에 로드된 후 호출됩니다.
  2. viewWillAppear(_:): 뷰가 화면에 표시되기 직전에 호출됩니다.
  3. viewDidLayoutSubviews(): 뷰의 서브뷰 레이아웃이 조정된 후 호출됩니다.
  4. viewDidAppear(_:): 뷰가 화면에 표시된 후 호출됩니다.
override func viewDidLoad() {
    super.viewDidLoad()
    // 뷰 초기화, 데이터 로딩 등 일회성 설정
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // 화면이 표시되기 전 준비 (상태 업데이트 등)
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    // 화면이 표시된 후 추가 작업 (애니메이션, 네트워크 요청 등)
}

5. 앱 상태 전환

앱의 UI가 표시된 후, 앱은 다음 상태로 전환됩니다:

  1. Not Running → Inactive → Active: 앱이 전경에서 활성화됩니다.
  2. 상태 변화 알림: UIApplication.didBecomeActiveNotification 알림이 발송됩니다.
  3. 델리게이트 메서드 호출: applicationDidBecomeActive(_:) 메서드가 호출됩니다.
func applicationDidBecomeActive(_ application: UIApplication) {
    // 앱이 활성 상태가 되었을 때 실행할 코드
    // 일시 중지된 작업 재개, UI 업데이트 등
}

iOS 13 이상의 Scene 기반 라이프사이클에서는:

func sceneDidBecomeActive(_ scene: UIScene) {
    // Scene이 활성 상태가 되었을 때 실행할 코드
}

앱 상태 전환과 메모리 관리

앱 상태 다이어그램

iOS 앱은 생명주기 동안 다음과 같은 상태를 거치게 됩니다:

  1. Not Running: 앱이 실행되지 않았거나 시스템에 의해 종료된 상태
  2. Inactive: 앱이 전경에서 실행 중이지만 이벤트를 받지 않는 상태 (예: 알림 센터 열림)
  3. Active: 앱이 전경에서 실행 중이고 이벤트를 받는 상태
  4. Background: 앱이 백그라운드에서 코드를 실행 중인 상태
  5. Suspended: 앱이 백그라운드에 있지만 코드를 실행하지 않는 상태

![앱 상태 다이어그램 (개념적 표현)]

메모리 관리 및 해제

iOS의 메모리 관리는 다음과 같이 작동합니다:

  1. 메모리 압박 시: 시스템이 메모리 부족을 감지하면 백그라운드 앱부터 종료합니다.
  2. didReceiveMemoryWarning: 메모리 부족 경고 시 이 메서드가 호출됩니다.
  3. 종료 우선순위: 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 앱 개발에 도움이 되길 바랍니다.

반응형