[๐ŸŽWWDC21] Use async/await with URLSession (๋ฏธ์™„์„ฑ...)

Kioยท2022๋…„ 3์›” 19์ผ
0

WWDC

๋ชฉ๋ก ๋ณด๊ธฐ
1/4

WWDC21, Use async/await with URLSession ์„ ์ง์ ‘ ๋ณด๊ณ  ๋ฒˆ์—ญํ•œ ๊ธ€์ž„์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.


๊ธฐ์กด URLSession

Intro (00:00)

์•ˆ๋…•ํ•˜์„ธ์š”. ์ €๋Š” Guoye ์ž…๋‹ˆ๋‹ค.
์ œ ๋™๋ฃŒ Zhenchao ์™€ ์ €๋Š” HTTP frameworks ์—์„œ ์ผํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„์€ Swift concurrency(๋™์‹œ์„ฑ) ์— ๋Œ€ํ•ด ๋งŽ์ด ๋“ค์–ด๋ณด์•˜์„ ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋“ค์–ด๋ณด์ง€ ๋ชปํ–ˆ๋‹ค๋ฉด Meet async/await in Swift ์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”. ์ €๋Š” URLSession ์—์„œ async/await ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ œ๊ฐ€ Swift ๋™์‹œ์„ฑ ์„ ๊ฐ€์žฅ ์ข‹์•„ํ•˜๋Š” ๊ฒƒ์€, ์ด๊ฒƒ์ด ๋‹น์‹ ์˜ ์ฝ”๋“œ๋ฅผ ์„ ํ˜•์ ์ด๊ณ  ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๊ณ , ๊ธฐ๋ณธ Swift ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

  • Swift concurrency
    • Linear
    • Concise
    • Native error handling

๋„คํŠธ์›Œํ‚น์€ ๋ณธ์งˆ์ ์œผ๋กœ ๋น„๋™๊ธฐ์ด๋ฉฐ, iOS 15 ๋ฐ macOS Monterey ์—์„œ Swift ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก URLSession์— ์ƒˆ๋กœ์šด API๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. async/await ์ฑ„ํƒํ•˜์—ฌ ์ƒˆ๋กœ์šด API๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด ์ €ํฌ๊ฐ€ ์ค€๋น„ํ•œ ์•ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๊ฐ•์•„์ง€๋ฅผ ์ข‹์•„ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•œ ์‚ฌ์ง„ ๊ณต์œ  ์•ฑ์ด๊ณ , ์šฐ๋ฆฌ๋Š” ์ด ์‚ฌ์ง„๋“ค์„ ์ฆ๊ฒจ์ฐพ๊ธฐ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


(01:04)

์œ„๋Š” ๊ฐ•์•„์ง€ ์‚ฌ์ง„์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. URLSession ์—์„œcompletionHandler ๋ฅผ ๊ธฐ๋ฐ˜ ํŽธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ๋Š” ๊ฐ„๋‹จํ•ด ๋ณด์ด๋ฉฐ, ์ œํ•œ๋œ ํ…Œ์ŠคํŠธ์—์„œ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์—๋Š” ์ ์–ด๋„ 3๊ฐ€์ง€์˜ ์˜ค๋ฅ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿผ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ์š”? ์ฒซ ๋ฒˆ์งธ๋กœ ์ œ์–ดํ๋ฆ„์„ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” data task ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ๊ฒƒ์„ resume(์‹คํ–‰) ํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๊ณ  ๋‚˜์„œ ํ•œ๋ฒˆ์˜ task ๊ฐ€ ๋๋‚˜๋ฉด ์šฐ๋ฆฌ๋Š” completion handler ๋กœ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. response ๋ฅผ ์ฒดํฌํ•˜๊ณ , image ๋ฅผ ๋งŒ๋“ค๊ณ  ๋‚˜๋ฉด ์ œ์–ด ํ๋ฆ„์ด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

