iOS / @MainActor

iOS 앱개발 공부

목록 보기
26/30

✒️ 도입

Swift 5.5에서 도입된 Structured Concurrency는 iOS 개발의 패러다임을 바꿨다.
그중에서도 @MainActor는 UI 업데이트의 안전성을 컴파일 타임에서 보장해 주는 핵심적인 도구다.
기존의 DispatchQueue.main.async를 대체하며 더 안전하고 직관적인 코드를 작성하는 방법을 정리한다.

🧠 @MainActor의 개념과 필요성

iOS 앱에서 UI 업데이트는 반드시 메인 스레드(Main Thread)에서 이루어져야 한다.
이를 어길 경우 앱이 크래시되거나 예측 불가능한 UI 버그가 발생한다.

1) Actor와 Global Actor

MainActor는 Swift의 Actor 모델을 기반으로 하며, 시스템 전체에서 메인 스레드를 관리하는 Global Actor다. @MainActor 어트리뷰트가 붙은 클래스, 메서드, 혹은 프로퍼티는 항상 메인 큐에서 실행되도록 보장받는다.

2) 컴파일 타임 체크

과거에는 런타임에 메인 스레드 여부를 확인해야 했으나, @MainActor를 사용하면 컴파일러가 이를 미리 체크한다. 만약 메인 액터 외부에서 접근하려고 하면 컴파일러가 에러나 경고를 발생시켜 데이터 레이스(Data Race)를 방지한다.

✅ @MainActor의 활용 방법

1) 클래스 전체 적용

ViewModel이나 ViewController 전체에 적용하여 모든 프로퍼티와 메서드가 메인 스레드에서 동작하게 할 수 있다.

@MainActor
class MyViewModel: ObservableObject {
    @Published var name = "Yachiyo"
    
    func updateName() {
        // 항상 메인 스레드에서 실행됨
        name = "Kaguya"
    }
}

2) 특정 메서드에만 적용

비동기 작업을 수행하는 함수 내부에서 UI를 건드리는 부분만 특정하여 지정할 수 있다.

class DataManager {
    func fetchData() async {
        // 백그라운드 작업 수행
        let result = await networkRequest()
        
        await updateUI(with: result)
    }
    
    @MainActor
    func updateUI(with data: String) {
        // UI 업데이트 로직
    }
}

🔥 심화: nonisolated와 격리 해제

때로는 클래스가 @MainActor로 선언되어 있더라도, 특정 메서드는 메인 스레드를 점유하지 않고 백그라운드에서 실행되어야 할 때가 있다.
이때 nonisolated 키워드를 사용한다.

  • nonisolated: 액터의 격리에서 제외되어 어떤 스레드에서도 호출 가능하게 만든다. 하지만 액터의 가변 프로퍼티에는 직접 접근할 수 없다.

💡 요약

  • @MainActor는 UI 업데이트 전용 스레드인 메인 스레드에서의 실행을 보장한다.
  • 컴파일 타임에 안전성을 검사하므로 런타임 에러를 획기적으로 줄여준다.
  • await 키워드를 통해 메인 액터로의 전환을 명시적으로 처리한다.
  • ObservableObject를 사용하는 ViewModel은 특별한 이유가 없다면 클래스 수준에서 @MainActor를 선언하는 것이 권장된다.

참조 - Apple Documentation - MainActor

profile
이유있는 코드를 쓰자!!

0개의 댓글