iOS Development Guide

Actor (2) - actor-isolated와 cross-actor reference

ZeddiOS의 Swift ) Actor (2) 글을 바탕으로, actor-isolated 멤버가 무엇인지, 같은 actor 내부 접근과 외부 접근이 어떻게 다른지, 그리고 왜 다른 actor에는 보통 `await`로 접근해야 하는지 정리한다.

Confirmed from source Inference clearly labeled Knowledge item #75
학습 날짜

2026-04-09

Why This Work Exists

Actor 1편에서 “왜 actor가 필요한가”를 정리했다면, 2편에서는 “actor 내부와 외부에서 접근 규칙이 왜 다르게 보이는가”를 정리해야 한다. 특히 actor-isolated, cross-actor reference, `await` 필요 여부가 가장 많이 헷갈린다.

Scope / Non-scope

  • actor-isolated 프로퍼티/메서드가 무엇인지 설명한다.
  • 같은 actor 내부 접근과 외부 접근의 차이를 설명한다.
  • `let`과 `var`가 actor 외부에서 어떻게 다르게 보이는지 정리한다.
  • `nonisolated`, global actor, reentrancy 상세 규칙은 다음 문서로 넘긴다.

As-is

흔한 오해는 “actor 안의 값은 외부에서 아예 못 본다” 혹은 “변경할 때만 await가 필요하다”는 식의 이해다. 하지만 실제 기준은 read/write 여부보다 먼저 actor 경계를 넘는 접근인지 여부다.

To-be

더 정확한 표현은 이렇다. 같은 actor 내부에서는 자기 actor-isolated 상태를 await 없이 접근할 수 있고, 다른 actor나 외부에서 그 상태에 접근하면 cross-actor reference가 되므로 보통 `await`가 필요하다.

What The Developer Must Understand Next

핵심 요약

`변경할 때만 await`가 아니라, `다른 actor 경계를 넘을 때 await`라고 이해하는 것이 맞다.

API / Data Contract

  • `actor-isolated`는 그 actor의 실행 문맥 안에서 직접 접근할 수 있는 멤버를 뜻한다.
  • `cross-actor reference`는 actor 바깥이나 다른 actor에서 그 멤버에 접근하는 경우다.
  • 같은 모듈 안의 actor `let` 저장 프로퍼티는 예외적으로 동기 접근이 허용될 수 있다.
  • actor의 mutable `var` 상태는 조회만 해도 보통 actor isolation 대상이다.

Risks And Decisions Needed

  • `self에서만 접근 가능`이라고만 쓰면 좁다. 더 정확한 표현은 `같은 actor isolation 영역 안에서 직접 접근 가능`이다.
  • `var는 외부에서 참조 못 한다`라고 쓰면 과하다. 정확히는 `외부에서 동기적으로 직접 접근 못 하고, 보통 await를 통한 접근이 필요하다`다.
  • `변경할 때만 await`라고 정리하면 틀릴 수 있다. 다른 actor의 mutable 상태는 read도 보통 await가 필요하다.

Class Diagram

classDiagram class BankAccount { +let accountNumber +var balance +deposit() +transfer() } class SameActorContext { +directAccess() } class ExternalContext { +crossActorAccess() } SameActorContext --> BankAccount : direct isolated access ExternalContext --> BankAccount : await cross-actor access

Sequence Diagram

sequenceDiagram participant Self as Self Actor participant Other as Other Actor rect rgb(240,253,244) Note over Self: Same actor Self->>Self: balance += amount Self->>Self: deposit(amount) end rect rgb(245,248,255) Note over Self,Other: Cross-actor Self->>Other: await deposit(amount) Other-->>Self: complete end

Flowchart

flowchart LR A["Want to access actor member"] --> B{"Same actor isolation?"} B -->|"Yes"| C["Direct access"] B -->|"No"| D{"Immutable let in same module?"} D -->|"Yes"| E["Synchronous read may be allowed"] D -->|"No"| F["Cross-actor reference"] F --> G["Use await access"]

Detailed Reading

`actor-isolated`라는 말은 actor의 저장 프로퍼티나 메서드가 그 actor의 보호 영역 안에 있다는 뜻이다. 따라서 같은 actor 내부에서는 이 멤버들을 자유롭게 참조할 수 있다.

예를 들어 actor 안에서 `self.balance`를 읽거나 `deposit()`을 호출하는 것은 같은 actor 내부 접근이므로 cross-actor reference가 아니다. 이 경우에는 보통 `await`가 필요 없다.

하지만 다른 actor나 일반 외부 코드에서 `account.balance`, `account.deposit()`처럼 접근하면 actor 경계를 넘게 된다. 이때가 cross-actor reference이고, 보통 비동기 접근 규칙에 따라 `await`가 필요하다.

Concrete Example

actor BankAccount {
    let accountNumber: Int
    var balance: Double

    init(accountNumber: Int, balance: Double) {
        self.accountNumber = accountNumber
        self.balance = balance
    }

    func deposit(amount: Double) {
        balance += amount
    }

    func transfer(to other: BankAccount, amount: Double) async {
        balance -= amount
        await other.deposit(amount: amount)
    }
}

위 코드에서 `balance -= amount`는 자기 actor 내부 상태 변경이므로 await가 필요 없다. 반면 `other.deposit(...)`은 다른 actor의 isolated 메서드 호출이므로 await가 필요하다.

About let and var

actor의 `let` 저장 프로퍼티는 초기화 후 변하지 않기 때문에, 같은 모듈 안에서는 외부에서도 동기 접근이 허용될 수 있다. 예를 들어 `account.accountNumber`는 같은 모듈 안에서 그냥 읽을 수 있다.

반대로 `var`는 mutable state이므로 외부에서 동기적으로 직접 읽고 쓰는 접근이 허용되지 않는다. 외부에서 보려면 보통 `await account.balance`처럼 cross-actor 비동기 접근을 사용해야 한다.

그래서 글에서 말하는 “나중에 `let`을 `var`로 바꾸면 외부 API 계약이 달라진다”는 말은, 기존 동기 접근 코드가 이제는 `await`를 요구하게 된다는 뜻이다.

Document Conclusion

  • 같은 actor 내부에서는 자기 actor-isolated 멤버를 await 없이 다룬다.
  • 다른 actor나 외부에서 접근하면 cross-actor reference가 된다.
  • cross-actor reference는 read/write를 가리지 않고 보통 await가 필요하다.
  • 같은 모듈 내부의 immutable let 저장 프로퍼티는 예외적으로 동기 읽기가 허용될 수 있다.

QA Checklist

  • `변경할 때만 await`로 단순화하지 않았는지 확인한다.
  • `var는 외부에서 못 본다`가 아니라 `외부에서 동기 직접 접근이 안 된다`로 적었는지 확인한다.
  • `self`보다 `same actor isolation` 개념을 중심으로 설명했는지 확인한다.
  • `let`의 same-module 예외를 빠뜨리지 않았는지 확인한다.

Operations / Rollout Checklist

  • 이 문서는 Actor 시리즈 2편으로 유지한다.
  • 3편에서는 `nonisolated`, protocol conformance, actor와 module boundary를 이어서 정리한다.
  • 인덱스 HTML에 시리즈 순서가 보이게 연결한다.