-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
async/await 와 URLSession 사용하기(1) - async/await #5
Comments
WWDC 21 에서 async/await 가 소개되었습니다. 세션들의 일부 내용을 가져와서 기존 completionHandler 의 문제가 무엇이었는지, async/await 는 무엇이고 URLSession 과 어떻게 함께 사용하는지 알아봅시다. 기존에 우리는 비동기 작업에서 completion handler 를 사용해왔어요! 아래의 코드를 async/await 를 사용해서 바꾸어 봅시다. (아래는 WWDC21 Use async/await with URLSession 세션의 일부입니다.) 기존의 completion handler 의 문제
보시면 앞뒤로 점프하고 있습니다… 이렇게 제어 흐름이 복잡합니다.
총 3개의 서로 다른 실행 context 가 있을만큼 놀라울 정도로 복잡합니다.
컴파일러는 여기서 도울 수 없기 때문에 data races 와 같은 스레딩 이슈를 피하기 위해서 극도의 주의를 기울여야된다고 합니다.
이는 caller(호출자)가 만든 가정을 위반할 수도 있습니다.
데이터 형식이 잘못된 경우, UIImage init 은 nil 을 반환하므로 nil 이미지와 nil 에러를 가진 completionHandler 를 호출했을 것입니다. (이 부분은 Swift5 부터 도입된 (아래는 WWDC21 Meet async/await in Swift 세션의 일부입니다.)
completionHandler 는 작업 종료시 항상 호출되어야 하는데, 이것은 개발자에게 달려있고, 컴파일러가 검증해주지도 않습니다. 따라서 잠재적으로 버그가 발생할 수 있는 코드가 됩니다.
결과적으로 총 20여줄의 코드를 작성하게 되었고, 버그가 발생할 수 있는 지점은 5곳이 있습니다…
세션에서는 이것을 좀 더 안전하지만, 코드를 더 못생기고 약간 더 길게 만드는 행위라고 이야기 합니다. 🔥 정리하자면..!
문제점이라고 할 수 있겠네요. async/await 적용
iOS 13부터 async/await 를 지원하는 URLSession API 가 추가되었습니다.Xcode 13.2 릴리즈 노트에서 iOS 15 부터 지원되었던 것이 iOS 13 으로 바뀌었습니다!(야-호) Apple Developer Documentation - Xcode 13.2 Release Notes
async함수 이름 뒤에 async 함수는 호출할 때 앞에 func fetchPhoto(url: URL) async throws -> UIImage
(Task 는 비동기 작업의 단위입니다. 비동기 컨텍스트를 생성해서 동기 컨텍스트에서도 비동기를 호출 할 수 있습니다.) await비동기 함수 호출시 potential suspension point(잠재적인 일시 중단 지점)로 지정합니다. let (data, response) = try await URLSession.shared.data(for:request) 비동기 처리 함수를 실행 후, 완료될 때까지 기다리기 위해서 potential suspension point 가 있어야하고
(
평범한 함수를 호출하는 경우 작업이 끝날 때까지 스레드를 점령하고 있습니다.
그런데 async 함수는 suspend 될 수 있고, 그 동안 다른 작업이 실행될 수 있습니다. 어떻게 가능할까요? async 함수는 suspend 되면 스레드 제어권을 포기하고, 시스템에게 넘깁니다. 그래서 다른 작업을 할 수 있고, 적절한 시기에 다시 async 함수를 resume 합니다. async 함수가 끝나면 스레드의 제어권은 해당 함수로 다시 넘어옵니다. 어떻게 멈춘 함수로 돌아가나요? suspension point 에서 유지되는 모든 정보는 힙에 저장되기 때문입니다. 적용해보자!내용
Movie APIAPI Docs](https://developers.themoviedb.org/3/getting-started/introduction) 위의 오픈 API 를 사용했습니다. 사용 방법에 대해서는 API 문서를 확인할 수 있습니다. 제가 사용한 API 들은 다음과 같습니다.
[GET] https://api.themoviedb.org/3/movie/popular?api_key="내 api key"
// 출처: https://developers.themoviedb.org/3/movies/get-popular-movies
// original, w500 은 이미지 사이즈에 따라 설정하면 됩니다.
[GET] https://image.tmdb.org/t/p/original/"이미지 URL"
[GET] https://image.tmdb.org/t/p/w500/"이미지 URL"
// 출처: https://developers.themoviedb.org/3/getting-started/images async/awiat 를 활용한 서버통신과 에러 핸들링.// ✅ async 함수는 concurrent context 에서만 사용이 가능합니다. 그래서 Task 블럭 안에서 실행.
Task {
do {
// ✅ getMovie() 메서드는 async 메서드이고, 에러를 던집니다.
movies = try await getMovie()
movieCollectionView.reloadData()
}
// ✅ do-catch 문을 활용해서 throw 된 에러를 핸들링.
catch MovieDownloadError.invalidURLString {
print("movie error - invalidURLString")
} catch MovieDownloadError.invalidServerResponse {
print("movie error - invalidServerResponse")
}
}
// ✅ 비동기 함수를 구현하기 위해서 async 키워드 사용.
// ✅ 에러를 던지기 때문에 throws 사용.
private func getMovie() async throws -> [Result] {
// Const 구조체에 상수로써 URL 을 관리.
guard let url = URL(string: Const.URL.baseURL + Const.Endpoint.popular + Const.Key.apiKey) else {
throw MovieDownloadError.invalidURLString
}
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw MovieDownloadError.invalidServerResponse
}
let popularMovie = try JSONDecoder().decode(PopularMovie.self, from: data)
return popularMovie.results
} Error Handling 을 위한 열거형 생성import Foundation
enum MovieDownloadError: Error {
/// 유효하지 않은 URL 형식 오류.
case invalidURLString
/// 유효하지 않은 통신 오류.
case invalidServerResponse
} Codable 한 Model 구조체 생성
import Foundation
// MARK: - Movie
struct PopularMovie: Codable {
let page: Int
let results: [Result]
let totalPages, totalResults: Int
enum CodingKeys: String, CodingKey {
case page, results
case totalPages = "total_pages"
case totalResults = "total_results"
}
}
// MARK: - Result
struct Result: Codable {
let adult: Bool
let backdropPath: String
let genreIDS: [Int]
let id: Int
let originalLanguage: String
let originalTitle, overview: String
let popularity: Double
let posterPath, releaseDate, title: String
let video: Bool
let voteAverage: Double
let voteCount: Int
enum CodingKeys: String, CodingKey {
case adult
case backdropPath = "backdrop_path"
case genreIDS = "genre_ids"
case id
case originalLanguage = "original_language"
case originalTitle = "original_title"
case overview, popularity
case posterPath = "poster_path"
case releaseDate = "release_date"
case title, video
case voteAverage = "vote_average"
case voteCount = "vote_count"
}
} 결과111.mov출처: Use async/await with URLSession - WWDC21 - Videos - Apple Developer Meet async/await in Swift - WWDC21 - Videos - Apple Developer 번역 - Use async / await with URLSession, WWDC 2021 [Swift] async / await & concurrency |
The text was updated successfully, but these errors were encountered: