번역 - Use async / await with URLSession, WWDC 2021

okstring·2021년 10월 6일
1

해당 글은 Use async/await with URLSession 을 번역하며 공부해본 글입니다.

내가 Swift 동시성을 가장 좋아하는 것은 코드를 선형적이고 간결하게 만들고 기본 Swift 오류 처리를 지원한다는 것입니다. 네트워킹은 본질적으로 비동기식이며 iOS 15 및 macOS에서는 Swift 동시성 기능을 활용할 수 있도록 URLSession에 새로운 API를 도입했습니다. 새로운 API를 보여드리기 위해 async/await를 채택하기 위해 작업 중인 앱이 있습니다. 사진 공유 앱이며 이 사진을 즐겨찾기에 추가할 수 있습니다.

다음은 강아지 사진을 가져오는 기존 코드입니다. URLSession에서 completionHandler 기반 편의 메소드를 사용하고 있습니다. 코드는 간단해 보이며 제한된 테스트에서 작동했습니다. 그러나 적어도 세 가지 오류가 있습니다. 본격적으로 살펴보겠습니다. 먼저 제어 흐름을 따라가 보겠습니다.

데이터 작업을 만들고 resume it. 그런 다음 작업이 완료되면 completionHandler로 이동하여 response를 확인하고 이미지를 생성하고 여기서 제어 흐름이 종료됩니다. 흠, 우리는 앞뒤로 점프하고 있습니다. 스레딩은 어떻습니까? 이 작은 코드 조각에 대해 놀라울 정도로 복잡합니다. 총 3개의 서로 다른 실행 컨텍스트가 있습니다.

가장 바깥쪽 레이어는 호출자의 스레드 또는 큐에서 실행되고 URLSessionTask completionHandler는 Session의 Delegate Queue에서 실행되며 최종 completionHandler는 main Queue에서 실행됩니다. 컴파일러는 여기에서 우리를 도울 수 없기 때문에 data races과 같은 스레딩 문제를 피하기 위해 극도의 주의를 기울여야 합니다.

completionHandler에 대한 호출은 main queue에 일관되게 전달되지 않습니다. 이것은 버그일 수 있습니다. 또한, 우리는 여기서 early return을 놓치고 있습니다. 오류가 발생하면 completionHandler를 두 번 호출할 수 있습니다. 이것은 호출자가 만든 가정을 위반할 수 있습니다. 마지막으로 이것은 명확하지 않을 수 있지만 UIImage 생성이 실패할 수 있습니다. 데이터 형식이 잘못된 경우 이 UIImage 초기화 프로그램은 nil을 반환하므로 nil 이미지와 nil 오류가 모두 있는 completionHandler를 호출했을 것입니다. 이것은 예상되지 않을 가능성이 높습니다.

이제 이것은 async/await를 사용하는 새 버전입니다. 제어 흐름은 위에서 아래로 선형이며 이 함수의 모든 것이 same concurrency context에서 실행된다는 것을 알고 있으므로 더 이상 스레딩 문제에 대해 걱정할 필요가 없습니다. 여기에서는 URLSession에서 새로운 비동기 데이터 메서드를 사용했습니다. 현재 실행 컨텍스트를 차단하지 않고 일시 중단하고 성공적으로 완료되면 데이터와 응답을 반환하거나 오류를 throw합니다. 또한 응답이 예상치 못한 경우 오류를 발생시키기 위해 throw 키워드를 사용했습니다. 이를 통해 호출자는 Swift 기본 오류 처리를 사용하여 오류를 포착하고 처리할 수 있습니다. 마지막으로 이 함수에서 선택적 UIImage를 반환하려고 하면 컴파일러가 짖을 것이므로 본질적으로 nil을 올바르게 처리하도록 강제합니다.

다음은 네트워크에서 데이터를 가져오는 데 사용한 메서드의 signatures입니다. URLSession.data 메소드는 URL 또는 URLRequest를 허용합니다. 기존 data task convenience methods와 동일합니다.

또한 데이터를 업로드하거나 파일을 업로드할 수 있는 업로드 방법을 제공합니다. 기존 upload task convenience methods과 동일합니다. 기본 방법인 GET은 업로드를 지원하지 않으므로 요청을 보내기 전에 올바른 HTTP 방법을 설정해야 합니다.

다운로드 메소드는 응답 본문을 메모리가 아닌 파일로 저장합니다. 이 새로운 방법은 다운로드 task convenience methods과 달리 파일을 자동으로 삭제하지 않으므로 직접 삭제하는 것을 잊지 마십시오. 이 예에서는 추가 처리를 위해 파일을 다른 위치로 이동합니다.

Swift concurrency's cancellation은 URLSession 비동기 메서드에서 작동합니다. 취소하는 한 가지 방법은 동시성 Task.Handle을 사용하는 것입니다. 여기서 async를 호출하여 두 리소스를 하나씩 로드하는 동시성 작업을 만듭니다. 나중에 Task.Handle을 사용하여 현재 실행 중인 작업을 취소할 수 있습니다. 동시성 작업은 "Task"이라는 이름을 공유하더라도 URLSessionTask와 관련이 없습니다. 방금 이야기한 데이터, 업로드, 다운로드 등의 메서드는 반환하기 전에 전체 response body가 도착할 때까지 기다립니다. response body을 점진적으로 수신하려면 어떻게 해야 합니까? URLSession.bytes 메소드를 소개하게 되어 기쁩니다. response header가 수신되면 return되고 response body를 bytes의 AsyncSequence로 전달합니다.

