async/await๋ถํฐ actor๊น์ง, ๋์์ฑ์ ์ง์ง ๋์ ์๋ฆฌ
Swift 5.5 ์ดํ ๋ฑ์ฅํ Concurrency(๋์์ฑ)๋ iOS ๊ฐ๋ฐ์ ํจ๋ฌ๋ค์์ ์์ ํ ๋ฐ๊ฟ๋์๋ค.
์ด์ ์๋ ์ฝ๋ฐฑ, completion handler, GCD(DispatchQueue)๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ ์ดํ์ง๋ง,
์ด์ ๋ ์ธ์ด ์์ค์์ ์์ ํ๊ณ ๊ฐ๊ฒฐํ๊ฒ ๋น๋๊ธฐ๋ฅผ ํํํ ์ ์๋ค.
์ด๋ฒ ๊ธ์์๋ async/await, Task, actor๋ฅผ ์ค์ฌ์ผ๋ก
"Swift ๋์์ฑ์ด ์ค์ ๋ก ์ด๋ป๊ฒ ์๋ํ๋๊ฐ"๋ฅผ ์ค๋ฌด ๊ฐ๋ฐ์ ๊ด์ ์์ ์ ๋ฆฌํ๋ ค๊ณ ํ๋ค.
์ Concurrency์ธ๊ฐ?
๊ธฐ์กด GCD ๊ธฐ๋ฐ ์ฝ๋์ ๋ฌธ์ ์ ์ ๋ช ํํ๋ค.
DispatchQueue.global().async {
let data = fetchData()
DispatchQueue.main.async {
self.label.text = data.title
}
}
- ์ฝ๋ฐฑ ์ค์ฒฉ์ผ๋ก ์ธํ ๊ฐ๋
์ฑ ์ ํ
- ์ค๋ ๋ ์์ ์ฑ ๋ณด์ฅ ๋ถ์ฌ
- ์ฝ๋ ํ๋ฆ์ด "์์ฐจ์ "์ด์ง ์์ ๋๋ฒ๊น
์ด๋ ค์
Swift Concurrency๋ ์ด๋ฅผ ์ปดํ์ผ๋ฌ ์์ค์์ ์์ ํ๊ฒ ์ ์ดํ ์ ์๋๋ก ์ค๊ณ๋์๋ค.
๋น๋๊ธฐ ํจ์ ๊ฐ์ ํธ์ถ ๊ด๊ณ๋ฅผ ๋ช
์์ ์ผ๋ก ํํํ๋ฉฐ, ๋ฐ์ดํฐ ๊ฒฝ์(Data Race)์ ์ธ์ด ์ฐจ์์์ ๋ฐฉ์งํ๋ค.
async/await ๊ธฐ๋ณธ ๊ตฌ์กฐ
func fetchUser() async throws -> User {
try await NetworkManager.shared.loadUser()
}
- async: ํจ์๊ฐ ๋น๋๊ธฐ๋ก ๋์ํ ์ ์์์ ๋ช
์
- await: ํธ์ถ ์์ ์์ ์ผ์ ์ค์ง(suspend) ๊ฐ๋ฅ์ฑ์ ๋ํ๋
์ฆ, await๋ ์ ๊น ๋ฉ์ท๋ค๊ฐ ๊ฒฐ๊ณผ๊ฐ ์ค๋น๋๋ฉด์ ๋ค์ ์ด์ด์ง๋ ๊ฒ์ด๋ค.
Swift๋ ์ด ์ค๋จ ์ง์ ์ ์๋ ๊ด๋ฆฌํ๋ฏ๋ก ๊ฐ๋ฐ์๊ฐ ์ฝ๋ฐฑ์ ์ง์ ๋ค๋ฃฐ ํ์๊ฐ ์๋ค.
Task์ Structured Concurrency
Swift์ Task๋ ๋น๋๊ธฐ ์์ ์ ๊ธฐ๋ณธ ๋จ์์ด๋ค.
Task {
let data = try await fetchData()
print(data)
}
์ฌ๊ธฐ์ ์ค์ํ ๊ฐ๋
์ Structured Concurrency์ด๋ค.
๋ถ๋ชจ Task๊ฐ ์ทจ์๋๋ฉด ํ์ Task๋ค๋ ์๋์ผ๋ก ์ทจ์๋๋ฉฐ, ๋์ ์๋ ๊ตฌ์กฐ๊ฐ ๋๋ค.
[์์] - ์ฌ๋ฌ ์์
๋ณ๋ ฌ ์คํ
await withTaskGroup(of: Int.self) { group in
for i in 1...5 {
group.addTask {
return await heavyJob(i)
}
}
let total = await group.reduce(0, +)
print("Total:", total)
}
TaskGroup์ ์์ ์์
๋ค์ ๋ฌถ์ด ๋ณ๋ ฌ ์คํํ๋ฉฐ, ๋ด๋ถ์ ์ผ๋ก suspend/resume๋ฅผ ํตํด ์๋ ์ค์ผ์ค๋ง ๋๋ค.
actor๋ก ๋ฐ์ดํฐ ๊ฒฝ์ ๋ฐฉ์งํ๊ธฐ
ํด๋์ค๋ ์ฌ๋ฌ ์ค๋ ๋์์ ๋์์ ์ ๊ทผํ๋ฉด ์ํํ์ง๋ง,
actor๋ ๋ด๋ถ ์ํ์ ๋ํ ์ ๊ทผ์ ์ง๋ ฌํ(serialize)ํ๋ค.
actor Counter {
private var value = 0
func increment() { value += 1 }
func getValue() -> Int { value }
}
- actor ๋ด๋ถ์ value๋ ์ค์ง ํ๋์ Task๋ง ์ ๊ทผ ๊ฐ๋ฅ
- ์ธ๋ถ์์ ์ ๊ทผํ ๋๋ ํญ์ await ํ์
let counter = Counter()
await counter.increment()
print(await counter.getValue())
์ฆ, actor๋ "๋น๋๊ธฐ ์์ ํด๋์ค"๋ผ๊ณ ๋ณผ ์ ์๋ค.
@MainActor๋ก UI ์์ ํ๊ฒ ์ ๋ฐ์ดํธ
UI๋ ํญ์ ๋ฉ์ธ ์ค๋ ๋์์๋ง ๋ณ๊ฒฝํด์ผ ํ๋ค.
@MainActor๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋น ํจ์๋ ํ๋กํผํฐ๊ฐ ํญ์ ๋ฉ์ธ ์ค๋ ๋์์ ์คํ๋จ์ ์ปดํ์ผ๋ฌ๊ฐ ๋ณด์ฅํ๋ค.
@MainActor
func updateUI() {
label.text = "์
๋ฐ์ดํธ ์๋ฃ"
}
์์ ์๋ DispatchQueue.main.async๋ก ๊ฐ์๋ ๋ถ๋ถ์
@MainActor๋ก ์ ์ธ๋ง ํด๋ ๋์ผํ ํจ๊ณผ๋ฅผ ์ป์ ์ ์๋ค.
์ค๋ฌด์์ ์์ฃผ ์ฐ์ด๋ ํจํด: Task ์ทจ์
API ์์ฒญ์ด ์ฐ์์ ์ผ๋ก ๋ค์ด์ฌ ๋, ์ด์ ์์ฒญ์ ์ทจ์ํ๊ณ ์๋ก์ด ์์ฒญ๋ง ์ ์งํด์ผ ํ ๋๊ฐ ๋ง๋ค.
var currentTask: Task<Void, Never>?
func loadData() {
currentTask?.cancel()
currentTask = Task {
do {
let data = try await fetchData()
await updateUI(with: data)
} catch {
if Task.isCancelled { return }
}
}
}
์ด๋ฐ ๋ฐฉ์์ผ๋ก ์ด์ ๋น๋๊ธฐ ์์
์ ์ทจ์๋ฅผ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ค.
Task ์ทจ์๋ Swift Concurrency์ ๊ตฌ์กฐ์ ํน์ฑ ๋๋ถ์ ๊ฐ๋จํ๊ฒ ์ ์ด๋๋ค.
Swift์ Concurrency๋ ๋จ์ํ ๋ฌธ๋ฒ์ด ์๋๋ผ, ์์ ํ ๋์์ฑ ๋ชจ๋ธ์ด๋ค.
์ด์ ๋ ๋น๋๊ธฐ๋ฅผ "์ค๋ ๋ ๋จ์"๊ฐ ์๋ "์์
(Task) ๋จ์"๋ก ๋ค๋ฃจ๋ ์๋์ด๋ฉฐ,
์ปดํ์ผ๋ฌ๊ฐ ๋ฐ์ดํฐ ๊ฒฝ์์ ์ฌ์ ์ ๋ง์์ฃผ๋ ์ญํ ์ ํ๋ค.
์ค๋ฌด์์๋ ์ด ํจํด๋ค์ ์กฐํฉํด์ ์๋์ ๊ฐ์ด ํ์ฅํ ์ ์๋ค.
- API ์ฐ์ ํธ์ถ ๊ด๋ฆฌ
- ViewModel์ async ์ฒ๋ฆฌ
- actor ๊ธฐ๋ฐ ์ํ ์ ์ฅ์ ๊ตฌ์ฑ
Swift์์ ๋์์ฑ์ ๋ ์ด์ ์ ํ์ด ์๋ ๊ธฐ๋ณธ๊ธฐ๊ฐ ๋ ๊ฒ ๊ฐ๋ค.
๊ธฐ์กด์ GCD๋ OperationQueue ๊ธฐ๋ฐ ์ฝ๋๋ฅผ ์ ์งํ๋ ๋์ ,
์ด์ ๋ async/await์ actor๋ก ๊ตฌ์กฐ๋ฅผ ์ค๊ณํ๋ ๊ฒ์ด ์ ์ง๋ณด์์ ์์ ์ฑ ์ธก๋ฉด์์ ํจ์ฌ ํจ์จ์ ์ด๋ผ๊ณ ์๊ฐํ๋ค.
์๋ก์ด ์ฝ๋๋ฅผ ์์ฑํ ๋๋ง๋ค "์ด ์์
์ ์ด๋ค Task๋ก, ์ด๋ค Actor์์์ ์์ ํ๊ฒ ์ฒ๋ฆฌ๋ ๊น?"๋ฅผ ์๊ฐํ๋ ์ต๊ด์ด Swift Concurrency๋ฅผ ์ ๋๋ก ์ดํดํ๊ณ ํ์ฉํ๋ ์ฒซ ๊ฑธ์์ด๋ผ๊ณ ์๊ฐํ๋ค.
์ด์ ์๋ escaping ํด๋ก์ ์ completion handler๋ก ๋น๋๊ธฐ๋ฅผ ์ฒ๋ฆฌํ๋ฉด์ ๋ณต์กํ ์ฝ๋ฐฑ ์ฒด์ธ์ ๊ฐ์ํด์ผํ์๋ค.
๊ทธ ์ด์ ๋ ๋น์์ Swift Concurreny๊ฐ ๋ฏ์ค์๊ณ , ๋ฐฐ์์ผ ํ ๊ฐ๋
์ด ๋๋ฌด ๋ง์ ์ฝ๊ฒ ๋ค๊ฐ์์ง ๋ชปํ๋ค.
'async/await๊ฐ ๋ญ๋ฐ? ์ด๋ ต๋ค. ๊ทธ๋ฅ ์๋ ๋ฐฉ์์ผ๋ก ๊ฐ๋ฐํ์'๋ผ๋ ์๊ฐ์ด ์์งํ ๋ง์์ด์๋ค.
ํ์ง๋ง ์ค์ ๋ก ์ ์ฉํด๋ณด๋, ๋ณต์กํ๋ ๋น๋๊ธฐ ํ๋ฆ์ด ๋๋ผ์ธ ์ ๋๋ก ๋จ์ํด์ง๊ณ ์ฝ๋์ ์๋๊ฐ ๋ช
ํํด์ก๋ค.
์ง๊ธ ๋์ด์ผ๋ณด๋ฉด, Swift Concurrency๋ฅผ ์ดํดํ๋ ค๋ ๊ณผ์ ์ด ๋จ์ํ ๋ฌธ๋ฒ ํ์ต์ด ์๋๋ผ
๋น๋๊ธฐ๋ผ๋ ๊ฐ๋
์์ฒด๋ฅผ ์๋กญ๊ฒ ๋ฐ์๋ค์ด๋ ๊ฒฝํ์ด์๋ ๊ฒ ๊ฐ๋ค.
๋ณต์กํ ์ฝ๋๋ฅผ ์ค์ด๋ ๋ฐ์ ๊ทธ์น์ง ์๊ณ , '์ด๋ป๊ฒ ๋ ์์ ํ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค ๊ฒ์ธ๊ฐ'๋ฅผ ์๊ฐํ๊ฒ ๋๋ฉด์
์ฑ์ ์ ์ฒด ๊ตฌ์กฐ๋ฅผ ๋ค์ ๋ฐ๋ผ๋ณด๊ฒ ๋์๋ ๊ฒ ๊ฐ๋ค.
์ดํ์ ๋ธ๋ก๊ทธ์์ ์ด ๊ตฌ์กฐ๋ฅผ ViewModel๊ณผ ๋คํธ์ํฌ ๊ณ์ธต์ ์ด๋ป๊ฒ ์ ์ฉํ ์ ์์์ง ์์ฑํด๋ณด๋ ค๊ณ ํ๋ค.
(๋ค์ ๊ธ์ ์๋. ์๋ฌดํผ ์ธ ์์ .)
'๐' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [iOS] WKWebView์์์ ์คํฌ๋กคโBounceโSafeArea ๋์ (0) | 2025.11.17 |
|---|---|
| iOS ์น๋ทฐ ์๋จ ๋ ธ์น ๋์ํ๊ธฐ (0) | 2025.11.07 |
| [Swift] Actor, isolated, nonisolated (0) | 2025.10.28 |
| [Swift] iOS ์์ฒด์ธ์ฆ ๊ถํ์ '๊ฑฐ๋ถ' ์ํ๋ฅผ ๊ฐ์งํ ์ ์๋ค? (0) | 2025.10.26 |
| [Swift] Privacy Masking(Privacy Screen) ๊ตฌํํ๊ธฐ (0) | 2025.10.19 |