3편을 보기전 전편을 보지 않으셨다면
1편 부터 봐주시실 바랍니다! https://velog.io/@little_tail/Swift-Concurrency1%ED%8E%B8async-await
자 그러면 시작해 보겠습니다.
저번 1편때 코드를 다시 가져와 보겠습니다.
func someRequest<Dto: DTOType>(router: Router, completion: @escaping (Result<Dto, Error>) -> Void) {
let urlRequest = router.makeURLRequest()
let task = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
if let error = error {
completion(.failure(error)) // 헨들러
} else if (response as? HTTPURLResponse)?.statusCode != 200 {
completion(.failure(NetworkError.failForStatusCodeChange)) // 헨들러
} else {
guard let data = data else {
completion(.failure(NetworkError.dataIsNil)) // 헨들러
return
}
do {
let decodedData = try JSONDecoder().decode(Dto.self, from: data)
completion(.success(decodedData)) // 헨들러
} catch {
completion(.failure(NetworkError.decodingError)) // 헨들러
}
}
}
task.resume()
}
해당 하는 코드처럼 개발자가 모든 에러나 성공 사례들을 컴플리션 핸들러로 컨트롤 하여야 했습니다.
- 너무 복잡해! -> 흐름 복잡
- 핸들러 잘못짜도 에러가 나지 않아!! -> 문제 파악 어려움
- 이게 무슨 코드야...? -> 코드 가독성 저하
그래서 해당 코드를 1편과 2편을 통해 아래와 같이 구성하였습니다.
func someRequest<Dto: DTOType>(router: Router) async throws -> Dto {
let urlRequest = router.makeURLRequest() // Thread Block
let (data, response) = try await URLSession.shared.data(for: urlRequest)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw NetworkError.failForStatusCodeChange
}
do {
let decodedData = try JSONDecoder().decode(Dto.self, from: data) // Thread Block
return decodedData
} catch {
throw NetworkError.decodingError
}
}
async - await 를 통해 코드를 더욱 간결하게 만들었습니다.
- 코드의 흐름이 마치 동기 코드같아요!
- 해당 코드 에선 동일한
concurrency context
에서 이루어져 쓰레딩 이슈를 해결할수 있습니다.CompletionHandler
없이 코드를 종료 시켜줍니다 -> Throw -> 어디서 무제가 생기는지도 알수있죠.
URLSession 에선 다운로드와 업로드 메서드를 제공합니다.
func download(from url: URL) async throws -> (URL, URLResponse)
func download(for request: URLRequest) async throws -> (URL, URLResponse)
func download(resumeFrom resumeData: Data) async throws -> (URL, URLResponse)
//구현 예시
func downloadFile(from url: URL) async throws -> (URL, URLResponse) {
let (fileURL, response) = try await URLSession.shared.download(from: url)
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
throw URLError(.badServerResponse)
}
return (fileURL, response)
}
func downloadFile(for request: URLRequest) async throws -> (URL, URLResponse) {
let (fileURL, response) = try await URLSession.shared.download(for: request)
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
throw URLError(.badServerResponse)
}
return (fileURL, response)
}
Task {
do {
let url = URL(string: "https://example.com/exampleFile")!
let (fileURL, response) = try await downloadFile(from: url)
// 다운로드된 파일~
print("File downloaded to: \(fileURL)")
} catch {
print("파일 다운로드 실패: \(error)")
}
}
GET은 업로드를 지원하지 않으므로 요청전 올바른 HTTP 방법을 설정해야합니다.
func upload(for request: URLRequest, from data: Data) async throws -> (Data, URLResponse)
func upload(for request: URLRequest, fromFile url: URL) async throws -> (Data, URLResponse)
import Foundation
func uploadFile(to url: URL, from fileURL: URL) async throws -> (Data, URLResponse) {
var request = URLRequest(url: url)
request.httpMethod = "PUT"
let (data, response) = try await URLSession.shared.upload(for: request, fromFile: fileURL)
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
throw URLError(.badServerResponse)
}
return (data, response)
}
Task {
do {
let url = URL(string: "https://example.com/upload")!
let fileURL = URL(fileURLWithPath: "/path/to/your/file.zip")
let (data, response) = try await uploadFile(to: url, from: fileURL)
// 업로드 결과 다루기
} catch {
print("파일 업로드 실패: \(error)")
}
}
생각 외로 분량이 적었습니다..
더많은 양이 나올 부분이라고 생각했었는데 이정도 더군요.
다음편은구조화 동시성
에 대해서 배워보겠습니다.
모두 고생하셨습니다.