[iOS] Global Actor in Swift Concurrency

someng·2025년 10월 11일
0

iOS

목록 보기
39/39

Swift Concurrency는 async/awaitTask 같은 개념과 함께 글로벌 액터(global actor) 라는 개념을 도입했어요.
가장 흔히 쓰이는 글로벌 액터는 @MainActor이지만, 직접 커스텀 글로벌 액터를 정의해서 사용하는 것도 가능합니다.

많은 개발자에게 글로벌 액터가 언제, 어떻게 사용되는지는 여전히 다소 생소할 수 있는데, 이 글에서는 글로벌 액터의 개념과 사용 방법, 주의점 등을 정리해놨습니다.


Global Actor란 무엇인가?

Global Actor는 말 그대로 “전역적으로 사용할 수 있는 액터”입니다.
일반 액터(actor)가 특정 인스턴스의 상태 접근을 병렬성(동시성)으로부터 격리(isolation)하는 것처럼, 글로벌 액터는 전역 수준에서의 데이터나 기능 접근을 격리하는 역할을 해요.

예를 들어 @MainActor는 UI 업데이트처럼 항상 메인 스레드에서 실행되어야 할 코드에 붙여서, 해당 코드가 메인 스레드 격리 도메인(main-thread actor executor) 안에서 안전하게 실행되도록 보장합니다.

@MainActor
func updateUI() {
    // 메인 스레드에서 안전하게 실행됨
}

@MainActor를 속성(property)이나 타입(type)에 붙이는 것도 가능합니다:

@MainActor
var titles: [String] = []

@MainActor
final class ContentViewModel {
    var titles: [String] = []
    // ...
}

이런 식으로 붙이면, 그 속성이나 타입의 접근은 모두 @MainActor 격리 도메인 내에서만 허용돼요.

하지만 글로벌 액터는 메인 스레드에만 한정되지 않아요 — 직접 글로벌 액터를 정의해서 앱의 특정 기능 영역을 격리할 수도 있습니다.


커스텀 글로벌 액터를 사용하는 방법

예를 들어, 이미지 처리(image processing) 작업이 많은 앱이 있다고 가정해볼게요.
이미지 처리 기능은 동시에 여러 작업이 돌면 안 되고, 이미지 처리 관련 코드는 하나의 동기화된 흐름으로 실행되고 싶을 수 있어요.

이럴 때 커스텀 글로벌 액터를 정의할 수 있습니다:

@globalActor
actor ImageProcessing {
    static let shared = ImageProcessing()
}
  • @globalActor attribute 를 붙이면 이 액터가 Global Actor로 간주됩니다.
  • GlobalActor 프로토콜을 준수해야 하고, static let shared 인스턴스를 제공하면 됩니다.

이렇게 정의하면, 이후 코드에서 다음처럼 사용할 수 있어요:

@ImageProcessing
final class ImageCache {
    var images: [URL: Data] = [:]
    // 이미지 캐싱 로직 등…
}

@ImageProcessing
func applyFilter(_ inputImage: UIImage) -> UIImage {
    // 이미지 필터 적용 …
}

이제 @ImageProcessing으로 표시된 클래스나 함수는 ImageProcessing 글로벌 액터 격리 도메인에서 실행됩니다. 이렇게 하면 이미지 처리 관련 상태 접근을 중앙에서 안전하게 관리할 수 있어요.


⚠️ 글로벌 액터의 오용을 방지하기

그런데 위 예제에서 문제점이 하나 있어요 — 바로 누군가가 글로벌 액터 인스턴스를 직접 생성해버릴 수 있다는 것:

// 가능하긴 하지만, 이건 의도하지 않은 사용 방식이에요.
ImageProcessing()

이렇게 하면 새로운 액터 인스턴스가 생성되면서, 서로 다른 실행자(executor)가 만들어질 수 있고, 결국에는 동시성 안전성이 깨지는 문제가 생기죠.

이걸 막으려면 글로벌 액터의 initprivate으로 선언하는 게 좋습니다:

@globalActor
actor ImageProcessing {
    public static let shared = ImageProcessing()
    private init() { }
}

이렇게 하면 외부에서는 ImageProcessing()과 같이 생성할 수 없게 되어, 오직 shared 인스턴스를 통해서만 접근하게 돼요. 이렇게 해서 글로벌 액터의 중복 인스턴스 생성을 방지할 수 있습니다.

출처: https://www.avanderlee.com/concurrency/global-actor/

profile
👩🏻‍💻 iOS Developer

0개의 댓글