티스토리 뷰
GitHub 레포 하나 소개하고 오늘 글을 시작해보겠습니다. Swift Programming Language Evolution 이라고 Apple에서 운영하는 레포입니다. Swift 언어의 발전을 위해 모두 제안을 할 수 있는 공간이자 다음 Swift 언어가 어떤 방향을 가지고 발전할지 알 들여다 볼 수 있는 곳이기도 합니다.
오늘 dataTask에서 발생할 수 있는 Error의 종류에 대해 다뤄 볼 예정인데 이에 앞서 Result type에 대한 얘기도 해보려구요. SE-0235 에서 Swift 표준 라이브러리에 Result 타입이 처음 소개됩니다. 특히나 비동기 API를 사용한다면 코드가 복잡해지니 간단하고 명확한 방식으로 Error 처리를 할 필요가 있어 Swift 초창기부터 사람들이 Result 타입을 요구했다고 합니다. 그리고는 Swift5에 패치되었다능! (아주 따끈따끈한 친구지요~?)
Result의 구조
public enum Result<Success, Failure: Error> {
// 'success'는 Success 값을 저장
case success(Success)
// 'failure'는 Failure 값을 저장
case failure(Failure)
}
extension Result where Failure == Swift.Error {
@_transparanet
public init(catching body: () throws -> Success) { }
}
extension Result: Equatable where Success: Equatable, Failure: Equatable { }
extension Result: Hashable where Success: Hashable, Failure: Hashable { }
위에서 보시다시피 success와 failure는 generic으로 구현이되어 있기 때문에 어떤 연관값도 받을 수 있습니다. 다만 failure는 Swift의 Error에 conform 해야하는 조건이 있어요. 그렇기 때문에 Error를 채택해서 custom Error를 만들어낼 수 있어요. NetworkError, AuthenticationError 처럼!
URLSession의 dataTask를 통해 데이터를 주고 받는 통신을 하다보면 예기치 못한 네트워크 오류를 마주칠 수 있습니다. 그래서 적절한 error handling이 필요합니다. 때에 따라서는 재요청을 보낼 수도 있어야하고 언제는 사용자에게 재입력을 요구할 수도 있어야 하겠죠. A Error일 때는 B처럼 행동하고 C Error 일 때는 D처럼 행동하는 등 Error가 명확히 정의되어 있으면 error를 다뤄야 하는 개발자도 실수를 줄일 수 있고, 사용자는 불편함 없이 서비스를 사용할 수 있겠죠.
enum NetworkError: Error
URLSession의 dataTask를 활용해 응답을 받아 온 상황을 처리하는 코드는 아래와 비슷하게 작성하시겠죠?
typealias NetworkResult = (Result<Data, NetworkError>) -> ()
func fetchData(completion: @escaping NetworkResult) {
let request = URLRequest(url: URL(string: "...")!)
URLSession.shared.dataTask(with: request) { data, response, error in
...
// error handling
...
DispatchQueue.main.async {
completion(.success(data))
}
}.resume()
}
URLRequest를 URL을 통해 만들 때와 주석으로 error handling으로 작성한 부분에서 몇 가지 상황이 발생합니다. 우선 제가 새롭게 만든 NetworkError 타입을 살펴보시고 각각 상황에 대해 살펴보겠습니다. (가장 기본적인 형태(?)라고 생각하고 충분히 더 세분화 가능합니다.)
enum NetworkError: Error {
case invalidURL
case transportError
case serverError(code: Int)
case missingData
case decodingError
}
저는 주로 위와 같이 작성해두고 사용합니다. 다음과 같은 상황에 사용합니다.
1. invalidURL
String 값으로 URL을 생성하려고 시도한다면(URL(string: "...")) optional 값이 반환 됩니다. 유효하지 않은 URL일 수도 있기 때문에 걸러주기 위함인데요, 이 때 guard문 등을 사용해서 적절한 error handling을 하면 좋을 것 같습니다. 저는 주로 아래와 같이 사용해요.
guard let url = URL(string: "...") else {
#if DEBUG
print("\(#function) - Error - invalid url")
#endif
completion(.failure(.invalidURL))
}
2. transportError
URLSession을 통해 2가지 error를 판단할 수 있는데 그 중 하나가 transport error, 다른 하나가 3번에 있는 server error 입니다. Transport error는 서버에게까지 가다가 문제가 생길 경우 Error? 파라미터에 담아져서 옵니다.
3. serverError
Server-side에서 발생하는 error는 Response?에 담겨져 와요. 우리의 요청사항은 전달이 되었지만 서버에서 문제가 생겨서 Response? 파라미터에 담아져서 들어와요. 서버에서 문제가 생겼을 경우 400번대 에러, 정상은 200번대인데 조금 더 세분화 할 수 있는 부분이 이 부분이죠.
4. missingData
Data 파라미터도 optional 형태로 오는 이유는 data가 없을 수도 있기 때문입니다. 어떤 종류의 데이터가 넘어와야 하는데 유실되었는 등 data가 없다면 이 역시 적절히 handling을 해줘야 하죠.
5. decodingError
Decoding error는 client side에서 개발자가 전달받은 데이터를 가공해서 사용할 수 있도록 파싱을 해야하는데 문제가 생겼는 경우에요. 이 때는 통신에 문제가 생겼다기 보다 개발자가 모델을 잘못 만들었는 등의 실수가 있는 경우라 위와 마찬가지로 저는 이렇게 사용합니다.
case .success(let data):
guard let recipe = try? JSONDecoder().decode(Recipe.self, from: data) else {
#if DEBUG
print("Decoding ERROR : \(#function)")
#endif
completion(.failure(.decodingError))
return
}
completion(.success(recipe))
'iOS' 카테고리의 다른 글
[iOS] 제네릭을 이용한 코드 중복 해결 (0) | 2021.12.06 |
---|---|
[iOS] UICollectionView와 관련 된 경험들 (2) | 2021.12.05 |
[iOS] Multi-threading과 Concurrency(1) : Thread, Queue, Race Condition (0) | 2021.11.07 |
[iOS] Frame vs Bounds (0) | 2021.10.29 |
[iOS] URLSession 짚어보기 (0) | 2021.10.27 |
- Total
- Today
- Yesterday
- ios
- 개발자인턴
- 삼성소프트웨어아카데미
- SWIFT
- 인턴
- 소프트웨어역량시험
- 데이터분석
- OS
- 보안
- 알고리즘
- TableView
- 소프트웨어아카데미
- 코테
- 부스트캠프
- nosql
- 컴과졸작
- firebase
- 프로그래머스
- 컴공졸작
- 운영체제
- C++
- 부캠
- swacademy
- 컴퓨터공학
- RxSwift
- 부스트캠프2020
- 삼성
- 졸업작품
- 커넥트재단
- 코딩테스트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |