๊ธ€ ์ž‘์„ฑ์ž: ์ด์ง€์›๐ŸŒฉ๏ธ

๊ด€์‹ฌ ์žˆ๋Š” ์„ธ์…˜์„ ํ•˜๋‚˜์”ฉ ๋ณด๊ณ  ์žˆ๋Š”๋ฐ, ๊ทธ์ค‘ ์ฒซ ๋ฒˆ์งธ๋กœ ์‹œ์ฒญํ•œ ๊ฒƒ์ด "Meet async/await in Swift"๋‹ค. ์ค‘๋ฐ˜๋ถ€๊นŒ์ง€์˜ ๋‚ด์šฉ์„ ์ •๋ฆฌํ–ˆ๋‹ค. completionHandler๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ์™€ async/await์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋ฅผ ๋น„๊ตํ•˜๋Š” ๋‚ด์šฉ์ธ๋ฐ ์ด๊ฒƒ๋งŒ ๋ด๋„ ์–ด๋Š ์ •๋„ ๊ฐ์ด ์žกํžŒ๋‹ค.

 

๊ธฐ์กด์—๋Š” completionHandler๋ฅผ ์ด์šฉํ•ด ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ–ˆ๋‹ค. ๋น„๋™๊ธฐ ์ž‘์—…์„ ํ•˜๋ฉด, ์Šค๋ ˆ๋“œ๊ฐ€ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์„ ์™„๋ฃŒํ•  ๋•Œ๊นŒ์ง€ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

๋งŽ์€ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์นœ์ˆ™ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ๋ฅผ ์‚ดํŽด๋ณด๋ฉด, ํ…Œ์ด๋ธ” ๋ทฐ๊ฐ€ ์žˆ๊ณ , ์—ฌ๊ธฐ์—๋Š” ์„œ๋ฒ„์— ์ €์žฅ๋œ ์ธ๋„ค์ผ ์ด๋ฏธ์ง€๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค. ์ธ๋„ค์ผ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ณผ์ •์„ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

ViewModel์—์„œ fetchThumbnail ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ž‘์—…๋“ค์ด ์ง„ํ–‰๋œ๋‹ค.

  1. thumbnailURLRequest ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ๋ฌธ์ž์—ด์—์„œ URLRequest๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  2. 1์—์„œ ์ƒ์„ฑํ•œ URLRequest๋ฅผ ์ด์šฉํ•ด URLSession.dataTask(with:completion:)์„ ํ˜ธ์ถœํ•œ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ์š”์ฒญ์— ๋Œ€ํ•œ Data๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
  3. UIImage(data:)๋ฅผ ์ด์šฉํ•ด Data๋ฅผ UIImage๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
  4. ๋งˆ์ง€๋ง‰์œผ๋กœ UIImage.prepareThumbnail์„ ํ†ตํ•ด ์›๋ณธ ์ด๋ฏธ์ง€์—์„œ ์ถ•์†Œํ•ด ๋ Œ๋”๋งํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ์ž‘์—…์—์„œ 1๊ณผ 3์˜ ์ž‘์—…์€ ์•„์ฃผ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๊ฐ€ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ 2, 4์˜ ์ž‘์—…์€ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฐ๋‹ค. ๊ทธ๋ž˜์„œ 2, 4 ์ž‘์—…์„ ์œ„ํ•ด (์ง€๊ธˆ๊นŒ์ง€๋Š”) completionHandler๋ฅผ ์‚ฌ์šฉํ–ˆ์—ˆ๋‹ค. ์ฝ”๋“œ๋กœ ์‚ดํŽด๋ณด์ž.

func fetchThumbnail(for id: String, completion: @escaping (UIImage?, Error?) -> Void) {
    let request = thumbnailURLRequest(for: id)
    let task = URLSession.shared.dataTask(withL request) { data, response, error in 
        if let error = error {
            completion(nil, error)
        } else if (response as? HTTPURLResponse)?.statusCode != 200 {
            completion(nil, FetchError.badID)
        } else {
            guard let image = UIImage(data: data!) else {
                return
            }
            image.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in 
                guard let thumbnail = thumbnail else {
                    return
                }
                completion(thumbnail, nil)
            }
        }
    }
    task.resume()
}

