티스토리 뷰
개요
최근 RxSwift를 하며 비동기에 대한 이런저런 고민을 하다 보니, 최근 진행되었던 WWDC 21의 비동기 관련 세션이 궁금해졌어요 :)
이번 글에서는 영상에서 나온 내용을 정리해볼게요!
해당 세션의 주소는 아래와 같아요 ☺️
WWDC 21 - Meet async/await in Swift 보러 가기
우선 공식 페이지에서 설명하는 세션에 대한 정보를 먼저 읽어볼게요.
Swift가 이제 비동기 메서드를 지원하며, 코드를 더 간결하게 사용할 수 있다는 의미예요.
자세한 것은 영상을 보며 살펴볼게요!
동기 / 비동기
세션은 간단하게 동기와 비동기의 차이에 대해 언급하며 시작돼요.
preparingThumbnail(of:) 메서드가 완료된 후 fetchThumbnail 메서드가 실행된다고 할 때, 동기적으로 작동한다면 preparingThumbnail 메서드가 종료되기까지 스레드에서 다른 작업은 이뤄지지 않아요.
반면, 비동기적(completionHandler)으로 작동한다면 prepareThumbnail이 완료되기 전까지 해당 스레드에서 다른 작업들을 수행하고 있을 수 있어요. 그리고 완료가 된다면 그때 fetchThumbnail 메서드가 실행되겠죠 :)
이런 작업을 위해 위처럼 completionHandler로 클로저를 활용해 작업할 수도 있고, delegate callback을 사용할 수도 있어요.
이미지를 다운로드하는 앱 만들기
위와 같은 앱을 만들 때 왼쪽에 있는 이미지 아이콘은 로컬에 저장된 것이 아니라 웹에서 다운로드하는 상황이에요.
로직을 도식화하면 아래와 같아요.
위의 4가지 작업은 반드시 순서에 맞춰 진행되어야 해요.
그런데 이 작업들은 크게 두 가지로 구분될 수 있어요.
URLRequest 작업과 data를 활용해 UIImage를 그리는 작업은 즉각적으로 실행이 가능한 작업이에요.
반면 URLSession을 활용해 data를 요청하는 작업과 썸네일을 준비하는 메서드는 요청 후 완료되기까지 시간이 소요되는 작업이에요.
이 로직을 코드로 구현하면 아래와 같아요.
작동하는 데에 문제가 없지만 결론적으로 위와 같은 코드는 굉장히 길고 읽기 어려워요.
코드 상에서 if let, guard let의 바인딩이 여러 번 등장하는데 이때마다 실패 케이스 역시 completionHandler에 담아주어야 하기 때문이에요.
이렇게 긴 코드를 줄이고, 읽기 쉽게 만드는 데 사용하면 좋은 것이 async / await이에요 :)
20줄짜리 코드가 6줄이 되었어요 :)
우선 메서드 정의에서 completionHandler가 사라졌어요.
대신, async와 throws가 붙어있어요. throws는 아시다시피 Error를 throw 할 수 있는 메서드임을 의미하며, 마찬가지로 async 역시 await를 위해 필요한 마킹이라고 생각하면 될 것 같아요.
첫 줄의 request는 동기적으로 작동할 거예요.
그리고 2번째 줄로 넘어가면 try await가 있어요.
기존 코드에서는 이 과정에서 에러 체크를 하며 error 자체를 completionHandler로 넘겨주는 작업을 했었기 때문에 코드가 길어지고 읽기가 어려웠지만, 여기서는 try로 처리했어요. 비동기 처리과정에서 에러가 발생한다면 바로 throw 해줄 수 있게 되었어요.
여기서 Swift의 철학인 "영어로 읽히는 코드"에 적합하게 네이밍 되었다는 것을 느꼈어요.
"try await URLSession-"을 해석하면 "URLSession에서 data를 request 하는 작업을 기다리도록 시도해봐"라고 있는 그대로 해석할 수 있을 것 같아요.
스레드의 상태를 보면 위와 같을 텐데, data라는 작업이 완료될 때까지 멈추는 게 아니라 비동기로 진행되는 data를 기다리며 스레드는 다른 일을 할 수 있어요.
여기서 에러가 발생하지 않고 값이 정상적으로 들어온다면 await이 끝나고 값이 전달되며 다음 줄로 넘어가게 될 거예요.
3번째 줄부터는 다시 동기적으로 작업이 진행되고, 5번째 줄 UIImage를 갱신하는 작업에서 다시 await이 있어요.
UI를 그리는 작업은 다시 비동기적으로 진행해요.
여기서 사용하는 thumbnail은 예시 코드를 작성한 Robert가 직접 만든 코드인데, 메서드뿐만 아니라 프로퍼티 역시 async가 가능하다는 것을 보여주고 있어요.
Swift 5.5부터 property getter도 throw 할 수 있어요.
그래서 만약 프로퍼티에도 throw를 하고 싶다면, 메서드와 마찬가지로 async throw로 표기해줄 수 있어요.
그리고 또 한 가지 중요한 조건이 나와요.
읽기 전용 프로퍼티만 async 할 수 있어요. setter가 있으면 async로 설정할 수 없다는 의미겠죠.
Suspend란?
함수가 동기적으로 동작할 경우 스레드 상황을 살펴볼게요.
그림에서 주황색으로 표시된 thumbnailURLRequest가 동기적으로 동작할 경우, fetchThumbnail에서 thumbnailURLRequest를 부르고 그 결과물이 올 때까지 fetchThumbnail 함수를 동작시키는 스레드는 "occupied" 즉, 다른 일을 하지 않고 대기하고 있어요. 그리고 thumbnailURLRequest가 끝나면 그 이후 동작을 마무리하는 구조로 동작해요.
반면, 비동기적으로 함수가 동작할 경우 suspend라는 개념으로 동작해요.
suspend는 결정을 미룬다는 의미도 될 수 있고, 정학시킨다는 의미처럼 뭔가 멈춘다는 어감을 가지고 있는데 말 그대로 스레드의 사용권을 System에게 미룬다는 의미예요.
data(for:) 메서드가 시작되고 await 상태가 되었을 때 대기 상태 스레드를 System에게 요청하여 "나는 현재 대기 중이니 더 필요한 곳이 있으면 써라"라고 말하게 되고, 시스템은 다른 작업을 이 스레드에서 실행시키다가 data(for:) 작업을 이어나갈 수 있는 상태가 되면 resume 시켜 다시 그 스레드에서 작업을 이어 나가요.
이 개념을 코드에서 살펴볼게요 :)
함수 내의 2번째 줄에서 await가 동작하며 이 작업이 끝날 때까지 점선으로 표기된 블록은 suspend 돼요. URLSession의 data 작업이 끝날 때까지 동작하지 않는 코드 블록이라는 뜻이에요.
그래서 await으로 마킹을 해주는 것이 단지 코드의 양을 줄여서만 좋은 것이 아니라 코드를 이해할 때 이 이후 작업은 URLSession.shared.data(for:)가 끝날 때까지 기다렸다가 일어난다는 의미를 전달할 수 있어요.
정리
async는 해당 function이 suspend 할 수 있게 만들어요.
await로 표시하면 비동기 함수가 해당 async 메서드를 suspend 할 수 있게 해 줘요.
suspension 동안 스레드는 다른 작업을 할 수 있어요.
await로 선언된 작업이 마무리되면 그다음 작업이 resume(재개)돼요.
Test에 async 적용하기
이번 세션에서 가장 흥미롭게 들었던 부분이에요 :)
기존 테스트에서는 비동기 테스트를 진행하기 위해 Expectation이라는 설정을 사용했어야 했어요.
저는 이런 코드가 직관적이지 않다고 생각해서 RxNimble을 사용해서 비동기 테스트를 진행했었어요.
하지만 이제 async / await를 활용해서 보다 간략하고 직관적인 비동기 테스트 코드를 작성할 수 있어요.
위의 코드에 적용해본다면
위와 같이 훨씬 간략해진 것을 볼 수 있어요 ☺️
'iOS 앱개발 > WWDC' 카테고리의 다른 글
[WWDC 20] - # 10028 Meet WidgetKit(2) (0) | 2022.05.04 |
---|---|
[WWDC 20] - # 10028 Meet WidgetKit(1) (0) | 2022.05.04 |
[WWDC 21] #10216 ARC in Swift: Basics and beyond(ARC에 관하여) (0) | 2022.02.22 |
[WWDC 15] - Protocol Oriented Programming(POP) {2편 - OOP를 POP로 변경해보기} (0) | 2021.05.23 |
[WWDC 15] - Protocol Oriented Programming(POP) {1편 - 왜 OOP로는 부족한가?} (0) | 2021.05.20 |
- Total
- Today
- Yesterday
- PassingCars#Codility#Python
- filter#isalnum#lower
- 병든 나이트#BOJ#탐욕법#Python
- NumberofDiscIntersections#Codility#Sort#Python
- Triangle#Sorting#Codility#Python
- django
- 텀 프로젝트#백준알고리즘#Python
- 파이썬알고리즘인터뷰#4장
- django#slicing
- API#lazy#
- 날짜 계산#BOJ#완전탐색#Python
- 미로 탐색#백준알고리즘#Python
- 종이자르기#분할정복#BOJ#Python
- Swift#Tuples#Range
- 암호코드#dp#BOJ#Python
- Distinct#Codility#Python
- N으로 표현#DP#Programmers#Python
- 나무자르기#BOJ#이분탐색#Python
- 랜선자르기#이분탐색#BOJ#Python
- 배열합치기#분할정복#BOJ#Python
- 반복수열#백준알고리즘#Python
- 쿼드트리#BOJ#분할정복#Python
- Brackets#Stacks and Queues#Codility#Python
- 순열사이클#BOJ#Python
- 토마토#백준알고리즘#Python
- 리모컨#완전탐색#BOJ#Python
- 백준 알고리즘#BackTracking
- 공유기 설치#BOJ#이분탐색#Python
- 터틀비치#리콘#xbox#controller
- 섬의개수#백준알고리즘#Python
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |