์ด์ Async/await ์ธ์ ์ ๋ณด์๋๋ฐ์.
Async ํจ์๋ suspend ๋ ์ ์๊ณ , suspend ๋๋ฉด ํธ์ถํ ํจ์๋ suspend ๋๊ธฐ ๋๋ฌธ์
async ํค์๋๊ฐ ๋ถ์ ํจ์๋ฅผ ์คํํ๋ ค๋ฉด
ํด์ผ๋ง ํฉ๋๋ค.
๊ทธ ์ค Async Task Block์ ๊ดํด ์์๋ณด๊ธฐ ์ํด์ ์ค๋์ ์ด ์ธ์
์ ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
Explore structured concurrency in Swift
์ธ์
์์์ ์, Concurrency๋ ์ ๋ฒ ๊ธ์์ ์ ๋ฆฌํ ์ฉ์ด ์
๋๋ค.
Queue(์์
๋๊ธฐ ํ๋ ฌ)์ ์์
์ฒ๋ฆฌ ๋ฐฉ์์ ๊ดํ ์ฉ์ด ์ค ํ๋์์ฃ !
Concurrency๋ ๋์ ์คํ ๋๋ค๋ ๋ป์ผ๋ก,
ConcurrentQueue๋ ๋ค์ด์จ ์์
์ ์ฌ๋ฌ๊ฐ์ ๋ค๋ฅธ Thread์์ ๋์์ ์คํ ์ํต๋๋ค.
Swift 5.5 ๋ถํฐ structured concurrency(๊ตฌ์กฐํ๋ ๋์์ฑ) ์ด๋ผ๋ ๊ฐ๋ ์ ์ฌ์ฉํ์ฌ concurrent Program(๋์์ฑ ํ๋ก๊ทธ๋จ)์ ์์ฑํ๋ ์๋ก์ด ๋ฐฉ๋ฒ์ ์๊ฐํฉ๋๋ค.
structured Programming.. ๊ตฌ์กฐ์ ํ๋ก๊ทธ๋๋ฐ์ด๋ ๋ฌด์์ผ๊น?
์ปดํจํ
์ด๊ธฐ์๋ control-flow(์ ์ด ํ๋ฆ)๊ฐ ๋ชจ๋ ๊ณณ์์ ์ด๋ํ ์ ์๋ ์ผ๋ จ์ ๋ช
๋ น์ผ๋ก ํ๋ก๊ทธ๋๋ฐ์ด ๋์๊ธฐ ๋๋ฌธ์ ํ๋ก๊ทธ๋จ์ ์ฝ๊ธฐ๊ฐ ์ด๋ ค์ ์ต๋๋ค.
ํ์ง๋ง! ์์ฆ์ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ ์ธ์ด๊ฐ control-flow(์ ์ด ํ๋ฆ)์ ๋ณด๋ค ๊ท ์ผํ๊ฒ ๋ง๋ค๊ธฐ ์ํด structured Programming(๊ตฌ์กฐํ๋ ํ๋ก๊ทธ๋๋ฐ)์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋์ด์ ์ ๋ ์ง ์์ต๋๋ค.
์๋ฅผ๋ค๋ฉด..
if ๋ธ๋ก์ ๊ตฌ์กฐํ๋ ํ๋ก๊ทธ๋๋ฐ์ ์ฌ์ฉํฉ๋๋ค.
์ค์ฒฉ๋ ์ฝ๋ ๋ธ๋ฝ์ด ์ -> ์๋๋ก ์ด๋ํ๋ ๋์์๋ง ์กฐ๊ฑด๋ถ๋ก ์คํ๋๋๋ก ์ง์ ํฉ๋๋ค.
Swift ์์, then Block์ static scoping(์ ์ ๋ฒ์ ์ง์ )๋ ์ค์ํฉ๋๋ค.
์ฆ, ๋ณ์๋ช
์ด ๋ธ๋ก์ ์ ์๋ ๊ฒฝ์ฐ์๋ง ๋ณ์๋ช
์ผ๋ก ํ์๋ฉ๋๋ค. ๋ํ, ๋ธ๋ก์์ ์ ์๋ ๋ชจ๋ ๋ณ์์ ์๋ช
์ ๋ธ๋ก์ด ๋ ๋ ๋ ์ข
๋ฃ๋ฉ๋๋ค.
๊ฒฐ๋ก ์, static scope(์ ์ ๋ฒ์)๋ฅผ ์ฌ์ฉํ structured programming์ control-flow(์ ์ด ํ๋ฆ)๊ณผ variable lifetime(๋ณ์ ์๋ช )์ ์๊ธฐ ์ฝ๊ฒ ๋ง๋ญ๋๋ค.
๋ณด๋ค ์ผ๋ฐ์ ์ผ๋ก, ๊ตฌ์กฐํ๋ ์ ์ดํ๋ฆ์ ์์ฐ์ค๋ฝ๊ฒ ์์๋ฅผ ์ง์ ํ๊ณ ํจ๊ป ์ค์ฒฉ๋ ์ ์์ต๋๋ค.
์ด๊ฒ์ด Structured Programming์ ๊ธฐ์ด ์
๋๋ค.
์ฐ๋ฆฌ๋ ๊ดํธ(static scope)๋ฅผ ์ด๊ณ Top-down์ผ๋ก ์ฝ๋๋ฅผ ์ฝ์ผ๋ฉด์ ์ฌ๋ฌ scope๋ค์ ์๋ค๊ฐ๋ค ์ฝ๋๊ฒ์ด ๋งค์ฐ ์ง๊ด์ ์ด๊ณ ๋น์ฐํ๊ฒ ์ฌ๊ธฐ๊ณ ์์ต๋๋ค!
ํ์ง๋ง..๐ฑ
์์ฆ์ ํ๋ก๊ทธ๋๋ฐ์ asynchronous(๋น๋๊ธฐ์ ์์
์์) + concurrent(๋์์ ์ฌ๋ฌ ์์
์คํ) ๋ฅผ ํน์ง์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ structured Programming์ ๋ ์ฝ๊ฒ ์์ฑํ ์ ์์์ต๋๋ค.
์๋๋ฉด ์ด๋ ํ ๊ตฌ์กฐ๋ฅผ ์ด๋ ํ ์์์ ๋ง๊ฒ control-flow๊ฐ ์๊ฒจ๋์ผ ํ๋๋ฐ... ๋น๋๊ธฐ์ ๋์์ฑ์ ๊ทธ๊ฑธ ์ ๊ฒฝ์ฐ์ง ์์ผ๋๊น์
Asynchronous ํ ์ฝ๋๋ฅผ ์ด๋ป๊ฒ structued programming ์ผ๋ก ๋ฐ๊พธ๋์ง ๋ด ์๋ค.
/*
Asynchronous code with completion Handlers is unstructured
*/
func fetchThumbnail(for ids: [String], completion handler: @escaping ([String: UIImage]?, Error?) -> Void) {
guard let id = ids.first else {
handler([:], nil)
return
}
let request = URLRequest(url: URL(string: id)!)
let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
if let error {
handler(nil, error)
} else if (response as? HTTPURLResponse)?.statusCode != 200 {
handler(nil, HyunndyError.noImage)
} else {
// MARK: 3๋ฒ
guard let image = UIImage(data: data!) else {
handler(nil, HyunndyError.noImage)
return
}
// MARK: 4๋ฒ
image.prepareThumbnail(of: CGSize(width: 40.0, height: 40.0), completionHandler: { thumbnail in
guard let thumbnail else {
handler(nil, HyunndyError.noImage)
return
}
fetchThumbnail(for: Array(ids.dropFirst()), completion: { thumbnails, error in
// .... ad image to thumbnails .....
})
})
}
})
task.resume()
}
์ด ์ฝ๋๋ structured ํ์ง ์์ต๋๋ค.
์๋?
CompletionHandler๋ฅผ ์ฌ์ฉํ๋ ํจํด์ ๊ฒฐ๊ณผ๋ก,
์ด ํจ์๋ error Handling์ ์ํด ๊ตฌ์กฐํ๋ ์ ์ด ํ๋ฆ(structured control-flow)๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์๋๋ฉด CompletionHandler๋ ๊ทธ๋ฅ throw๋ Error๋ฅผ ์ฒ๋ฆฌํ๋๊ฒ ์๋ฏธ์๊ธฐ ๋๋ฌธ์
๋๋ค.
(๋ญ๊ฐ ์ ์ดํ๋ฆ์ผ๋ก์จ ๊ด๋ฆฌ๋ฅผ ํ๋ค๋๊ฒ ์๋๋ผ ๊ทธ๋ฅ Error๋ฅผ ๋์ง๊ณ ๊ทธ๊ฒ๋ง ์ฒ๋ฆฌํ๋ค์ ์๋ฏธ์ธ ๊ฒ ๊ฐ์ต๋๋ค)
๋..
์ ํจ์๋ Array์์ ๊ฐ๋๋ค์ด์๋ url๊ณผ ๋งค์นญ๋๋ ๋์
๋๋ฆฌ๋ฅผ ๋ง๋ค์ด์ CompletionHandler์ ๋๊ฒจ์ค์ผํ๋๋ฐ, ๊ทธ๋ฌ๋ ค๋ฉด ์ค์ฒฉ์ ์จ์ผํฉ๋๋ค.
ํ์ง๋ง? CompletionHandler์ ํํ ๋๋ฌธ์ ์ฌ๊ท ํจ์๋ฅผ ์ฐ๊ณ ์์ง ๋ชปํฉ๋๋ค.
์ ์ด ํ๋ฆ์ด ๋ง๋ค์ด์ง์ง ์๋๋ค๋ ์๋ฆฌ์ง์.
์ด์ ์ด๊ฑธ Structured Programming์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ Async/await ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋๋ก ๋ค์ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
func fetchThumbnail2(for ids: [String]) async throws -> [String: UIImage] {
var thumbnails: [String: UIImage] = [:]
for id in ids {
let request = URLRequest(url: URL(string: id)!)
let (data, response) = try await URLSession.shared.data(for: request)
try validateResponse(response)
guard let image = await UIImage(data: data)?.byPreparingThumbnail(ofSize: CGSize(width: 40.0, height: 40.0)) else {
throw HyunndyError.noImage
}
thumbnails[id] = image
}
return thumbnails
}
TA-DA-!
์ฐ๋ฆฐ ์ด๋ฏธ Meet Async/await์ ๋ณด๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์ค๋ช
ํ์ง ์๊ฒ ์ต๋๋ค.
์ฌ๊ธฐ์ ์ถ๊ฐ๋ก..
์ด์ ! ์ฐ๋ฆฌ๋ concurrency๋ฅผ ์ถ๊ฐํ ์ ์์ผ๋ฏ๋ก, ์ฌ๋ฌ ๋ค์ด๋ก๋๊ฐ ๋ณ๋ ฌ(parrel)๋ก ๋ฐ์ํ ์ ์์ต๋๋ค.
์ฐ๋ฆฌ๋ ํ๋ก๊ทธ๋จ์ Concurrency(๋์์ฑ)์ ์ถ๊ฐํ๊ธฐ ์ํด Additional Task๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
Task๋ async ํจ์์ ํจ๊ป ๋์ํ๋ Swift์ ์ ๊ธฐ๋ฅ ์
๋๋ค.
asyncํ ์ฝ๋๋ฅผ ์คํํ๊ธฐ ์ํ Freshํ execution(์คํ) context๋ฅผ ์ ๊ณตํฉ๋๋ค.
๊ฐ Task๋ ๋ค๋ฅธ execution Task์ ๊ด๋ จํ์ฌ concurrently(๋์์) ์คํ๋ฉ๋๋ค.
์ฆ, Task๋ App์ ์คํ ์ปจํ ์คํธ(execution contenxt, ์์ ํ๋ก์ฐ)๋ฅผ ๋ํ๋ ๋๋ค.
์์ ํ๊ณ ํจ์จ์ ์ธ ํ์ด๋ฐ์ parallel(๋ณ๋ ฌ)๋ก ์คํ๋๋๋ก ์๋์ผ๋ก ์ค์ผ์ฅด๋ง ๋ฉ๋๋ค.
Async ํจ์๋ฅผ ํธ์ถํด๋ ์๋ก์ด Task๊ฐ ๋ง๋ค์ด์ง์ง ์๋๋ค๋๊ฑธ ์ ์ํด์ผ ํฉ๋๋ค! Task๋ ๋ช
์์ ์ผ๋ก ์์ฑํด์ผ ํฉ๋๋ค.
structed concurrency๋ ์ ์ฐ์ฑ๊ณผ ๋จ์์ฑ ์ฌ์ด์ ๊ท ํ์ ๊ดํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ Swift์๋ ๋ช๊ฐ์ง ๋ค๋ฅธ Task๊ฐ ์์ต๋๋ค.
ํจ๊ป ๋ณด์์ฃ !
Async-let ๋ฐ์ธ๋ฉ์ด๋ผ๋ ์๋ก์ด ๊ตฌ๋ฌธ ํ์์ผ๋ก ์์ฑ๋๋ ๊ฐ์ฅ ๊ฐ๋จํ Task ์ ๋๋ค.
๋จผ์ ๊ธฐ๋ณธ์ ์ธ let Binding์ ๋ค์๊ณผ ๊ฐ์ Flow๋ก ์ด๋ฃจ์ด์ง๋๋ค.
์ด Flow์์ ๋ค์ด๋ก๋ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ์ ์์ผ๋ ํ๋ก๊ทธ๋จ์ด ๋ค์ด๋ก๋๋ฅผ ์์ํ๊ณ ๋ฐ์ดํฐ๊ฐ ์ค์ ๋ก ํ์ํ ๋ ๊น์ง ๋ค๋ฅธ ์์ ์ ํ๊ธธ ์ํฉ๋๋ค.
์ฌ๊ธฐ์!! ์ฐ๋ฆฌ๋ ๊ธฐ์กด let ์์ async๋ง ๋ถ์ฌ์ฃผ๋ฉด ๋ฉ๋๋ค.
์ด๋ฐ ํํ๋ฅผ async-let concurrent binding ์ด๋ผ๊ณ ํฉ๋๋ค.
concurrent binding๊ณผ sequential binding์ ๋งค์ฐ ๋ค๋ฆ ๋๋ค!
concurrent binding์ ํ๊ฐํ๊ธฐ ์ํด, Swift๋ new child Task๋ฅผ ์์ฑํฉ๋๋ค.
์ด ์์
์ ํธ์ถํ(์์ฑํ) Task์ ํ์ Task ์
๋๋ค.
๋ชจ๋ Task๋ App์ ์คํ ์ปจํ ์คํธ์ด๋ฏ๋ก, 2๊ฐ์ ํ์ดํ๊ฐ ํ ๋ฒ์ ๋์ต๋๋ค.
Child Task๊ฐ Concurrent(๋์)์ ๋ค์ด๋ก๋๋ฅผ ํ๋ ๋์,
Parent Task๋ Following statements(๋ค์ ๋์)๋ฅผ ์ํํฉ๋๋ค.
ํ์ง๋ง!!!! ๊ฒฐ๊ณผ์ ์ค์ ๊ฐ์ด ํ์ํ statements์ ๋๋ฌํ๋ฉด Parent Task๋ placeholder์ ์ถฉ์กฑํ๋ Child Task์ ์์
์ด ์๋ฃ๋ ๋ ๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค!
๋ ๊ฐ์ ์๋ก ๋ค๋ฅธ URL์์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ด๋ก๋ํ๋ ์ฝ๋๋ฅผ ๋ณด๋ฉฐ async-let concurrent binding์ ๋ํด ๋ด ์๋ค.
/// Structured Concurrency with seuquential binding
func fetchOneThumbnail(withId id: String) async throws -> UIImage {
let imageReq = URLRequest(url: URL(string: id)!), metaReq = URLRequest(url: URL(string: id)!)
let (data, _) = try await URLSession.shared.data(for: imageReq)
let (metadata, _) = try await URLSession.shared.data(for: metaReq)
guard
let size = parseSize(from: metadata),
let image = await UIImage(data: data)?.byPreparingThumbnail(ofSize: size)
else {
throw HyunndyError.noImage
}
return image
}
let์ ์ค๋ฅธ์ชฝ์ try await์ ์ฌ์ฉํ๋ Sequential Binding(์์ฐจ ๋ฐ์ธ๋ฉ) ์
๋๋ค.
try await์ ํ๋์ชฝ์์ Error๋ Suspend๊ฐ Observing ๋๋ ์์น์ด๊ธฐ ๋๋ฌธ์
๋๋ค.
์ฌ๊ธฐ์๋ download๊ฐ ์์ฐจ์ ์ผ๋ก ์งํ๋ฉ๋๋ค.
ํ์ง๋ง ์ฐ๋ฆฐ ์! ๋ค์ด๋ก๋๋ฅผ ๋์์(Concurrently) ์ํํ๊ณ ์ถ์ต๋๋ค.
Concurrent๋ฅผ ๊ตฌํํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ์ฃ ? Task๋ฅผ ์ถ๊ฐํด์ผํฉ๋๋ค.
์ฐ๋ฆฌ๋ ๋ฐฉ๊ธ ๋ญ๋ฐฐ์ ์ฃ ? async-let Task์!
/// Structured Concurrency with async-let concurrent binding
func fetchOneThumbnail2(withId id: String) async throws -> UIImage {
let imageReq = URLRequest(url: URL(string: id)!), metaReq = URLRequest(url: URL(string: id)!)
/// async let์ ์ฐ๋ฉด ์ด์ ๋ค์ด๋ก๋๊ฐ Child Task์์ ๋ฐ์ํ๋ฏ๋ก ๋์ด์ ์ค๋ฅธ์ชฝ์ try await์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
/// ํ์ Task, ์์ Task์์ ๋์์ ๋ฐ์ธ๋ฉ๋ ๋ณ์๋ฅผ """์ฌ์ฉํ ๋"""" Parent Task์์๋ง Error๋ suspend๊ฐ Observing ๋ฉ๋๋ค.
async let (data, _) = URLSession.shared.data(for: imageReq)
async let (metadata, _) = URLSession.shared.data(for: metaReq)
/// ๋ฐ๋ผ์ ์ฌ์ฉํ๋ ๋ถ๋ถ์ try await์ ๋ถ์
๋๋ค.
guard
let size = parseSize(from: try await metadata),
let image = try await UIImage(data: data)?.byPreparingThumbnail(ofSize: size)
else {
throw HyunndyError.noImage
}
return image
}
async-let์ ํตํด concurrency Binding์ผ๋ก ๋ฐ๋ ์ฝ๋ ์ ๋๋ค.
์ด์ ๋ค์ด๋ก๋๋ Child Task๋ค์์ ๋์์ ๋ฐ์ํฉ๋๋ค.
๋ฐ๋ผ์ Error๋ Suspend๊ฐ Child Task์์ ๋ฐ์ํ์ง ์๊ธฐ ๋๋ฌธ์, async let์ ๋ถ์ธ ๋ณ์ ์์์ try await์ ์ง์์ค๋๋ค.
๊ทธ๋ผ ์ด๋ ๋ฃ์ด์ฃผ๋๋?
์์ Flow ๊ธฐ์ต๋์์ฃ ?
Parent Task์ Child Task๊ฐ ๋์์ Bindingํ ๊ฐ์ ์ค์ ๋ก ์ฐ๋ ๋ถ๋ถ์์ Error๋ Suspend๋ฅผ Observe ํ๊ณ ์์ต๋๋ค.
๋ฐ๋ผ์ ๊ฐ์ ์ค์ ๋ก ์ฌ์ฉํ๋
guard
let size = parseSize(from: try await metadata),
let image = try await UIImage(data: data)?.byPreparingThumbnail(ofSize: size)
์ด๊ณณ์ ๋ฃ์ด์ค๋๋ค.
์ฌ๊ธฐ๊น์ง ๋งํ Child Task๋ ์ค์ ๋ก Task Tree ๋ผ๋ ๊ณ์ธต ๊ตฌ์กฐ์ ์ผ๋ถ์
๋๋ค.
ํ๋์ Asynchronous ํจ์์์ ๋ค๋ฅธ ํจ์๋ก ํธ์ถํ ๋ ๋ง๋ค, ๋์ผํ Task๊ฐ ํธ์ถ์ ์คํํ๋๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
/// Structured Concurrency with async-let concurrent binding
func fetchOneThumbnail2(withId id: String) async throws -> UIImage {
let imageReq = URLRequest(url: URL(string: id)!), metaReq = URLRequest(url: URL(string: id)!)
/// async let์ ์ฐ๋ฉด ์ด์ ๋ค์ด๋ก๋๊ฐ Child Task์์ ๋ฐ์ํ๋ฏ๋ก ๋์ด์ ์ค๋ฅธ์ชฝ์ try await์ ์ฌ์ฉํ์ง ์์ต๋๋ค.
/// ํ์ Task, ์์ Task์์ ๋์์ ๋ฐ์ธ๋ฉ๋ ๋ณ์๋ฅผ """์ฌ์ฉํ ๋"""" Parent Task์์๋ง Error๋ suspend๊ฐ Observing ๋ฉ๋๋ค.
async let (data, _) = URLSession.shared.data(for: imageReq)
async let (metadata, _) = URLSession.shared.data(for: metaReq)
/// ๋ฐ๋ผ์ ์ฌ์ฉํ๋ ๋ถ๋ถ์ try await์ ๋ถ์
๋๋ค.
guard
let size = parseSize(from: try await metadata),
let image = try await UIImage(data: data)?.byPreparingThumbnail(ofSize: size)
else {
throw HyunndyError.noImage
}
return image
}
๋ฐ๋ผ์, fetchOneThumbnailํจ์๋ ํด๋น Task์ ๋ชจ๋ attribute๋ฅผ ์์ํฉ๋๋ค.
async-let๊ณผ ๊ฐ์ด ์๋ก์ด Task๋ฅผ ๋ง๋ค ๋, ํ์ฌ ํจ์๊ฐ ์คํ์ค์ธ Task์ ์์์ด ๋ฉ๋๋ค.
Task๋ ํน์ function์ child๋ ์๋์ง๋ง, ๊ทธ๋ค์ ์๋ช
์ ํด๋น function์ ๋ฒ์๋ก ์ง์ ๋ ์ ์์ต๋๋ค.
Task Tree๋ ํธ๋ฆฌ๊ตฌ์กฐ์ด๊ธฐ ๋๋ฌธ์ ํ์ Task๊ฐ ๋ชจ๋ ์๋ฃ๋์ด์ผ๋ง ์์ Task๊ฐ ์๋ฃ๋ ์ ์์ต๋๋ค.
์ด ๊ท์น์ ์์ Task๊ฐ awaited๋์ง ์๋๋ก ํ๋ ๋น์ ์์ ์ธ ์ ์ด ํ๋ฆ์๋ ์ ์ฉ๋ฉ๋๋ค.
์ด๊ฒ ๋ญ๋ง์ด์ผ?
guard
let size = parseSize(from: try await metadata),
let image = try await UIImage(data:
์ฝ๋๋ฅผ ๋ณด๋ฉด, data๋ฅผ ๊ฐ์ ธ์ค๋ ๋ค์ด๋ก๋๋ฅผ ๋จผ์ ์์ํ๋๋ฐ, metaData ์์
๋ถํฐ ๊ธฐ๋ค๋ฆฝ๋๋ค.
๊ทผ๋ฐ ๋ง์ฝ data๋ฅผ ๋ค์ด๋ก๋ํ๋๋ฐ์ ์ค๋ฅ๊ฐ ๋๋ฉด ์ด์ฉ๊น์?
Parent Task๋ data์์ ์ค๋ฅ๊ฐ ๋๋, metaData ์์
์ ๊ทธ๋ฅ ์ทจ์ ์ํ๋ก ๋ง๋ค์ด๋ฒ๋ฆฐ๋ค์ fetchThumbnail ํจ์๊ฐ ๋๋ ๋ ๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค.
Task๋ฅผ Cancel๋ก ๋งํนํด๋ Stop ์ํค์ง ์์ต๋๋ค. ๊ทธ๋ฅ ๊ฒฐ๊ณผ๊ฐ ์ด์ ํ์ํ์ง ์๋ค๊ณ ํฉ๋๋ค.
์์ Task๊ฐ ์คํจํ๋ฉด ํ์ Task๋ ์๋์ผ๋ก ์ทจ์๋ฉ๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก data๋ฅผ ๋ค์ด๋ก๋ ํ๋ Task๊ฐ ์๋ฌ๋ฅผ ๋ฐ์์ํค๋ฉด, ํด๋น ์์
์ Cancel๋ก ํ์๋ฉ๋๋ค.
fetchThumnnail() ํจ์๋ ๋ง์ง๋ง์ ์๋ฌ๋ฅผ ๋ฐ์์ํค๋ฉด์ ์ข
๋ฃ๋ฉ๋๋ค.
์.. ์ด๋ฐ
(์ดํดํ๊ฒ ๋ง๋์ง ๋ชจ๋ฅด๊ฒ ์ง๋ง) ์ด ๋ณด์ฅ์ structured Concurrency์ ๊ธฐ๋ณธ์
๋๋ค.
ARC๊ฐ ๋ฉ๋ชจ๋ฆฌ ์๋ช
์ ์๋์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก Task์ ์๋ช
์ฃผ๊ธฐ๋ ์๋์ผ๋ก ๊ด๋ฆฌํฉ๋๋ค.
์ฌ๊ธฐ๊น์ง Cancellation์ ์ ํ์ ๊ดํ ๋ด์ฉ์
๋๋ค.
ํ์ง๋ง ์ธ์ Task๊ฐ Stop ๋ฉ๋๊น?
Task๊ฐ ํธ๋์ญ์
์ค์ด๊ฑฐ๋, ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ์ด๋ ค์๋ ๊ฒฝ์ฐ Stop ์ํค๋๊ฑด ์ข์ง ์์ต๋๋ค.
-> ์๊ฒ ๋ฐ๋ก Cancellation์ด cooperativeํ ์ด์ ์
๋๋ค.
Cancellation..๊ทธ๋.
Task๊ฐ ์ด๋ป๊ฒ๋ Error๊ฐ ๋์ Cancel ๋ ์ ์๋ค๊ณ ํฉ์๋ค.
๊ทธ๋ผ ์ด๊ฑธ ์ฐ๋ฆฌ๊ฐ ์ด๋ป๊ฒ ์ถ์ ํ ๊ฒ์ธ๊ฐ!
/// Cancellation checking
func fetchThumbnails(for ids: [String]) async throws -> [String: UIImage] {
var thumbnails: [String: UIImage] = [:]
for id in ids {
/// Throws an error if the task was canceled.
/// Task๊ฐ ์ทจ์๋ ๊ฒฝ์ฐ Error์ ๋ฐ์์์ผ for ๋ฃจํ๋ฅผ ํ์ถ์ํต๋๋ค.
/// Task๊ฐ cancel๋ ๊ฒฝ์ฐ ์ธ๋ชจ์๋ ํต์ ์ ๊ณ์ํด์ App์ด crash ๋๊ฒ ๋
๋๊ณ ์ถ์ง ์์ต๋๋ค.
try Task.checkCancellation()
if Task.isCancelled { break }
thumbnails[id] = try await fetchOneThumbnail(withId: id)
}
return thumbnails
}
try Task.checkCancellation()
if Task.isCancelled
๋ฑ์ผ๋ก Task๊ฐ Cancel๋ ๊ฒ์ ๊ฐ์งํ์ฌ ๋ฌด์๋ฏธํ Task ์คํ์ ํ์ง ์์ ์๋ ์์ต๋๋ค.
์..์์งํ ์ฌ๊ธฐ์๋ถํฐ ์ฝ๊ฐ ๋ฉ๋ถ์์ต๋๋ค.
๋ ๊ทธ๋ฅ...Async/await ์ธ์
์ ๋์๋ Task { } ๊ฐ ๊ถ๊ธํ์ ๋ฟ์ธ๋ฐ...
์ ? ใ
ใ
์ด๊ฒ ๋ชจ์ผ? ใ
ใ
์คํค ์ด๊ธ ์ฝ์ค ๋ด๋ ค์๋๋ฐ ๊ฐ์๊ธฐ ์ค๊ธ์ฝ์ค ๋ด๋ ค๊ฐ๋ผ๋ ๊ธฐ๋ถ..?
์๋ฌดํผ ์ฌ๊ธฐ์๋ถํฐ๋ ์ดํด๋ณด๋จ ์ ๋ง..๊ทธ๋ฅ WWDC ๋ด์ฉ์ ๋์ ์ฐ๊ฒจ๋ฃ๊ธฐ ์ํด ์ด๊ฑฐ์ง๋ก ํ์ตํ ๋ด์ฉ์ ๋์ดํฉ๋๋ค..
async-let Task๋ ์ฌ์ฉ๊ฐ๋ฅํ concurrency๊ฐ ๊ณ ์ ๋์ด์์ ๋ ์ฌ์ฉํฉ๋๋ค.
์๋ฅผ๋ค์ด์..
/// Async-let is for concurrency with static width
func fetchThumbnails3(for ids: [String]) async throws -> [String: UIImage] {
var thumbnails: [String: UIImage] = [:]
for id in ids {
thumbnails[id] = try await fetchOneThumbnail2(withId: id)
}
return thumbnails
}
์ด ํจ์์์๋
2๊ฐ์ Child Task๊ฐ ์์ฑ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์ ๋ฃจํ๊ฐ ๋๊ธฐ ์ ์ ์ด 2๊ฐ์ Task๊ฐ ์๋ฃ๋์ผํฉ๋๋ค.
ํ์ง๋ง..
์ฐ๋ฆฌ๊ฐ ๋ชจ๋ ์ธ๋ค์ผ์ ๊ทธ๋ฅ ํ ๋ฒ์ ๋ค์ด๋ก๋ํ๊ณ ์ถ๋ค๋ฉด?
์ฌ๊ธฐ์๋ ids๋ผ๋ ์ ์ ๋ฐฐ์ด์ด ์์์ง๋ง, ๋ฃจํ๊ฐ ๋ช ๊ฐ ๋์ง๋ ๋ชจ๋ฅด๊ณ ๋ง์ฝ ๊ทธ๊ฒ ์์ฒญ ๋ง์์ง๋ค๋ฉด?
์ด๋ฐ ์ํฉ์ ๋์ํ ์ ์๋๊ฒ Task Group ์ ๋๋ค.
Task Group์ dynamicํ ์์ concurrency๋ฅผ ์ ๊ณตํ๋๋ก ์ค๊ณ๋ structured concurrency์ ํ ํํ์ ๋๋ค.
Task Group์ withThrowingTaskGroup ํจ์๋ก ํธ์ถํ์ฌ ๋ง๋ค ์ ์์ต๋๋ค.
func taskGroupFetchThumbnail(for ids: [String]) async throws -> [String: UIImage] {
var thumbnail: [String: UIImage] = [:]
try await withThrowingTaskGroup(of: Void.self, body: { group in
for id in ids {
group.addTask {
thumbnail[id] = try await self.fetchOneThumbnail2(withId: id)
}
}
})
}
withThrowingTaskGroup ํจ์๋
Error์ Throw ํ ์ ์๋ Child Task๋ฅผ ๋ง๋ค๊ธฐ ์ํด ๋ฒ์๊ฐ ์ง์ ๋ group object๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
Group { } ์ ๋๊ฐ๋ฉด Task์ ์๋ช
์ด ๋คํ๊ณ ,
๋ธ๋ก ๋ด๋ถ ์์ for-in loop๋ฅผ ๋ฐฐ์นํ๊ธฐ ๋๋ฌธ์ dynamiํ ์์ Task๋ฅผ ๋์์ ์์ฑํ ์ ์์ต๋๋ค. ์์์ ์๊ด์์ต๋๋ค.
ํ์ง๋ง ์ ์ฝ๋๋ ์ปดํ์ผ ์๋ฌ๋ฅผ ๋ฐ์ํฉ๋๋ค.
thumbnail ๋์
๋๋ฆฌ๋ ํ ๋ฒ์ ๋ ๊ฐ ์ด์์ ์ก์ธ์ค๋ฅผ ์ฒ๋ฆฌํ ์ ์๋๋ฐ Task Group ์์์๋ ๋์์ Task๊ฐ ๋ง์ด ์๊ฒจ๋๋ฏ๋ก data race ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ๋ ๊ฒ ์
๋๋ค!
๊ณผ๊ฑฐ์๋ ์ด ์ค๋ฅ๋ค์ ์ง์ ์ฐพ์์ด์ผ ํ๋๋ฐ, ์ง๊ธ์ ์ปดํ์ผ๋ฌ๊ฐ ์๋ ค์ค๋ค๋๋ฐ์.
์ด๊ฒ ๋๋ถ ์
๋๋ค.
data-race safety ๋ผ๋ ๊ฐ๋
์ธ๋ฐ..
Task์ ์์ฑ์ @Sendable ํด๋ก์ ์์ ์คํ๋ฉ๋๋ค.
@Sendable ํด๋ก์ ๋ณธ๋ฌธ์ ๋ณ๊ฒฝ ๊ฐ๋ฅํ ๋ณ์๋ฅผ ์บก์ณํ๋๊ฒ์ด ์ ํ๋ฉ๋๋ค.
์? Task๊ฐ ์คํ๋ ํ ์์ ๋ ์ ์์ผ๋๊น์.
์ด ๋ง์... Task ์์์ ์บก์ณ๋ ๊ฐ์ ๊ณต์ ํ๊ธฐ์ ์์ ํ value Type, actors, ์์ฒด ๋๊ธฐํ๋ฅผ ๊ตฌํํ class ๊ฐ์ฒด ๊ฐ์ ๊ฒ์ด์ด์ผ ํ๋ค๋ ๊ฒ์
๋๋ค.
๋ค์ ๋์์์!
์ฝ๋๋ฅผ ์์ ํฉ๋๋ค.
// A task group is for concurrency with dynamic width
func taskgroupFetchThumbnail(for ids: [String]) async throws -> [String: UIImage] {
var thumbnails: [String: UIImage] = [:]
try await withThrowingTaskGroup(of: (String, UIImage).self, body: { group in
for id in ids {
group.addTask {
return (id, try await self.fetchOneThumbnail2(withId: id))
}
}
// ์ด for-await ๋ฃจํ๋ ์๋ฃ๋ ์์๋๋ก ์์ ์์
์์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
for try await (id, thumbnail) in group {
thumbnails[id] = thumbnail
}
})
return thumbnails
}
withThrowingTaskGroup(of: ~)
์ ๋ค์ด๊ฐ๋๊ฒ group Task์์ ๋ฐํ๋๋ ํ์
์ด์๊ตฐ์.
Group Task์์์ ๋ฃจํ ์์ ์๋ ๋ง์ ์์ Task๋ฅผ ๋์์ ์์ฑํด์ ํ ๋ฒ์ ๋ง๋ค์ด์ ๋ฆฌํด์ํค๊ณ ,
๋ฐ์์์ for-in ๋ฃจํ๋ ์์ฐจ์ ์ผ๋ก thumbnail ๋์
๋๋ฆฌ๋ฅผ ์ธํ
ํฉ๋๋ค.
๋ง์ฝ์ Error๊ฐ ๋ฐ์ํ๋ฉด async-let ์ฒ๋ผ ์ข ๋ฃ๋์ง ์๊ณ cancel, waitํ๋ค๊ฐ ํจ์ ์ข ๋ฃ๋ ๋ ๊ฐ์ด ๋๋๋ค๊ณ ํฉ๋๋ค.
์ฌ๊ธฐ๊น์ง Scoped(๋ฒ์๊ฐ ์ง์ ๋) Structured(๊ตฌ์กฐํ๋) Task๋ฅผ ์ ๊ณตํ๋
๋ฐฉ๋ฒ์ด์์ต๋๋ค.
๋ค์์ unStructured Task ์
๋๋ค.
์ ์ง ์ด๊ฒ ์ ๊ฐ ์ฐพ๋ Task์ผ ๊ฒ ๊ฐ์ ์๊ฐ์ด ๋ญ๋๋ค!
์ฑ์ ๊ตฌํํ๋ฉด์.. Task๊ฐ ํญ์ ๋ช
ํํ ๊ณ์ธต ๊ตฌ์กฐ์ ์ํ์์ผ๋ก๋ง ๊ตฌํ๋์ง ์์ต๋๋ค.
์คํ๋ ค ํจ์ฌ ๋ ๋ง์ ์๋๊ด๋ฆฌ๊ฐ ํ์ํ ๋์ ์ ์ฐ์ฑ์ ์ ๊ณตํ๋ Unstructuredํ ๋ฐฉ์์ด ๋ ๋ง์ฃ .
์ ์ผ ํํ ์ํฉ์,
non-async ์ฝ๋์์ async ๊ณ์ฐ์ ์ํํ๊ธฐ ์ํด Task๋ฅผ ์์ํ๋ ค๋ ๊ฒฝ์ฐ Parent Task๊ฐ ์ ํ ์์ ๊ฒฝ์ฐ ์
๋๋ค.
๋๋,
๋ด๊ฐ ์ํ๋ Task์ ์๋ช
์ด single scope ๋๋ single function์ด ์๋์๋ ์์ฃ .
์๋ฅผ ๋ค์ด, ๊ฐ์ฒด๋ฅผ active ์ํ๋ก ์ ํํ๋ ๋ฉ์๋ ํธ์ถ์ ๋ํ ์๋ต์ผ๋ก Task๋ฅผ ์์ํ ๋ค์ ๊ฐ์ฒด๋ฅผ deactivate ์ํ๋ก ์ ํํ๋ ๋ฉ์๋ ํธ์ถ์ ๋ํ ์๋ต์ผ๋ก Task๋ฅผ ์ทจ์ํ๊ณ ์ถ์ ์๋ ์์ต๋๋ค.
์ด๊ฒ์ AppKit ๋ฐ UIkit์์ ๋ธ๋ฆฌ๊ฒ์ดํธ ๊ฐ์ฒด๋ฅผ ๊ตฌํํ ๋ ๋ง์ด ๋ํ๋ฉ๋๋ค.
์์๋ฅผ ๋ณด์์ฃ !
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt item: IndexPath) {
let ids = getThumbnailIds(for: item)
let thumbnails = await fetchThumbnail2(for: ids)
}
CollectionViewCell์ ๋คํธ์ํฌ์์ ๊ฐ์ ธ์จ ์ธ๋ค์ผ์ ๊ฐ์ ธ์ ๋์ฐ๋ ค๊ณ ํฉ๋๋ค.
ํ์ง๋ง Delegate ๋ฉ์๋๋ async ํ์ง ์๊ธฐ ๋๋ฌธ์ async ํจ์๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
MainThread์์ ๋์ํ๊ธฐ ๋๋ฌธ์ awaitํ ์ ์๊ธฐ ๋๋ฌธ์ด์ฃ ใ
.ใ
UI ์์
์ Main Thread์์ ๋ฐ์ํด์ผํ๋ฉฐ, Swift๋ @MainActor๋ผ๊ณ ํ๊ธฐํจ์ผ๋ก์จ ์ด๊ฑธ ๋ณด์ฅํฉ๋๋ค.
๋ง์ฝ์ ์ฐ๋ฆฌ๊ฐ Delegate ๋ฉ์๋ ์์์ Task ๋ฅผ ์์ฑํ๋ค๋ฉด, ์ฐ๋ฆฌ๋ ์ด Task๊ฐ Main Actor์์ ์คํ๋๊ธธ ์ํฉ๋๋ค.
์ด ๋! unstructured Task๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt item: IndexPath) {
let ids = getThumbnailIds(for: item)
let thumbnails = await fetchThumbnail2(for: ids)
}
์ด ์ฝ๋๋ฅผ unstructured Task๋ฅผ ์ฌ์ฉํด async ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๊ฒ ํด๋ณผ๊ฒ์.
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt item: IndexPath) {
let ids = getThumbnailIds(for: item)
Task {
let thumbnails = try await fetchThumbnail2(for: ids)
display(thumbnails, in: cell)
}
}
async ๊ด๋ จ ์ฒ๋ฆฌ๋ฅผ Task์์ ๋ฃ์์ต๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ๋ฌด์จ์ผ์ด ์ผ์ด๋๋๋ฉด..
Task๋ฅผ ์์ฑํ๋ ์์ ์ ๋๋ฌํ๋ฉด Swift๋ original scope์ ๋์ผํ Actor์์ ์คํ๋๋๋ก ์ค์ผ์ค๋ง ํฉ๋๋ค. (Task๋ ์๋์ผ๋ก ์ค์ผ์ค๋ง)
์ด ๊ฒฝ์ฐ์๋ MainActor์์ ์คํ๋๊ฒ ์ฃ ?
์ธ๋ค์ผ์ ๋ฐ์์ค๋ Task๋ Delegate ํจ์ ์์์ Main Thread๋ฅผ ์ฐจ๋จํ์ง ์๊ณ ๊ธฐํ๊ฐ ์์ ๋ Main Thread์์ ์คํ๋ฉ๋๋ค.
์ด๋ฐ์์ผ๋ก Task๋ฅผ ์คํํ๋ฉด ๊ตฌ์กฐํ๋ ์ฝ๋์ ๊ตฌ์กฐํ ๋์ง ์์ ์ฝ๋ ์ฌ์ด์ ์ค๊ฐ์ง์ ์ ์ ๊ณตํ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ ์ง์ ์์ฑ๋ Task์ ํน์ง์ด ์๋๋ฐ์.
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt item: IndexPath) {
let ids = getThumbnailIds(for: item)
Task {
let thumbnails = try await fetchThumbnail2(for: ids)
display(thumbnails, in: cell)
}
}
๊ทธ๋์!!!! ์ ์ ์์์์ collectionView๋ฅผ ํ๊ธฐํ ๋ ์ธ๋ค์ผ์ด ๋ค์ด๋ก๋ ๋๊ธฐ๋์ ์ ์คํฌ๋กค๋๋ฉด ํด๋น Task๋ ์ทจ์ํด์ผ ํฉ๋๋ค.
์ Task๋ unscoped task์ด๊ธฐ ๋๋ฌธ์ Task cancel์ ์๋์ด ์๋๋๋ค.
๋ฐ๋ผ์..
var thumbnailTasks: [IndexPath: Task<Void, Error>] = [:]
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt item: IndexPath) {
let ids = getThumbnailIds(for: item)
thumbnailTasks[item] = Task {
defer { thumbnailTasks[item] = nil } // defer๊ฐ ๋ญ์ง
let thumbnails = try await fetchThumbnail2(for: ids)
display(thumbnails, in: cell)
}
}
์ด๋ ๊ฒ ์ ์ฅํด๋๋ค๊ฐ!!
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
thumbnailTasks[indexPath]?.cancel()
}
cancel ์ํฌ ์ ์์ต๋๋ค.
์์์ ์ดํด๋ณธ Task๋ ๋ชจ๋ Task๋ ๋ชจ๋ ์์ฑ๋๋ฉด original scope์ ํน์ฑ์ ์์ํ๋ ํน์ง์ด ์์๋๋ฐ์.
์๋ฌด๊ฒ๋ ์์ํ๊ณ ์ถ์ง ์์ Task๋ ์์ต๋๋ค.
์์ ํ ์์จ์ฑ์ ์ํด...
cancel, await๋ฅผ manuallyํ๊ฒ ๊ด๋ฆฌํด์ค์ผํ๋ฉฐ lifeTime์ด unscopeํฉ๋๋ค.
originating context์์ ์๋ฌด๊ฒ๋! ์์ํ์ง ์์ต๋๋ค.
์๋ฒ์์ ์ธ๋ค์ผ์ ๊ฐ์ ธ์จ ํ ๋์ค์ ๊ฐ์ ธ์ค๋ ค๊ณ ํ ๋ ๋คํธ์ํฌ์ ๋ค์ ์ฐ๊ฒฐ๋์ง ์๋๋ก ๋ก์ปฌ ๋์คํฌ ์บ์์ ๊ธฐ๋กํ๋ ค๊ณ ํ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
์บ์ฑ์ ๋ฉ์ธ ์กํฐ์์ ๋ฐ์ํ ํ์๊ฐ ์์ผ๋ฉฐ ๋ชจ๋ ์ธ๋ค์ผ ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ทจ์ํ๋๋ผ๋ ๊ฐ์ ธ์จ ์ธ๋ค์ผ์ ์บ์ํ๋๊ฒ์ด ์ฌ์ ํ ์ ์ฉํฉ๋๋ค.
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt item: IndexPath) {
let ids = getThumbnailIds(for: item)
thumbnailTasks[item] = Task {
defer { thumbnailTasks[item] = nil } // defer๊ฐ ๋ญ์ง
let thumbnails = try await fetchThumbnail2(for: ids)
display(thumbnails, in: cell)
/*
Datached Task
๋ก์ปฌ ๋์คํฌ์ ์บ์ฑํ๋๊ฑฐ๋๊น ๋ฎ์ ์ฐ์ ์์์ ๋ฐฑ๊ทธ๋ผ์ด๋์ ์ง์ ํฉ๋๋ค.
*/
Task.detached(.background, operation: {
writeToLocalcache(thumbnails)
})
}
}
์๋ฐ์์ผ๋ก Detach Task๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
๋๋์ด..!!
๋๋์ด ๋๋ฌ๋ค..!!!!
์ด 27๋ถ 54์ด ์ง๋ฆฌ ์์์ 3์ผ ๋์ ์ ๋ฆฌ?.. ๋ฐ์ ์ ์๋ค.
ํด๊ทผ ํ์ ์คํฐ๋๋ฅผ ํ๋๋ฐ...์ 10๋ถ ์ ๋ฆฌํ๋ฉด 2์๊ฐ ์ง๋์๊ณ ๊ทธ๋ฌ๋๊ณ ใ
ใ
ใ
์ด์ ์์ผ meet async/await ํ๋๋ฐ Unstructured Task ํ๋ ์๋ ค๋ค๊ฐ ์ฒดํ ๋ปํ๋ค.
์ด์จ๋ ํฌ๊ธฐํ์ง ์์์ด...
์ด ์ธ์
์ async/await์ ๋ํ ๊ณต๋ถ๋ฅผ ๋ ํ ๋ค์์ ๋ค์ ํ ๋ฒ ๋ด์ผ๊ฒ ๋ค.
์ ๊ฒฐ๋ก ์ ์ผ๋ก ๋ด๊ฐ ๊ถ๊ธํ๋ Task๋
Unstructured task ์๊ณ ,
์..์ง๊ธ์ผ๋ก์จ๋ ๋ค๋ฅธ Task๋ฅผ ์ธ ์ ์์์ง ๋ชจ๋ฅด๊ฒ ๋ค.
์์ ์ฝ๋๋ฅผ ์ข ๋ ํ์ตํด์ผ ๋ ๊ฒ ๊ฐ๋ค.
๋ค์ ํ์ต์ ํ๊ธฐ์ ์..
asyn/await์์ ๋ฐฐ์ด ์์ ๋ค์ ๋ค์ ์ ๋ฆฌํ๊ณ
๋ด๊ฐ ์ง๊ธ์ฐ๋ ํ๋ก์ ํธ์์ ์ด๊ฑธ ์ด๋ป๊ฒ ๋
น์ผ ์ ์์์ง ๊ณ ๋ฏผํด๋ณด๊ณ ..!
alamofire์๋ ์ด๋ป๊ฒ ํฉ์ณ์ง์ง ํ์ตํด๋ด์ผ๊ฒ ๋ค.
Actor, defer์ ๊ฐ๋
๋.
๋!