-
[Swift] Wrapping existing asynchronous code in async/await in SwiftSwift 2023. 8. 17. 09:21
Swift์ async/await ๊ธฐ๋ฅ์ iOS 13 ๋ฐ ์ดํ ๋ฒ์ ์์ ๋น๋๊ธฐ ์ฝ๋์ ๊ฐ๋ ์ฑ์ ํฅ์์ํค๋ ๋๋ผ์ด ๋ฐฉ๋ฒ์ ๋๋ค. ์ ํ๋ก์ ํธ์์๋ ์ด๋ก ์ธํด ๋ ํํ๋ ฅ์ด ์๊ณ ๊ฐ๋ ์ฑ์ด ์ข์ผ๋ฉฐ ๋๋ฒ๊น ํ๊ธฐ ์ฌ์ด ๋น๋๊ธฐ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ผ๋ฉฐ, ์ด๋ ๋๊ธฐ ์ฝ๋์ ๋งค์ฐ ์ ์ฌํ๊ฒ ์ฝํ๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ async/await๋ฅผ ์ฑํํ๋ ๊ฒ์ ํจ์์ ์๋ฃ ํธ๋ค๋ฌ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ ๋น๋๊ธฐ API๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์ฝ๋๋ฒ ์ด์ค๋ฅผ ์๋นํ ๋ณ๊ฒฝํด์ผ ํ ์๋ ์์ต๋๋ค.
๋คํํ๋, ๊ธฐ์กด์ ์ฝ๋ฐฑ ๊ธฐ๋ฐ ๋น๋๊ธฐ ์ฝ๋๋ฅผ ์๋ก์ด async/await ์ธ๊ณ๋ก ๊ฐ์ ธ์ค๊ธฐ ์ํด Swift์ ๋ด์ฅ ๊ธฐ๋ฅ์ ํ์ฉํ ์ ์์ต๋๋ค. ์ด ๊ธ์์๋ ๊ธฐ์กด์ ์ฝ๋ฐฑ ๊ธฐ๋ฐ ๋น๋๊ธฐ ์ฝ๋๋ฅผ async ๋ฐ async/await์ ํจ๊ป ์๋ํ๋ ํจ์๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ ์ค ํ๋๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ฝ๋ฐฑ ๊ธฐ๋ฐ ํจ์๋ฅผ async/await๋ก ๋ณํํ๊ธฐ
์ฝ๋ฐฑ ๊ธฐ๋ฐ ํจ์๋ ๋ค์ํ ํํ๋ก ๋ํ๋ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๋ค์ ์์ ๋น์ทํ๊ฒ ๋ณด์ผ ๊ฒ์ ๋๋ค.
func validToken(_ completion: @escaping (Result<token, error="">) -> Void) { let url = URL(string: "<https://api.internet.com/token>")! URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data else { completion(.failure(error!)) } do { let decoder = JSONDecoder() let token = try decoder.decode(Token.self, from: data) completion(.success(token)) } catch { completion(.failure(error)) } } } </token,>์์ ์์ ๋ validToken(_:) ๋ฉ์๋๊ฐ ์ด๋ป๊ฒ ๋ณด์ผ ์ ์๋์ง ๋งค์ฐ ๋จ์ํํ ๋ฒ์ ์ ๋๋ค. ์ด ํจ์๋ completion ํด๋ก์ ๋ฅผ ์ทจํ๋ฉฐ, ์ ํจํ ํ ํฐ์ ์ป๋ ์๋์ ๊ฒฐ๊ณผ๋ก completion ํด๋ก์ ๊ฐ ํธ์ถํ๋ ๋ช ๊ตฐ๋ฐ ์ง์ ์ด ์๋ค๋ ๊ฒ์ ๋๋ค.
validToken ํจ์๋ฅผ async ํจ์๋ก ์ฌ์ฉํ ์ ์๋๋ก ๋ง๋๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ async throws๋ก ํ์ํ๊ณ , Token์ ๋ฐํํ๋ 2๋ฒ์งธ ๋ฒ์ ์ ์์ฑํ๋ ๊ฒ์ ๋๋ค. ๋ฉ์๋ ์๊ทธ๋์ฒ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
func validToken() async throws -> Token { // ... }์ด ๋ฉ์๋ ์๊ทธ๋์ฒ๋ ์ด์ ๋ณด๋ค ํจ์ฌ ๊น๋ํด๋ณด์ด์ง๋ง, ์ด์ ๊ธฐ์กด์ ์ฝ๋ฐฑ ๊ธฐ๋ฐ ํจ์๋ฅผ ์ด๋ป๊ฒ๋ ํ์ฉํ๊ณ ํด๋ก์ ์ ์ ๋ฌ๋ async validToken์ผ๋ก๋ถํฐ ๋ฐํ๋ Result<Token, Error>๋ฅผ ์ฌ์ฉํด์ผํฉ๋๋ค.
์ด๋ฅผ ์ํด continuation์ด๋ผ๋ ๋ฉ์ปค๋์ฆ์ ํ์ฉํ ์ ์๋๋ฐ, ์ฌ์ฉํ ์ ์๋ continuation์๋ ์ฌ๋ฌ ๊ฐ์ง ์ข ๋ฅ๊ฐ ์์ต๋๋ค:
- withCheckedThrowingContinuation
- withCheckedContinuation
- withUnsafeThrowingContinuation
- withUnsafeContinuation
๋ณด์๋ค์ํผ, checked์ unchecked continuation์ด ์์ต๋๋ค. ์ด ์ฐจ์ด์ ์ ๋ํด์ ์๊ณ ์ถ๋ค๋ฉด ์ด ๊ธ์ ํ์ธํ์ธ์. ๋ํ throw์ non-throwing ๋ฒ์ ์ continuation์ด ์์ต๋๋ค. ์ด๋ ์ฌ์ฉ์๊ฐ ์์ ๊ฐ๋ฅํ ์ํฉ์ ์ ์ฉํฉ๋๋ค. async/await ํจ์๋ก ๋ณํํ๋ ์คํจํ ์ ์๋ ๊ฒฝ์ฐ, throwing continuation์ ์ฌ์ฉํ์ธ์. ํจ์๊ฐ ํญ์ ์ฑ๊ณต ๊ฐ์ผ๋ก ์ฝ๋ฐฑ์ ํธ์ถํ๋ ๊ฒฝ์ฐ, ์ผ๋ฐ continuation์ ์ฌ์ฉํ์ธ์.
์ฐ์ checked continuation์ ์ฌ์ฉํ ๋ ์์ฑ๋ validToken์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
func validToken() async throws -> Token { return try await withCheckedThrowingContinuation { continuation in validToken { result in switch result { case .success(let token): continuation.resume(returning: token) case .failure(let error): continuation.resume(throwing: error) } } } }return try await withChecked…์ ์ฃผ๋ชฉํ์ธ์.
continuation์ ๋ํ ๋ฐํ ํ์ ์ resume(returning:) ๋ฉ์๋ ํธ์ถ์์ ์ ๋ฌํ ๊ฐ์ฒด ํ์ ์ ๋๋ค.
์ฝ๋ฐฑ์ ๋ฐ๋ validToken ๋ฒ์ ์ ํจ์๋ Result<Token, Error>๋ก ์ฝ๋ฐฑ์ ํธ์ถํ๊ธฐ ๋๋ฌธ์ Swift๋ result ์ธ์์ ์ฑ๊ณต ์ผ์ด์ค๊ฐ Token์ด๋ผ๋ ๊ฒ์ ์๊ณ ์์ผ๋ฏ๋ก resume(returning:)์ ์ ๋ฌํ ๊ฐ์ฒด์ ํ์ ์ด Token์ด๊ณ , withCheckedThrowingContinuation์ ๋ฐํ ํ์ ์ Token์ด ๋ฉ๋๋ค.
withCheckedThrowingContinuation๊ณผ ๊ทธ์ ๋์ํ๋ ํจ์๋ค์ ๋ชจ๋ continuation ๊ฐ์ฒด์ resume ํจ์๊ฐ ํธ์ถ๋ ๋๊น์ง ์ผ์ ์ค๋จ๋๋ async ํจ์์ ๋๋ค. ์ด continuation ๊ฐ์ฒด๋ with*Continuation ํจ์์ ์ํด ์์ฑ๋๋ฉฐ, (๊ฒฐ๊ตญ) ์ด ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์คํ์ ์ฌ๊ฐํ๋ ๊ฒ์ ์ฌ์ฉ์์ ๋ชซ์ ๋๋ค. ์ด ์์ ์ ํ์ง ์์ผ๋ฉด continuation์ด result๋ฅผ ์์ฑํ์ง ์์ผ๋ฏ๋ก ๋ฉ์๋๊ฐ ์์ํ ์ผ์ ์ค๋จ๋ฉ๋๋ค.
with*Continuation ํจ์์ ์ ๋ฌ๋ ํด๋ก์ ๋ ์ฆ์ ์คํ๋๋ฏ๋ก, ์ฝ๋ฐฑ ๊ธฐ๋ฐ์ validToken ๋ฒ์ ์ด ๋ฐ๋ก ํธ์ถ๋ฉ๋๋ค. resume์ ํธ์ถํ๋ฉด ๋น๋๊ธฐ validToken ํจ์์ ํธ์ถ์๋ ์ผ์ ์ค๋จ๋ ์ํ์์ ๋ฒ์ด๋ ์ฆ์ ์ด๋ํ์ฌ ์คํ์ ์ฌ๊ฐํ ์ ์์ต๋๋ค.
Result๋ Error๋ฅผ ํฌํจํ ์ ์์ผ๋ฏ๋ก, async validToken์ด ์ค๋ฅ๋ฅผ ๋์ง๋๋ก ํ๋ ค๋ฉด failure case๋ฅผ ํ์ธํ๊ณ resume(throwing:)์ ํธ์ถํด์ผ ํฉ๋๋ค.
์์ ์ฝ๋๋ ์๋นํ ์ฅํฉํ๋ฉฐ, Swift ํ์ ์์ ํจํด์ด ๋งค์ฐ ์ผ๋ฐ์ ์ธ ํจํด์ผ ์ ์๋ค๋ ๊ฒ์ ์ธ์ํด์ Result ๊ฐ์ฒด๋ฅผ ํ์ฉํ๋ 3๋ฒ์งธ ๋ฒ์ ์ resume์ ์ ๊ณตํ์ต๋๋ค. ์ด๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
func validToken() async throws -> Token { return try await withCheckedThrowingContinuation { continuation in validToken { result in continuation.resume(with: result) } } }ํจ์ฌ ๊น๋ํฉ๋๋ค.
continuation์ ์ฌ์ฉํ ๋ ์ค์ํ 2๊ฐ์ง ์ฌํญ์ ๊ธฐ์ตํ์ธ์.
- continuation์ ํ ๋ฒ๋ง ์ฌ๊ฐํ ์ ์์ต๋๋ค.
- continuation ํด๋ก์ ๋ด์์ resume์ ํธ์ถํ๋ ๊ฒ์ ์ฌ๋ฌ๋ถ์ ์ฑ ์์ ๋๋ค. resume์ ํธ์ถํ์ง ์์ผ๋ฉด ํจ์ ํธ์ถ์๊ฐ result๋ฅผ ์์ํ ๊ธฐ๋ค๋ฆฌ๊ฒ ๋ฉ๋๋ค.
๋ํ ์ค๋ฅ๋ฅผ ๋์ง ์ ์๋ ์ง์ ์ฌ๋ถ๋ฅผ ์ ์ธํ๊ณ ๋ with*Continuation ํจ์๊ฐ ๋ชจ๋ ๋์ผํ ๊ท์น์ ์ฌ์ฉํ๋ค๋ ์ ์ ์์๋๋ ๊ฒ์ด ์ข์ต๋๋ค. ๋ค๋ฅธ ๊ท์น์ ์์ ํ ๋์ผํฉ๋๋ค.
Summary
์ด ๊ธ์์๋ ์ฝ๋ฐฑ์ ๋ฐ๋ ํจ์๋ฅผ continuation์ผ๋ก ๋ํํ์ฌ ์๋ก์ด async/await ํจ์๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด์์ต๋๋ค. continuation์๋ ๋ค์ํ ์ข ๋ฅ๊ฐ ์์ผ๋ฉฐ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง์ ๋ํด ์์๋ณด์์ต๋๋ค.
continuation์ ๋ชจ๋ ์ฝ๋๋ฅผ ํ ๋ฒ์ ๋ค์ ์์ฑํ์ง ์๊ณ ๋ ๊ธฐ์กด์ ์ฝ๋๋ฅผ async/await๋ก ๋ณํํ ์ ์๋ ํ๋ฅญํ ๋ฐฉ๋ฒ์ ๋๋ค. ๊ธ์ ์์ฑ์๋ ๊ฐ์ธ์ ์ผ๋ก continuation์ ํ์ฉํ์ฌ ๋๋ฆฌ์ง๋ง ํ์คํ๊ฒ ์ฝ๋์ ๋ง์ ๋ถ๋ถ์ ์ ์ง์ ์ผ๋ก async/await๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋คํธ์ํน ์ฝ๋๋ฅผ ๋ค์ ์์ฑํ์ง ์๊ณ ๋ ๋ทฐ ๋ชจ๋ธ๊ณผ ๋คํธ์ํน ์ฌ์ด์ async/await๋ฅผ ์ง์ํ๋ ์ค๊ฐ ๋ ์ด์ด๋ฅผ ์์ฑํ ์ ์๋ค๋ ๊ฒ์ ๋งค์ฐ ๋ฉ์ง ์ผ์ ๋๋ค.
์ ๋ฐ์ ์ผ๋ก continuation์ ๊ธฐ์กด์ ์ฝ๋ฐฑ ๊ธฐ๋ฐ ํจ์๋ฅผ async/await๋ก ๋ณํํ๋ ๊ฐ๋จํ๊ณ ์ฐ์ํ API๋ฅผ ์ ๊ณตํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
์๋ฌธ
https://www.donnywals.com/wrapping-existing-asynchronous-code-in-async-await-in-swift/
'Swift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift] Understanding Swift Concurrencyโs AsyncStream (0) 2023.08.17 [Swift] Any and AnyObject (0) 2023.05.08 [Swift] ์ฌ์ฉ์ ์ ์ ์ฐ์ฐ์ (Custom Operator) (0) 2023.05.03 [Swift] [SceneConfiguration] Info.plist contained no UIScene configuration dictionary (looking for configuration named "(no name)") ์ด์ ํด๊ฒฐ (0) 2023.04.04 [๊ธฐํ] ์ ๊ท ํํ์ (Regex) (0) 2022.03.31