Swift / Actor

iOS 앱개발 공부

목록 보기
27/30

✒️ 도입

비동기 프로그래밍에서 가장 까다로운 적은 단연 데이터 레이스(Data Race)다.
여러 스레드가 동시에 같은 공유 자원에 접근하여 데이터를 수정하려 할 때 발생하는 이 문제는 예측 불가능한 버그와 크래시를 유발한다.
Swift 5.5는 이를 언어 차원에서 해결하기 위해 Actor라는 새로운 참조 타입을 도입했다.
클래스와 닮았지만 결정적인 차이가 있는 Actor의 실체를 파헤쳐 본다.


📌 Actor란 무엇인가?

Actor는 클래스와 마찬가지로 참조 타입(Reference Type)이지만, 가장 큰 특징은 가변 상태(Mutable State)를 외부로부터 격리한다는 점이다.

1) 격리와 보호

Actor는 한 번에 하나의 작업만 자신의 상태에 접근할 수 있도록 보장한다.
즉, 여러 스레드에서 동시에 접근하려 해도 Actor 내부의 '사서'가 한 명씩 순서대로 처리해 주는 것과 같다.
이를 통해 별도의 Lock이나 Semaphore 없이도 완벽한 Thread-Safety를 달성한다.

2) 클래스와의 차이점

  • 클래스: 데이터 레이스에 취약함, 상속 가능.
  • Actor: 데이터 레이스 방지(격리), 상속 불가.

📚 Actor 사용 예시와 동작 방식

1) 가변 상태 보호 (Counter 예시)

클래스로 구현했다면 여러 스레드에서 동시에 increment()를 호출할 때 숫자가 꼬이겠지만, Actor는 이를 방지한다.

actor BankAccount {
    private var balance: Int = 0
    
    func deposit(amount: Int) {
        balance += amount
    }
    
    func getBalance() -> Int {
        return balance
    }
}

2) 외부에서의 접근 (await의 필요성)

Actor의 속성이나 메서드는 외부에서 접근할 때 반드시 비동기(async)로 처리해야 한다. 다른 작업이 진행 중일 때 기다려야 할 수도 있기 때문이다.

let account = BankAccount()

Task {
    // 내부 상태를 변경하거나 읽을 때 await 필수
    await account.deposit(amount: 1000)
    let currentBalance = await account.getBalance()
    print("현재 잔액: \(currentBalance)")
}

🧠 어떤 상황에서 Actor를 써야 할까?

  1. 공유 자원 관리: 여러 화면이나 비동기 작업에서 공통으로 접근하는 데이터 저장소(Cache, Database 등).
  2. UI 업데이트 (@MainActor): UI와 관련된 작업은 항상 메인 스레드에서 돌아가야 하므로, 시스템에서 제공하는 특별한 Global Actor인 @MainActor를 사용한다.
  3. 싱글톤 패턴 대체: 전역적으로 상태를 공유해야 하는 객체를 Actor로 선언하면 더 안전하게 관리할 수 있다.

💡 요약

  • Actor는 Swift Concurrency의 핵심으로, 데이터 레이스를 방지하는 참조 타입이다.
  • 내부 상태를 격리하여 한 번에 하나의 작업만 접근할 수 있게 한다.
  • 외부에서 Actor의 멤버에 접근할 때는 항상 await를 사용해야 한다.
  • 상속이 필요 없고 안전한 공유 자원 관리가 목적이라면 클래스보다 Actor가 최고의 선택이다.

참조: Apple Documentation - Actors

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

0개의 댓글