지금은 ScrollView를 pull down하여 즐겨찾기 수를 새로 고칠 수 있습니다. 이 즐겨찾기 카운트를 실시간으로 업데이트하고 싶습니다. 그렇게 하면 앱이 훨씬 더 인터랙티브하게 느껴집니다. 이를 위해 백엔드 엔지니어는 사진에 대한 실시간 업데이트를 제공하는 실시간 이벤트 Endpoint를 구축했습니다. response를 확인하기 위해 endpoint를 확인하겠습니다.

response body의 각 줄은 업데이트된 즐겨찾기 수와 같은 사진 업데이트를 설명하는 JSON 데이터입니다. 새로운 비동기 시퀀스 API를 사용하여 endpoint의 response을 사용하고 real-time events가 parse 될 때 즐겨찾기 수를 업데이트하겠습니다. photo collectionView가 나타날 때 호출되는 작업인 onAppearHandler 함수에서 실시간 업데이트를 시작할 수 있습니다. 함수 내에서 새 URLSession.bytes API를 호출하여 새 Endpoint에서 데이터를 가져오겠습니다.

여기에 반환된 바이트 유형은 URLSession.AsyncBytes입니다. 이것은 response body를 점진적으로 소비하는 방법을 제공합니다. 또한 서버에서 성공적인 응답을 받았는지 확인하기 위해 여기에 오류 검사를 추가했습니다.

응답의 각 줄을 JSON 데이터 조각으로 구문 분석하려고 합니다. 그렇게 하려면 AsyncBytes에서 lines 메서드를 사용할 수 있습니다.

이렇게 하면 데이터가 수신될 때 한 줄씩 응답을 사용할 수 있습니다.

루프 내에서 updateFavoriteCount를 호출하여 JSON 데이터를 구문 분석하고 UI를 업데이트할 수 있습니다. UI 업데이트는 main actor에서 발생해야 하므로 await 구문을 사용하여 비동기 함수인 updateFavoriteCount를 호출하고 있습니다. 이제 이 즐겨찾기 수가 실시간으로 업데이트됩니다.

AsyncSequence built-in transformation(lines)을 사용하여 response body를 한 줄씩 parse하는 방법을 보여주었습니다. AsyncSequence는 많은 편의 transformations을 지원하며 FileHandle과 같은 다른 시스템 프레임워크 API와 함께 AsyncSequence를 사용할 수도 있습니다. AsyncSequence에 대해 자세히 알아보려면 "Meet AsyncSequence" 비디오를 시청하는 것이 좋습니다.

URLSession은 authentication challenges, metrics 등과 같은 이벤트에 대한 콜백을 제공하는 delegate model을 중심으로 설계되었습니다. 새로운 비동기 메서드는 더 이상 기본 task을 노출하지 않습니다. 그렇다면 task과 관련된 authentication challenges를 어떻게 처리할까요? 이러한 모든 메서드는 a task-specific delegate리는 추가 argument를 사용하여 이 데이터 업로드, 다운로드 또는 bytes operation과 관련된 delegate messages를 처리하는 개체를 제공할 수 있습니다.

또한 동일한 기능을 활용할 수 있도록 Objective-C의 NSURLSessionTask에 대리자 속성을 도입합니다. Delegate는 완료되거나 실패할 때까지 Task에 의해 강력하게 유지됩니다. Task Delegate는 백그라운드 URLSession에서 지원되지 않습니다. Sesstion Delegate와 Task Delegate 모두에서 메서드가 구현되면 Task Delegate에 있는 메서드가 호출됩니다.

Dogs 앱에는 새로운 비동기 API로 작성된 간단한 data-fetching layer가 있습니다. 사진을 즐겨찾기로 표시하거나 즐겨찾는 모든 사진을 가져오는 것과 같은 일부 데이터 가져오기 tasks의 경우 사용자를 인증해야 합니다. 지금은 즐겨찾기 사진을 탭하면 "승인되지 않음" 오류가 발생합니다. task-specific delegate를 사용하여 사용자 인증을 추가하는 방법을 살펴보겠습니다. 먼저 URLSessionTaskDelegate를 작성해 보겠습니다. 이것을 AuthenticationDelegate라고 부르겠습니다.

AuthenticationDelegate는 URLSessionTaskDelegate 프로토콜을 준수하며 초기화 프로그램에서 signInController의 인스턴스를 허용합니다. 우리가 구현한 signInController 클래스에는 사용자에게 자격 증명을 묻는 메시지를 표시하는 데 사용할 수 있는 몇 가지 유용한 helper 함수가 이미 포함되어 있습니다. 다음으로 URLSession didReceive 챌린지 delegate method를 구현해보자.

delegate 메서드 내에서 사용자에게 자격 증명을 묻는 메시지를 표시하여 HTTP 기본 인증 문제에 응답하도록 선택할 수 있습니다. 물론 오류 처리를 잊어서는 안됩니다. 이제 이 AuthenticationDelegate 클래스를 작업별 대리자로 사용하겠습니다.

그렇게 하려면 인스턴스를 인스턴스화하고 URLSession.data 메서드에 대한 delegate parameter parse하면 됩니다. delegate 개체는 인스턴스 변수가 아니며 작업이 완료되거나 실패할 때까지 task에 의해 강력하게 유지됩니다. 여기서 새로운 점은 delegate를 사용하여 URLSession 작업의 인스턴스와 관련된 이벤트를 처리할 수 있다는 것입니다. 이는 delegate 메서드 내부의 논리가 특정 URLSession 작업에만 적용되고 다른 작업에는 적용되지 않을 때 편리합니다.

profile
step by step

0개의 댓글