Swift Actor:
Actor란?
Actor는 Swift의 Concurrency 모델에서 데이터 경쟁(data race)을 방지하기 위해 도입된 타입
멀티 스레드 환경에서도 안전하게 상태를 보호하면서 비동기 작업을 수행할 수 있다.
핵심 개념
- Actor는 상태를 보호하는 객체
- Actor 내부의 프로퍼티나 메서드는 직접 접근할 수 없고, 비동기(await) 방식으로 접근
- 내부 동작은 **직렬 큐(serial queue)**에서 처리 (즉, 동시에 두 작업이 실행되지 않음)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| actor Counter { var value: Int = 0
func increment() { value += 1 }
func getValue() -> Int { return value } }
let counter = Counter() await counter.increment() let current = await counter.getValue()
|
Actor vs Class vs Struct
항목 |
struct |
class |
actor |
타입 |
값 타입 (Value Type) |
참조 타입 (Reference Type) |
참조 타입 (Reference Type) |
메모리 할당 |
스택 또는 인라인 |
힙 |
힙 |
상속 |
❌ |
✅ |
❌ |
ARC 적용 |
❌ |
✅ |
✅ |
스레드 안전성 |
❌ 직접 처리해야 함 |
❌ 직접 처리해야 함 |
✅ 자동으로 직렬 처리됨 |
비동기 호출 필요 |
❌ |
❌ |
✅ (await ) |
동시성 안전 |
❌ |
❌ |
✅ |
중요한 특징과 제약
1. 동기 메서드도 actor 내부에서는 동기 실행
1 2 3 4 5 6 7 8
| actor Sample { var count = 0
func add() { count += 1 } }
|
2. 외부에서 프로퍼티/메서드 접근 시에는 항상 await 필요
1 2
| await sample.add() let result = await sample.count
|
3. 비동기 환경에서 안전하게 공유 자원을 보호하는 용도로 사용
→ 예: 네트워크 상태, 로그 기록, 캐시, 유저 세션 관리 등
actor와 class는 무엇이 가장 다른가?
구분 |
class |
actor |
멀티스레드에서 동시 접근 |
❌ 위험 (data race 발생 가능) |
✅ 내부적으로 직렬화 처리 |
메서드 호출 방식 |
동기 방식으로 접근 가능 |
외부에서는 await 필요 |
목적 |
모델링, 객체지향적 구조 |
상태 보호, 동시성 제어 목적 |
접근 제어 |
일반적인 접근 가능 |
동시성 컨텍스트 제약 (actor-isolated) |
actor 내에서 주의할 점
- actor 내부에서도
nonisolated
키워드를 사용해 동시 접근 허용 가능
MainActor
는 메인 스레드에서 실행되어야 하는 작업을 지정
- actor 내부에서
async let
, Task
등을 쓸 경우 자체 스레드 안전성 유지 불가 → 별도로 관리해야 함
actor가 유용한 상황
- 여러 스레드에서 동시에 접근하는 자원을 안전하게 보호하고 싶을 때
- 로그 기록, 캐시 접근, DB I/O, 상태 저장 등의 비동기 처리
- Swift Concurrency를 적용한 앱에서 thread-safe한 데이터 구조를 만들고 싶을 때
정리
actor
는 멀티스레드 안전한 클래스와 비슷한 구조
- 비동기 환경에서 안전한 상태 공유가 필요할 때 가장 적합
- 클래스를 쓰던 자리에 곧바로 대체하기보다는 상태 보호가 필요한 경우에만 actor로 치환하는 것이 권장됨