SwiftUI 개발을 하다 보면, 메인 스레드에서 UI를 업데이트하는 것의 중요성을 강조받게 된다.
이것은 앱의 반응성과 성능에 핵심적인 영향을 미친다.
여기서 MainActor가 중요한 역할을 한다고 한다, 오늘은 SwiftUI에서 MainActor를 효과적으로 사용하는 방법을 알아보도록 하자.
MainActor는 Swift의 동시성 모델에서 중요한 역할을 한다.
주로 메인 스레드에서 수행되어야 하는 작업을 관리하며, 이는 대부분 UI 업데이트와 관련이 있다.
SwiftUI에서는 MainActor를 사용하여 메인 스레드에서 안전하게 UI를 업데이트하고, 다른 스레드에서 수행되는 작업과의 동기화를 관리할 수 있다.
다들 이런 보라색 맛 오류를 한번 쯤 본적이 있을거다.
Background queue에서 UI 관련된 작업을 수행하면 다음 문구가 Xcode에 보라색으로 발생한다.
이럴 땐 간단하게 Main queue에서 실행할 수 있도록 변경해주면 된다.
바로 MainActor 사용 방법을 알아보도록 하자.
함수나 프로퍼티에 @MainActor
를 적용하면, 해당 코드가 메인 스레드에서 실행될 것임을 보장한다. 특히 UI를 업데이트하는 로직에 이를 적용하면 매우 유용하다.
예를 들어, 데이터를 비동기적으로 불러온 후 UI를 업데이트해야 하는 상황에서 유용하다. 백그라운드 스레드에서 데이터를 처리한 후, @MainActor가 적용된 함수를 호출하여 안전하게 UI를 업데이트할 수 있다.
class ViewModel: ObservableObject {
// 백그라운드 스레드에서 데이터 로딩
func loadData() async {
// 데이터 로딩 로직
// ...
await updateUI()
}
// UI 업데이트를 위한 @MainActor 함수
@MainActor
func updateUI() {
// UI 업데이트 로직
// ...
}
}
Swift의 async/await 패턴을 사용하여 비동기 작업을 수행한 후, MainActor.run
을 통해 메인 스레드에서 코드를 실행할 수도 있다.
class ViewModel: ObservableObject {
func loadData() async {
// 백그라운드 작업 수행
// ...
await MainActor.run {
// 메인 스레드에서 UI 업데이트 수행
// ...
}
}
}
MainActor
는 액터 기반 동시성 모델의 특별한 경우이다. 클래스나 구조체 전체를 @MainActor
로 표시하여, 내부의 모든 작업을 메인 스레드에서 실행하도록 할 수 있다.
이 방법은 특히 ViewModel이나 Controller 같은 클래스에서 메인 스레드에서만 작동해야 하는 여러 메서드와 프로퍼티가 있을 때 유용하다.
@MainActor
class ViewModel: ObservableObject {
// 모든 메서드와 프로퍼티는 메인 스레드에서 실행됨
func loadData() {
// 메인 스레드에서 안전하게 데이터 로딩 및 UI 업데이트 수행
// ...
}
}
여기서 갑자기 문득 궁금한점이 생겼다.
함수에 MainActor 여러개 적용하기보다 그냥 객체/구조체 전체에 MainActor 처리해주면 더 편한거 아닌가?
그래서 둘의 차이점을 조금 찾아봤다
단일 함수 또는 몇 개의 특정 함수에만 메인 스레드 실행을 제한하고 싶을 때 사용한다.
클래스나 구조체 내 다른 메서드와 프로퍼티에는 영향을 주지 않으므로, 클래스의 일부분만 메인 스레드에서 실행되어야 할 때 유용하다.
다양한 컨텍스트에서 실행되는 메서드가 있는 경우, @MainActor
를 함수 수준에서 적용함으로써 유연성을 유지할 수 있다.
객체의 모든 기능이 메인 스레드에서 실행되어야 할 때 사용한다.
예를 들어, ViewModel
이나 UI 컨트롤러
에서 주로 사용된다.
객체 또는 구조체의 모든 작업이 메인 스레드에 바인딩되므로, 백그라운드에서 실행되어야 하는 작업을 포함하는 객체에는 적합하지 않다.
메인 스레드에 대한 전역적인 제한이 적용되므로, 객체 내 특정 메서드만 백그라운드에서 실행해야 할 경우, 이 방법은 적합하지 않을 수 있다.
음 그러니까 개별 함수에 적용하는 것은 조금 더 세밀한 제어를 제공하는 반면에, 객체 전체에 적용하는 것은 일관된 메인 스레드 제약을 제공한다!!
그러면 특정 메서드만 백그라운드에서 실행하는 함수가 없을 경우에는 객체 또는 구조체 전체에@MainActor
처리를 해줘도 크게 문제될 건 없어보인다 상황 따라 잘 써야겠다.
MainActor
의 사용은 앱의 안정성과 사용자 경험을 크게 향상시킨다. 메인 스레드에서 수행되어야 하는 작업과 백그라운드 스레드에서 수행되는 작업 간의 동기화를 올바르게 관리함으로써, 앱이 더 반응적이고 부드럽게 동작하게 만들어 준다.