์Œ, ์ฝ”๋“œ๊ฐ€ ์•ž๋’ค๋กœ ๋„ˆ๋ฌด ์™”๋‹ค๊ฐ”๋‹ค ํ•ฉ๋‹ˆ๋‹ค. threading ์€ ์–ด๋–ค๊ฐ€์š”? ์ž‘์€ ์ฝ”๋“œ ๋ธ”๋Ÿญ์ธ๋ฐ๋„ ๋„ˆ๋ฌด๋‚˜๋„ ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด ์„œ๋กœ ๋‹ค๋ฅธ 3๊ฐœ์˜ excution contexts(์‹คํ–‰ ์ •๋ณด) ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ฐ€์žฅ ๋ฐ”๊นฅ ๋ ˆ์ด์–ด๋Š” ํ˜ธ์ถœ์ž์˜ thread ํ˜น์€ Queue ์—์„œ ๋™์ž‘ํ•˜๊ณ 
  • URLSessionTask completionHandler ๋Š” session ์˜ delegate Queue ์—์„œ ๋™์ž‘ํ•˜๊ณ 
  • ๋งˆ์ง€๋ง‰ completion handler ๋Š” main Queue ์—์„œ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

compiler ๋Š” ์—ฌ๊ธฐ์—์„œ ์šฐ๋ฆฌ๋ฅผ ๋„์™€์ค„ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—, data races ์™€ ๊ฐ™์€ ๋ชจ๋“  threading issues ๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ฃผ์˜๋ฅผ ๊ธฐ์šธ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

2:12

์ง€๊ธˆ ์ €๋Š” ๋ฌด์–ธ๊ฐ€ ์ž˜๋ชป๋œ ๊ฒƒ์„ ๋ฐœ๊ฒฌํ–ˆ๋Š”๋ฐ์š”. completionHandler ์— ๋Œ€ํ•œ ํ˜ธ์ถœ์ด ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ main Queue ์— ์ „๋‹ฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ฒ„๊ทธ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ, ์šฐ๋ฆฌ๋Š” ์—ฌ๊ธฐ์„œ early return(๋น ๋ฅธ ์ข…๋ฃŒ) ๋ฅผ ๋†“์น˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ๋˜๋ฉด completionHandler ๋Š” ๋‘ ๋ฒˆ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ˜ธ์ถœ์ž๊ฐ€ ๋งŒ๋“  ๊ฐ€์ •์„ ์œ„๋ฐ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ด๊ฒƒ์€ ๋ช…๋ฐฑํ•˜์ง€ ์•Š์ง€๋งŒ UIImage ์ƒ์„ฑ์ด ์‹คํŒจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ •ํ™•ํ•˜์ง€ ์•Š์€ ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ผ๋ฉด, UIImage ์ƒ์„ฑ์ž ๋Š” nil์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์šฐ๋ฆฌ๋Š” nil image ์™€ nil error ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” completionHandler ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ๊ธฐ๋Œ€ํ•˜๋Š” ๋ฐ”๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.




sync/await ์ ์šฉ (02:51)

์ด๊ฒƒ์€ async/await ๋ฅผ ์‚ฌ์šฉํ•œ ์ƒˆ๋กœ์šด ๋ฒ„์ „์ž…๋‹ˆ๋‹ค. ์™€์šฐ, ๋งค์šฐ ๊ฐ„๋‹จํ•ด์กŒ๋„ค์š”! ์ œ์–ดํ๋ฆ„์€ ์œ„๋ถ€ํ„ฐ ์•„๋ž˜๋กœ ์„ ํ˜•์ด๋ฉฐ, ์ด ํ•จ์ˆ˜์˜ ๋ชจ๋“  ๊ฒƒ์ด ๊ฐ™์€ concuurency context(๋น„๋™๊ธฐ ๋งฅ๋ฝ) ์—์„œ ์ž‘๋™ํ•˜๋Š” ๊ฑธ ์šฐ๋ฆฌ๋Š” ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ์šฐ๋ฆฌ๋Š” ๋” ์ด์ƒ threading issues ์— ๋Œ€ํ•ด ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.


์—ฌ๊ธฐ URLSession ์—์„œ ์ƒˆ๋กœ์šด async data method ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์‹คํ–‰ ์ฝ˜ํ…์ŠคํŠธ(์ฝ”๋“œ๋“ค์ด ์‹คํ–‰๋˜๊ธฐ ์œ„ํ•œ ํ™˜๊ฒฝ) ๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ๋ฉˆ์ถ˜ ์ƒํƒœ๋กœ, URLSession ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒํ•˜๋ฉด data ์™€ response ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ error ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.


