Swift 5.5에서 도입된 Structured Concurrency는 iOS 개발의 패러다임을 바꿨다.
그중에서도 @MainActor는 UI 업데이트의 안전성을 컴파일 타임에서 보장해 주는 핵심적인 도구다.
기존의 DispatchQueue.main.async를 대체하며 더 안전하고 직관적인 코드를 작성하는 방법을 정리한다.
iOS 앱에서 UI 업데이트는 반드시 메인 스레드(Main Thread)에서 이루어져야 한다.
이를 어길 경우 앱이 크래시되거나 예측 불가능한 UI 버그가 발생한다.
MainActor는 Swift의 Actor 모델을 기반으로 하며, 시스템 전체에서 메인 스레드를 관리하는 Global Actor다. @MainActor 어트리뷰트가 붙은 클래스, 메서드, 혹은 프로퍼티는 항상 메인 큐에서 실행되도록 보장받는다.
과거에는 런타임에 메인 스레드 여부를 확인해야 했으나, @MainActor를 사용하면 컴파일러가 이를 미리 체크한다. 만약 메인 액터 외부에서 접근하려고 하면 컴파일러가 에러나 경고를 발생시켜 데이터 레이스(Data Race)를 방지한다.
ViewModel이나 ViewController 전체에 적용하여 모든 프로퍼티와 메서드가 메인 스레드에서 동작하게 할 수 있다.
@MainActor
class MyViewModel: ObservableObject {
@Published var name = "Yachiyo"
func updateName() {
// 항상 메인 스레드에서 실행됨
name = "Kaguya"
}
}
비동기 작업을 수행하는 함수 내부에서 UI를 건드리는 부분만 특정하여 지정할 수 있다.
class DataManager {
func fetchData() async {
// 백그라운드 작업 수행
let result = await networkRequest()
await updateUI(with: result)
}
@MainActor
func updateUI(with data: String) {
// UI 업데이트 로직
}
}
때로는 클래스가 @MainActor로 선언되어 있더라도, 특정 메서드는 메인 스레드를 점유하지 않고 백그라운드에서 실행되어야 할 때가 있다.
이때 nonisolated 키워드를 사용한다.
@MainActor는 UI 업데이트 전용 스레드인 메인 스레드에서의 실행을 보장한다.await 키워드를 통해 메인 액터로의 전환을 명시적으로 처리한다.ObservableObject를 사용하는 ViewModel은 특별한 이유가 없다면 클래스 수준에서 @MainActor를 선언하는 것이 권장된다.