μλ νμΈμ. μ€λμ Swiftμ λμμ±μ λν΄ μμλ³΄κ² μ΅λλ€. λμμ±μ Swift 5.5μμ λμ λ μ€μν κΈ°λ₯μΌλ‘, λΉλκΈ° μ½λλ₯Ό λ μμ νκ³ μ§κ΄μ μΌλ‘ μμ±ν μ μκ² ν΄μ€λλ€. 볡μ‘ν κ°λ μ΄μ§λ§ ν¨κ» μ°¨κ·Όμ°¨κ·Ό μ΄ν΄λ³΄λλ‘ νκ² μ΅λλ€!
π λμμ±μ΄λ? (Understanding Concurrency)
Swiftμμ λμμ±μ ν¬κ² λ κ°μ§ κ°λ μ ν¬ν¨ν©λλ€:
- λΉλκΈ°(Asynchronous) μ½λ: μ€ν μ€μ μΌμ μ€λ¨λμλ€κ° λμ€μ λ€μ μ¬κ°λ μ μλ μ½λμ λλ€. ν λ²μ νλ‘κ·Έλ¨μ ν λΆλΆλ§ μ€νλ©λλ€.
- λ³λ ¬(Parallel) μ½λ: μ¬λ¬ μμ μ΄ λμμ μ€νλ μ μλ μ½λμ λλ€. μλ₯Ό λ€μ΄, 4μ½μ΄ νλ‘μΈμλ 4κ°μ μμ μ λμμ μ€νν μ μμ΅λλ€.
λμμ±μ μ΄μ : λ€νΈμν¬ μμ², νμΌ μ²λ¦¬μ κ°μ μκ°μ΄ μ€λ 걸리λ μμ μ μννλ λμμλ UIλ₯Ό μλ΅μ± μκ² μ μ§ν μ μμ΅λλ€.
Swiftμ λμμ± λͺ¨λΈμ μ°λ λ μμ ꡬμΆλμ΄ μμ§λ§, μ§μ μ°λ λλ₯Ό κ΄λ¦¬ν νμκ° μμ΅λλ€. μμ€ν μ΄ λΉλκΈ° ν¨μμ μ€νμ μλμΌλ‘ κ΄λ¦¬ν΄μ€λλ€.
π λΉλκΈ° ν¨μ μ μνκ³ νΈμΆνκΈ° (Async Functions)
λΉλκΈ° ν¨μ μ μνκΈ°
async ν€μλλ₯Ό μ¬μ©νμ¬ λΉλκΈ° ν¨μλ₯Ό μ μν©λλ€:
func listPhotos(inGallery name: String) async -> [String] {
// λΉλκΈ° λ€νΈμνΉ μ½λ
return ["IMG001", "IMG99", "IMG0404"]
}
ν¨μκ° λΉλκΈ°μ μ΄λ©΄μ μλ¬λ₯Ό λμ§ μλ μλ€λ©΄ async throwsλ₯Ό μ¬μ©ν©λλ€:
func fetchUserData(userID: String) async throws -> UserData {
// λ€νΈμν¬ μμ² μ€ μλ¬κ° λ°μν μ μμ
}
μ€μ: asyncλ λ°ν νμ΄ν(->) μμ μμΉνκ³ , throwsκ° μλ€λ©΄ throws μμ asyncλ₯Ό μμ±ν©λλ€.
λΉλκΈ° ν¨μ νΈμΆνκΈ°
λΉλκΈ° ν¨μλ₯Ό νΈμΆν λλ await ν€μλλ₯Ό μ¬μ©νμ¬ μ μ¬μ μΈ μ€λ¨ μ§μ μ νμν©λλ€:
let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)
μ΄ μ½λμ μ€ν νλ¦μ λ€μκ³Ό κ°μ΅λλ€:
- listPhotos ν¨μ νΈμΆ μ awaitμμ μΌμ μ€λ¨λ μ μμ
- ν¨μκ° λ°νλλ©΄ sortedNamesμ nameμ κ³μ° (μΌμ μ€λ¨ μμ)
- downloadPhoto νΈμΆ μ λ€μ awaitμμ μΌμ μ€λ¨λ μ μμ
- μ΅μ’ μ μΌλ‘ show(photo) μ€ν
μ€λ¨ μ§μ (Suspension Points): awaitλ μ½λκ° μΌμ μ€λ¨λ μ μλ μ§μ μ λͺ μμ μΌλ‘ νμν©λλ€. μ΄λ° λͺ μμ νμ λλΆμ λΉλκΈ° μ½λμ νλ¦μ λ μ½κ² μ΄ν΄ν μ μμ΅λλ€.
λΉλκΈ° ν¨μλ λ€μ μμΉμμλ§ νΈμΆν μ μμ΅λλ€:
- λ€λ₯Έ λΉλκΈ° ν¨μ, λ©μλ, νλ‘νΌν° λ΄λΆ
- @main νμ μ static main() λ©μλ λ΄λΆ
- ꡬ쑰νλμ§ μμ λμμ± μμ λ΄λΆ
π λΉλκΈ° μνμ€ (Asynchronous Sequences)
컬λ μ μ μμλ₯Ό λΉλκΈ°μ μΌλ‘ νλμ© μ²λ¦¬ν΄μΌ ν λλ λΉλκΈ° μνμ€λ₯Ό μ¬μ©ν©λλ€:
import Foundation
let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
print(line)
}
for-await-in 루ν: μΌλ° for-in 루νμ λΉμ·νμ§λ§, κ° λ°λ³΅μμ λ€μ μμλ₯Ό μ¬μ©ν μ μμ λκΉμ§ κΈ°λ€λ¦΄ μ μμ΅λλ€.
μμ λ§μ λΉλκΈ° μνμ€λ₯Ό λ§λ€λ €λ©΄ AsyncSequence νλ‘ν μ½μ ꡬννλ©΄ λ©λλ€.
π λ³λ ¬λ‘ λΉλκΈ° ν¨μ νΈμΆνκΈ° (Parallel Async Calls)
μ¬λ¬ λΉλκΈ° μμ μ μμ°¨μ μΌλ‘ μ€ννλ©΄ κ° μμ μ μ΄μ μμ μ΄ μλ£λ λκΉμ§ κΈ°λ€λ¦½λλ€:
// μμ°¨μ μ€ν - κ° λ€μ΄λ‘λκ° μ΄μ λ€μ΄λ‘λ μλ£ ν μμ
let firstPhoto = await downloadPhoto(named: photoNames[0])
let secondPhoto = await downloadPhoto(named: photoNames[1])
let thirdPhoto = await downloadPhoto(named: photoNames[2])
μμ λ€μ λ³λ ¬λ‘ μ€ννλ €λ©΄ async letμ μ¬μ©ν©λλ€:
// λ³λ ¬ μ€ν - λͺ¨λ λ€μ΄λ‘λκ° λμμ μμ
async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])
// λͺ¨λ κ²°κ³Όκ° νμν λ await
let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
μμ°¨μ vs λ³λ ¬:
- λ€μ μ½λκ° μ΄μ ν¨μμ κ²°κ³Όμ μμ‘΄νλ€λ©΄ μΌλ° awaitλ₯Ό μ¬μ©νμΈμ.
- μ¬λ¬ λ 립μ μΈ μμ μ λμμ μ€ννλ €λ©΄ async letμ μ¬μ©νμΈμ.
π μμ κ³Ό μμ κ·Έλ£Ή (Tasks and Task Groups)
Swift λμμ±μ ν΅μ¬ λ¨μλ μμ (Task)μ λλ€. μμ μ λΉλκΈ°μ μΌλ‘ μ€νν μ μλ μμ λ¨μμ λλ€.
μμ κ·Έλ£ΉμΌλ‘ μ¬λ¬ μμ κ΄λ¦¬νκΈ°
μ¬λ¬ μμ μ λμ μΌλ‘ μμ±νκ³ κ΄λ¦¬νλ €λ©΄ μμ κ·Έλ£Ή(Task Group)μ μ¬μ©ν©λλ€:
await withTaskGroup(of: Data.self) { group in
let photoNames = await listPhotos(inGallery: "Summer Vacation")
for name in photoNames {
group.addTask {
return await downloadPhoto(named: name)
}
}
for await photo in group {
show(photo)
}
}
μμ κ·Έλ£Ήμμ κ²°κ³Όλ₯Ό μμ§νκ³ λ°ννλ €λ©΄:
let photos = await withTaskGroup(of: Data.self) { group in
let photoNames = await listPhotos(inGallery: "Summer Vacation")
for name in photoNames {
group.addTask {
return await downloadPhoto(named: name)
}
}
var results: [Data] = []
for await photo in group {
results.append(photo)
}
return results
}
ꡬ쑰μ λμμ±(Structured Concurrency): μμ λ€ μ¬μ΄μ λͺ μμ μΈ λΆλͺ¨-μμ κ΄κ³κ° μμ΄ μμ κ΄λ¦¬μ μ·¨μκ° λ μ¬μμ§λλ€.
μμ μ·¨μ (Task Cancellation)
Swiftλ νλ ₯μ μ·¨μ λͺ¨λΈ(cooperative cancellation)μ μ¬μ©ν©λλ€. μμ μ΄ μ·¨μλμλμ§ νμΈνλ λ°©λ²μ λ κ°μ§μ λλ€:
- Task.checkCancellation()μ νΈμΆνμ¬ μ·¨μ μ μλ¬λ₯Ό λ°μμν΄
- Task.isCancelled νλ‘νΌν°λ₯Ό νμΈνμ¬ μ§μ μ·¨μ μ²λ¦¬
let photos = await withTaskGroup(of: Optional<Data>.self) { group in
let photoNames = await listPhotos(inGallery: "Summer Vacation")
for name in photoNames {
let added = group.addTaskUnlessCancelled {
// μ·¨μ μ¬λΆ νμΈ
guard !Task.isCancelled else { return nil }
return await downloadPhoto(named: name)
}
guard added else { break }
}
var results: [Data] = []
for await photo in group {
if let photo { results.append(photo) }
}
return results
}
νλ ₯μ μ·¨μ: μμ μ΄ μ·¨μλλ©΄ μλμΌλ‘ μ€λ¨λμ§ μμ΅λλ€. λμ , μμ μ μ·¨μλμλμ§ νμΈνκ³ μ μ ν λ°μν΄μΌ ν©λλ€.
π ꡬ쑰νλμ§ μμ λμμ± (Unstructured Concurrency)
λλ‘λ νμ¬ κ΅¬μ‘°μ κ΄κ³μμ΄ μμ μ μ€νν΄μΌ ν λκ° μμ΅λλ€. μ΄λ΄ λ ꡬ쑰νλμ§ μμ λμμ±μ μ¬μ©ν©λλ€:
// νμ¬ μ‘ν°μμ ꡬ쑰νλμ§ μμ μμ
μμ±
let handle = Task {
return await add(newPhoto, toGalleryNamed: "Spring Adventures")
}
let result = await handle.value
// λΆλ¦¬λ μμ
μμ± (νμ¬ μ‘ν°μ 무κ΄)
let detachedHandle = Task.detached {
return await processPhoto(newPhoto)
}
μ£Όμ: ꡬ쑰νλμ§ μμ μμ μ μ μ°νμ§λ§, κ·Έλ§νΌ κ΄λ¦¬μ λν μ± μλ ν½λλ€.
π μ‘ν° (Actors)
μ‘ν°λ λμμ± μ½λ κ°μ λ°μ΄ν°λ₯Ό μμ νκ² κ³΅μ ν μ μκ² ν΄μ£Όλ μ°Έμ‘° νμ μ λλ€:
actor TemperatureLogger {
let label: String
var measurements: [Int]
private(set) var max: Int
init(label: String, measurement: Int) {
self.label = label
self.measurements = [measurement]
self.max = measurement
}
func update(with measurement: Int) {
measurements.append(measurement)
if measurement > max {
max = measurement
}
}
}
μ‘ν° μ¬μ© λ°©λ²:
let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
// μ‘ν°μ νλ‘νΌν°λ λ©μλμ μ κ·Όν λλ await νμ
print(await logger.max) // 25
// λ€λ₯Έ μΈ‘μ κ° μΆκ°
await logger.update(with: 30)
print(await logger.max) // 30
μ‘ν° λΆλ¦¬(Actor Isolation): μ‘ν°λ ν λ²μ νλμ μμ λ§ λ³κ²½ κ°λ₯ν μνμ μ κ·Όν μ μλλ‘ λ³΄μ₯ν©λλ€. μ΄λ₯Ό ν΅ν΄ λ°μ΄ν° κ²½μ 쑰건μ λ°©μ§ν©λλ€.
μ‘ν° λ΄λΆμμλ μμ μ νλ‘νΌν°μ μ κ·Όν λ await μμ΄ μ§μ μ κ·Όν μ μμ΅λλ€:
extension TemperatureLogger {
func convertFahrenheitToCelsius() {
// μ‘ν° λ΄λΆμμλ await μμ΄ νλ‘νΌν°μ μ κ·Ό κ°λ₯
for i in measurements.indices {
measurements[i] = (measurements[i] - 32) * 5 / 9
}
}
}
π μ μ‘ κ°λ₯ νμ (Sendable Types)
λμμ± λλ©μΈ κ°μ λ°μ΄ν°λ₯Ό μμ νκ² μ λ¬νλ €λ©΄ ν΄λΉ νμ μ΄ 'μ μ‘ κ°λ₯(Sendable)'ν΄μΌ ν©λλ€:
// μ μ‘ κ°λ₯ νμ
μ μΈ
struct TemperatureReading: Sendable {
var measurement: Int
}
// μ‘ν° λ©μλμ μ μ‘ κ°λ₯ν νμ
μ λ¬
extension TemperatureLogger {
func addReading(from reading: TemperatureReading) {
measurements.append(reading.measurement)
}
}
let logger = TemperatureLogger(label: "Tea kettle", measurement: 85)
let reading = TemperatureReading(measurement: 45)
await logger.addReading(from: reading)
λ€μκ³Ό κ°μ νμ μ μλμΌλ‘ μ μ‘ κ°λ₯ν©λλ€:
- μ μ‘ κ°λ₯ν νλ‘νΌν°λ§ κ°μ§ ꡬ쑰체
- μ μ‘ κ°λ₯ν μ°κ΄κ°λ§ κ°μ§ μ΄κ±°ν
- λ³κ²½ λΆκ°λ₯ν ν΄λμ€(μ½κΈ° μ μ© νλ‘νΌν°λ§ κ°μ§)
νμ μ λͺ μμ μΌλ‘ μ μ‘ λΆκ°λ₯νκ² νμν μλ μμ΅λλ€:
struct FileDescriptor {
let rawValue: CInt
}
// μ΄ νμ
μ μ μ‘ λΆκ°λ₯νκ² νμ
@available(*, unavailable)
extension FileDescriptor: Sendable { }
π μ 리
Swiftμ λμμ± μμ€ν μ 볡μ‘ν λΉλκΈ° μ½λλ₯Ό μμ νκ³ μ΄ν΄νκΈ° μ½κ² μμ±ν μ μλλ‘ λμμ€λλ€. μ£Όμ κ°λ μ μμ½ν΄λ³΄λ©΄:
- λΉλκΈ° ν¨μ(async/await): μ½λ μ€νμ μΌμ μ€λ¨νκ³ λμ€μ μ¬κ°ν μ μμ
- λ³λ ¬ μ€ν(async let): μ¬λ¬ λΉλκΈ° μμ μ λμμ μμνκ³ λͺ¨λ κ²°κ³Όκ° νμν λ κΈ°λ€λ¦Ό
- μμ κ³Ό μμ κ·Έλ£Ή: λμμ± μ½λμ λ¨μμ ꡬ쑰νλ κ΄λ¦¬ λ°©λ²
- μ‘ν°: λ°μ΄ν° λ μ΄μ€ μμ΄ μνλ₯Ό μμ νκ² κ³΅μ νλ λ°©λ²
- μ μ‘ κ°λ₯ νμ : λμμ± κ²½κ³λ₯Ό λμ΄ μμ νκ² μ λ¬λ μ μλ λ°μ΄ν° νμ
Swiftμ λμμ± λͺ¨λΈμ νΉν λ€μκ³Ό κ°μ μ΄μ μ΄ μμ΅λλ€:
- μ½λκ° λ μ½κΈ° μ½κ³ μ μ§λ³΄μνκΈ° μ¬μ
- μ»΄νμΌ μκ°μ λ λ§μ μ€λ₯λ₯Ό μ‘μ μ μμ
- λ°μ΄ν° λ μ΄μ€μ κ°μ λμμ± λ²κ·Έλ₯Ό λ°©μ§νλ λ° λμλ¨
- μ°λ λ κ΄λ¦¬λ₯Ό μ§μ ν νμκ° μμ
'π₯ Bread Basics > Swift' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
Swift 곡μ λ¬Έμ μ 리 - μ€μ²©λ νμ (Nested Types) (1) | 2025.04.12 |
---|---|
Swift 곡μ λ¬Έμ μ 리 - νμ μΊμ€ν (Type Casting) (0) | 2025.04.12 |
Obj-C 곡μ λ¬Έμ μ 리 - κ°μ²΄, ν΄λμ€, λ©μμ§ (0) | 2025.04.11 |
Swift 곡μ λ¬Έμ μ 리 - λ§€ν¬λ‘ (Macros) (0) | 2025.04.11 |
Swift 곡μ λ¬Έμ μ 리 - μλ¬ μ²λ¦¬ (Error Handling) (0) | 2025.04.11 |
Swift 곡μ λ¬Έμ μ 리 - μ΅μ λ 체μ΄λ (Optional Chaining) (0) | 2025.04.11 |
Swift 곡μ λ¬Έμ μ 리 - μ΄κΈ°ν ν΄μ (Deinitialization) (1) | 2025.04.11 |
Swift 곡μ λ¬Έμ μ 리 - μ΄κΈ°ν (Initialization) (0) | 2025.04.11 |