ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Swift] Wrapping existing asynchronous code in async/await in Swift
    Swift 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 ํด๋กœ์ €๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ๋ช‡ ๊ตฐ๋ฐ ์ง€์ ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

    @escaping ์ฐธ๊ณ 

    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/

    ๋Œ“๊ธ€

Designed by Tistory.