[SC] 전송 가능
I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have it, I’ll keep the source link unchanged and translate the rest into Korean while preserving all formatting, markdown, and technical terms.
질문
왜 컴파일러는 값이 스레드‑안전인지 알아야 할까요?
컴파일러는 값이 격리 도메인 간에 이동될 때 데이터 레이스가 발생하지 않도록 보장해야 합니다. 값이 안전하지 않다면, 컴파일러는 비결정적인 동작을 방지하기 위해 경고나 오류를 발생시킵니다.
isolation domain 이란 무엇이며, 무엇에 사용되나요?
격리 도메인은 데이터 레이스 위험 없이 값이나 참조에 접근할 수 있는 경계를 정의합니다. 종류는 다음 세 가지가 있습니다:
| 유형 | 설명 |
|---|---|
nonisolated | 기본 도메인; 동시성 제한을 적용하지 않습니다. |
actor | 각 액터 인스턴스마다 고유한 도메인을 가집니다. |
| global actor | 여러 타입, 프로퍼티, 함수가 공유하는 도메인 (예: @MainActor). |
nonisolated 코드가 다른 도메인의 상태에 대해 갖는 제한은 무엇인가요?
nonisolated는 기본 격리 도메인이며 내부 동시성 제한이 없습니다.- 같은 타입의 다른 코드의 상태는 자유롭게 수정할 수 있습니다.
- 다른 격리 도메인의 상태는 수정할 수 없습니다.
상태가 없는 메서드 예시 (어떤 스레드에서도 호출 가능):
// ⚠️ `nonisolated`는 기본 도메인이므로 중복됩니다.
nonisolated func add(a: Int, b: Int) -> Int {
a + b
}
func add(a: Int, b: Int) -> Int {
a + b
}
왜 액터의 프로퍼티에 외부에서 접근하려면 await가 필요한가요?
actor는 모든 저장 프로퍼티와 메서드가 단일 스레드 환경에서 안전하게 실행되도록 보장하여 데이터 레이스를 방지합니다.
다른 도메인에서 해당 데이터를 접근하려면 await가 필요합니다:
- 액터 컨텍스트로 전환합니다.
- 액터가 독점 권한을 해제할 때까지 기다립니다.
actor Library {
// 격리된 프로퍼티; 읽기 위해서는 `await`가 필요합니다.
var books: [String] = []
// 액터 메서드 (외부에서 호출될 때는 암시적으로 async).
func addBook(_ title: String) {
books.append(title)
}
func getBookList() -> [String] {
books
}
// `nonisolated` 메서드: `await` 없이 호출 가능.
nonisolated func libraryName() -> String {
"A library of books"
}
}
사용 예시:
let library = Library()
// ❌ 액터 메서드를 동기적으로 호출할 수 없습니다.
library.addBook("hola") // 컴파일 오류
Task {
await library.addBook("Dopamine Nation")
let books = await library.getBookList()
print(books) // ["Dopamine Nation"]
// 프로퍼티 직접 읽기 (await 필요).
await library.books.forEach { print($0) }
// ❌ 액터 외부에서 프로퍼티를 변형할 수 없습니다.
// library.books.append("Another") // 오류
}
@MainActor 같은 global actor와 일반 액터는 어떻게 다르나요?
- 일반 액터: 각 인스턴스마다 고유한 격리 도메인을 가집니다.
- 글로벌 액터: 해당 액터로 표시된 모든 인스턴스가 같은 도메인을 공유합니다 (예:
@MainActor). 여러 부분이 동일한 동시성 제한 하에서 동작해야 할 때 유용합니다.
Sendable 프로토콜에 따라 공개 API가 스레드‑안전하다고 판단되려면 어떤 조건을 만족해야 하나요?
인터페이스가 격리 도메인 간에 안전하려면:
- 공개적으로 가변 수정자를 노출하지 않아야 합니다.
- 내부적으로 자체 잠금 메커니즘을 구현해야 합니다 (예:
NSLock,DispatchQueue). - 수정자는 copy‑on‑write 방식을 사용해야 합니다 (값 타입과 유사).
표준 라이브러리의 많은 타입이 이미 Sendable을 채택하고 있습니다. 컴파일러는 모든 멤버가 Sendable인 경우 암시적으로 준수를 추론할 수도 있습니다:
// ✅ 모든 멤버가 Sendable이므로 암시적 준수
멤버는 Sendable입니다.
struct SomeStruct {
var someIntValue: Int
}
// ❌ 암시적으로 준수하지 않음; 클래스는 레퍼런스 타입입니다.
class SomeClass {
var someIntValue: Int = 0
}
낭송
-
왜
Int프로퍼티를 가진struct는 암시적으로Sendable을 준수하지만, 같은 프로퍼티를 가진class는 그렇지 않을까요?
struct는 값 타입이기 때문에 복사할 때 새로운 독립 인스턴스가 생성되어 데이터 레이스를 방지합니다. 반면class는 참조 타입이며 여러 도메인에서 동시에 변형될 수 있습니다. -
값이 두 격리 도메인 사이를 이동할 때 무슨 일이 일어나는지 직접 설명해 주실 수 있나요?
Swift는 값이Sendable인지 확인합니다.Sendable이면 컴파일러가 이동을 허용하고, 동일한 데이터에 대한 비동기 동시 접근이 없음을 보장합니다. -
언제 …가 의미가 있을까요? (필요한 상황에 맞게 계속 작성하세요).
액터 내부에서 메서드를 nonisolated 로 표시하는 방법
액터의 상태를 보호할 필요가 없을 경우 nonisolated 로 표시할 수 있습니다. 예를 들어 메서드가 스칼라 값이나 상수 값을 반환할 때 이런 경우가 해당됩니다.
검토
1. 공개 API가 동시성 도메인 간에 안전하게 사용될 수 있는 세 가지 조건은 무엇인가요?
- 불변성 – 노출된 값은 생성된 이후에 수정될 수 없습니다.
- Sendable – 관련된 모든 타입이
Sendable프로토콜을 준수합니다. - 부수 효과 없음 – API가 외부 상태에 접근하거나 수정하지 않아 스레드 간에 공유될 수 있는 상태를 변경하지 않습니다.
2. Swift 6.2에서 Sendable의 기본 동작은 어떻게 바뀌었나요?
Swift 6.2는 **nonisolated(nonsending)**을 도입하여, 함수와 클로저가 격리 경계를 넘지 않는 한 Sendable이 되지 않도록 합니다. 따라서 컴파일러가 nonisolated 컨텍스트에서 자동으로 Sendable 준수를 강제하지 않아, 전송이 필요 없는 코드와의 상호 운용성이 향상됩니다.
참고 문헌
- Van der Lee, A. (2025). Swift Concurrency Course [온라인 코스]. avanderlee.com.