๋˜ํ•œ ์šฐ๋ฆฌ๋Š” ์˜ˆ์ƒ์น˜ ๋ชปํ•œ response ์˜ ๊ฒฝ์šฐ, error ๋ฅผ ๋˜์ง€๊ธฐ ์œ„ํ•ด throw ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ˜ธ์ถœ์ž๊ฐ€ Swift ๊ธฐ๋ณธ error ์ฒ˜๋ฆฌ ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ error ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , error ๋ฅผ ์žก์•„๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๋งˆ์ง€๋ง‰์œผ๋กœ, ๋งŒ์•ฝ ์ด ํ•จ์ˆ˜๋กœ๋ถ€ํ„ฐ optional UIImage ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ ค๊ณ  ์‹œ๋„ํ•œ๋‹ค๋ฉด compiler ๋Š” ์ฃผ์˜๋ฅผ ์ฃผ๋ฉฐ, ๊ธฐ๋ณธ์ ์œผ๋กœ nil ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ฐ•์š”ํ•ฉ๋‹ˆ๋‹ค.


URLSession.data (03:45)

์—ฌ๊ธฐ๋Š” ๋„คํŠธ์›Œํฌ๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ ๋ฉ”์„œ๋“œ์˜ signatures(๋ถ€๋ถ„) ์ด๋‹ค.
URLSession.data ๋ฉ”์„œ๋“œ๋Š” URL ํ˜น์€ URLRequest ๋ฅผ ์ฑ„ํƒํ•ฉ๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ data task ํŽธ์˜ ๋ฉ”์„œ๋“œ ์— ์กด์žฌํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.


URLSession.upload (04:02)

๋˜ํ•œ, ์šฐ๋ฆฌ๋Š” data ํ˜น์€ file ์„ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” upload ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ upload task ํŽธ์˜ ๋ฉ”์„œ๋“œ ์— ์กด์žฌํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ๋ฉ”์„œ๋“œ GET ์ด ์—…๋กœ๋“œ๋ฅผ ๋” ์ด์ƒ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—,
์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „์— ์ •ํ™•ํ•œ HTTP ๋ฉ”์„œ๋“œ ์„ค์ •์„ ํ™•์‹คํ•˜๊ฒŒ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


URLSession.download (04:21)

download ๋ฉ”์„œ๋“œ ๋Š” response body ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์•„๋‹Œ ํŒŒ์ผํ˜•ํƒœ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. download task ํŽธ์˜ ๋ฉ”์„œ๋“œ ์™€๋Š” ๋‹ค๋ฅด๊ณ , ์ด ์ƒˆ๋กœ์šด ๋ฉ”์„œ๋“œ๋Š” ์ž๋™์ ์œผ๋กœ ํŒŒ์ผ์„ ์‚ญ์ œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ์ง์ ‘ ํ•ด์•ผํ•จ์„ ์žŠ์ง€ ๋งˆ์„ธ์š”. ์ด ์˜ˆ์‹œ์—์„œ ์ถ”๊ฐ€์ ์ธ ๊ณผ์ •์„ ์œ„ํ•ด ๋‹ค๋ฅธ ์œ„์น˜๋กœ ํŒŒ์ผ์„ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.


Cancellation (04:44)

Swift concurrency's cancellation ์€ URLSession async ๋ฉ”์„œ๋“œ ์—์„œ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ทจ์†Œํ•˜๋Š” ํ•œ ๋ฐฉ๋ฒ•์€ concurrency Task.Handle ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

(์ž‘์„ฑ์ค‘)

Here, we call async to create a concurrency Task loading two resources one by one. Later, we can use the Task.Handle to cancel its current running operation.

Please note that concurrency Task is unrelated to URLSessionTask, even though they share the name "Task." The methods we just talked about -- data, upload, download -- wait for the entire response body to arrive before returning.

What if we want to receive the response body incrementally? I'm happy to introduce URLSession.bytes methods.

They return when the response headers have been received and deliver the response body as an AsyncSequence of bytes. To show you how it works, my colleague Zhenchao will demo how he's adopting it in the Dogs app.

Zhenchao Li: Thanks, Guoye! Hi, I'm Zhenchao. I have been working on a new feature of the Dogs app that shows how many people favorited a dog photo.

profile
Someday_iOS_Dev

0๊ฐœ์˜ ๋Œ“๊ธ€