0. 서론
SwiftData를 비동기 처리하려고 보면
CoreData의 await context.perform 처럼 ModelContext를 비동기처리해주는 함수가 없는 것을 확인할 수 있다.
당연한 이야기 이지만 async 키워드를 붙이고 Task 블록 내에서 호출해도 메인 스레드에서 동작한다.

그럼 어떻게 하면 비동기 컨텍스트에서 실행시킬 수 있을까?
1. ModelActor와 Actor
방법을 찾는 도중 @ModelActor라는 키워드를 발견했다. ModelActor가 그래서 뭔고?
SwiftData의 ModelContext를 비동기/동시성 환경에서 안전하게 다루기 위해 설계된 구조로,
데이터의 격리와 thread-safe한 처리를 자동으로 보장해주는 매크로

but, 이 친구를 사용하기 위해서는,,,, Actor와 같이 사용해야 한다.
그래서 Actor란?
Concurrency에서 Data Race를 방지해주는 타입, actor를 사용하면 여러 스레드에서 안전하게 데이터를 공유 가능.
- Swift 5.5에서 Swift Concurrency가 도입되면서 같이 나온 유형(class, struct, enum과 같은)
- Class 처럼 참조 타입이지만, 서브 클래싱은 지원X
- 공유자원을 격리(isolate) 시키고, 공유자원에 대해서 Serial 하게 접근하도록 처리.
- 외부에서 접근할때는 await가 필요함
사용법은 어렵지 않았다. class 대신 actor로 바꿈 된다.
또한, @ModelActor를 붙이면 actor 내에서 명시적으로 modelContext를 초기화 시켜주지 않아도 된다.
(외부에서 넘겨주긴 해야됨ㅎ)
@ModelActor
actor ModelActorExample {
func create() {
// code
}
func fetch() -> [] {
// code
}
}
2. 프로젝트 예시
간단하게 ModelActor를 활용해볼 수 있는 프로젝트를 만들어보자.
@ModelActor
actor ModelActorExample {
func createNote(content: String) {
let note = Note(content: content)
modelContext.insert(note)
try? modelContext.save()
}
func fetchNotes() -> [Note] {
let sortDescriptor = SortDescriptor(\Note.createdAt, order: .reverse)
let descriptor = FetchDescriptor<Note>(sortBy: [sortDescriptor])
guard let data = try? modelContext.fetch(descriptor) else { return [] }
return data
}
}
뷰는 간단하게 아래처럼 짜 보았다.

App에서 초기화 시에 ModelActoExample 인스턴스를 생성해주고 뷰에 주입을 했다.
@main
struct ModelActorPracticeApp: App {
private let modelActor: ModelActorExample
init() {
let storage = SwiftDataStorage()
self.modelActor = ModelActorExample(modelContainer: storage.modelContainer)
}
var body: some Scene {
WindowGroup {
ContentView(modelActor: modelActor)
}
}
}
뷰에서는 다음과 같이 처리해주었다.
struct ContentView: View {
@State private var notes: [Note] = []
@State private var text: String = ""
private let modelActor: ModelActorExample
init(modelActor: ModelActorExample) {
self.modelActor = modelActor
}
var body: some View {
VStack {
// MARK: - TextField & Button
HStack {
TextField("", text: $text)
.textFieldStyle(.roundedBorder)
Button {
Task {
await modelActor.createNote(content: text)
await MainActor.run { self.text = "" }
await updateList()
}
} label: {
Text("저장")
}.buttonStyle(.borderedProminent)
}.padding(.horizontal, 24)
// MARK: - List
List {
ForEach(notes) {
Text($0.content)
}
}
}
.task {
await updateList()
}
}
private func updateList() async {
let datas = await modelActor.fetchNotes()
await MainActor.run { self.notes = datas }
}
}
이렇게 완성하고 breakpoint를 찍어봤는데....

3. TroubleShooting... 해결!
그래서 찾아보다가 레딧에서 나와 같은 고민을 하는 글을 발견했다.
https://www.reddit.com/r/SwiftUI/comments/1fhrsn9/what_is_a_good_way_to_create_a_modelactor_ready/
From the SwiftUI community on Reddit: What is a good way to create a ModelActor ready for background use?
Explore this post and more from the SwiftUI community
www.reddit.com
But according to this video https://www.youtube.com/watch?v=VG4oCnQ0bfw it seems the ModelActor itself needs to be created in the background off the main thread.
저 유튜브를 들어가서 보니 해당 @ModelActor를 비동기 컨텍스트 내에서 생성이 되어야한다는 내용이더라....
그래서 나는 ModelActorProvider Class를 따로 만들어서 해결하였다.
@Observable
final class ModelActorProvider {
var modelActor: ModelActorExample? = nil
func createModelActor() async {
let storage = SwiftDataStorage()
let modelActor = ModelActorExample(modelContainer: storage.modelContainer)
await MainActor.run { self.modelActor = modelActor }
}
}
이렇게 만들어주고, App 에서 ModelActorExample이 생성되면 흰화면에서 ContentView로 바뀌게 했다.
@main
struct ModelActorPracticeApp: App {
@State private var modelActorProvider = ModelActorProvider()
var body: some Scene {
WindowGroup {
if let modelActor = modelActorProvider.modelActor {
ContentView(modelActor: modelActor)
} else {
Color.white.ignoresSafeArea()
.task {
await modelActorProvider.createModelActor()
}
}
}
}
}
결과는..!

다른 스레드에서 잘 동작하는 것을 확인 할 수 있었다.

해당하는 프로젝트를 깃헙에도 올렸다.
GitHub - Monfi98/ModelActor-Practice
Contribute to Monfi98/ModelActor-Practice development by creating an account on GitHub.
github.com
4. 참고
- https://velog.io/@oasis444/Actor-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0
- https://syed4asad4.medium.com/power-of-modelactor-in-swiftdata-0053651261bb
- https://www.youtube.com/watch?v=VG4oCnQ0bfw
[iOS] Actor 알아보기
Actor를 사용하면 여러 스레드에서 안전하게 데이터를 공유하고, 동시에 실행되는 코드 블록에서 Data race가 발생하지 않도록 보장할 수 있습니다.
velog.io
'iOS > Swift' 카테고리의 다른 글
| [SwiftUI] 렌더링 실험 (3) | 2025.06.25 |
|---|---|
| [Swift] @Published 뷰모델을 Combine Subject로 바꿔보자 (0) | 2025.06.23 |
| [Swift] 스위프트 기본문법#4 - 튜플(Tuple) (0) | 2023.05.11 |
| [Swift] 스위프트 기본문법#3 - 제어문 (0) | 2023.02.14 |
| [Swift] 스위프트 기본문법#2 - 문자열과 문자 (0) | 2022.10.25 |
댓글