์ด ์ฝ”๋“œ์˜ ๋ฌธ์ œ์ ์œผ๋กœ, ๋ฐ์ดํ„ฐ์—์„œ UIImage๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์ธ๋„ค์ผ ์ค€๋น„๊ฐ€ ์‹คํŒจํ•˜๋ฉด fetchThumbnail์˜ ํ˜ธ์ถœ์ž์—๊ฒŒ ์•Œ๋ฆผ์ด ์ „์†ก๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๊ฐœ์ธ์ ์ธ ์˜๊ฒฌ์œผ๋กœ ์ฝ”๋“œ์˜ depth๊ฐ€ ๊นŠ์–ด์ ธ์„œ ๋ณต์žกํ•˜๋‹ค.

 

๊ทธ๋ž˜์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ชจ๋“  ๊ฒฝ์šฐ์— ์•Œ๋ฆผ์„ ๋ณด๋‚ด๊ฒŒ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

func fetchThumbnail(for id: String, completion: @escaping (UIImage?, Error?) -> Void) {
    let request = thumbnailURLRequest(for: id)
    let task = URLSession.shared.dataTask(withL request) { data, response, error in 
        if let error = error {
            completion(nil, error)
        } else if (response as? HTTPURLResponse)?.statusCode != 200 {
            completion(nil, FetchError.badID)
        } else {
            guard let image = UIImage(data: data!) else {
                completion(nil, FetchError.badImage)
                return
            }
            image.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in 
                guard let thumbnail = thumbnail else {
                    completion(nil, FetchError.badImage)
                    return
                }
                completion(thumbnail, nil)
            }
        }
    }
    task.resume()
}

๊ทธ๋Ÿฐ๋ฐ, ๋ฌธ์ œ๋Š” Swift์—์„œ๋Š” ์ด ๊ณผ์ •์„ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ์›ํ•˜๋˜ ๋™์ž‘์€ ๋„ค ๊ฐ€์ง€ ์ž‘์—…์„ ์ˆœ์„œ๋Œ€๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ๋ฟ์ด์—ˆ๋Š”๋ฐ, ์ฝ”๋“œ๋Š” ๋”ฐ๋ผ๊ฐ€๊ธฐ ๋ณต์žกํ•˜๊ณ  ์–ด๋ ค์›Œ์กŒ๋‹ค๋Š” ๊ฑฐ๋‹ค. ์˜๋„๊ฐ€ ๋ชจํ˜ธํ•ด์กŒ๋‹ค.

Result ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๊ฒƒ์„ ์ข€ ๋” ์•ˆ์ „ํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ์ฝ”๋“œ๋ฅผ ๋” ์ถ”ํ•˜๊ณ  ์•ฝ๊ฐ„ ๋” ๊ธธ๊ฒŒ ๋งŒ๋“ ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  ์ด ๊ณผ์ •์„ async/await๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์ข‹๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

func fetchThumbnail(for id: String) async throws -> UIImage {
    let request = thumbnailURLRequest(for: id)
    let (data, response) = try await URLSession.shared.data(for: request)
    guard (response as? HTTPURLResponse)?.statusCode == 200 else {
        throw FetchError.badID
    }
    let maybeImage = UIImage(data: data)
    guard let thumbnail = await maybeImage?.thumbnail else { 
        throw FetchError.badImage 
    }
    return thumbnail
}

async๋ฅผ ํ‘œ์‹œํ•  ๋•Œ๋Š” throws ์ง์ „ ๋˜๋Š” ํ•จ์ˆ˜๊ฐ€ throw ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ํ™”์‚ดํ‘œ ์•ž์— ๊ฐ€์•ผ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ async๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ๋” ๊ฐ„๋‹จํ•ด์ง„๋‹ค. ์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ถ•์†Œ๋˜๋ฉด ์ธ๋„ค์ผ ์ด๋ฏธ์ง€๊ฐ€ ๋ฐ˜ํ™˜๋˜๊ณ , ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ทธ๋ƒฅ ์˜ค๋ฅ˜๋ฅผ ๋˜์ง„๋‹ค.

 

๋” ์ž์„ธํ•œ ๋‚ด์šฉ๊ณผ ๋’ท๋ถ€๋ถ„์ด ๊ถ๊ธˆํ•˜๋‹ค๋ฉด ํ•˜๋‹จ ๋งํฌ๋ฅผ ํ†ตํ•ด ์ง์ ‘ ์‹œ์ฒญํ•˜๋ฉด ์ข‹์„ ๊ฑฐ ๊ฐ™๋‹ค. 

 

์ฐธ๊ณ  ์ž๋ฃŒ

๋ฐ˜์‘ํ˜•