[Swift 기초문법 - 17] Error
Swift에서 Error 프로토콜은 오류 처리를 위한 표준 인터페이스를 제공한다. Swift의 오류 처리 모델은 명확하고 안전하게 오류를 처리할 수 있도록 설계되어 있다. 이번 포스팅에서는 Swift의 Error 프로토콜, 오류 처리 방법, 그리고 커스텀 오류 타입을 만드는 방법에 대해 알아보겠다.
Swift의 Error 프로토콜
Swift에서는 오류를 나타내기 위해 Error 프로토콜을 사용한다. 이 프로토콜은 빈 프로토콜로, 특별한 요구사항이 없다. 즉, Error 프로토콜을 채택하기만 하면 어떤 타입이든지 오류로 사용할 수 있다.
enum NetworkError: Error {
case badURL
case requestFailed
case unknown
}
위 예제에서 NetworkError는 Error 프로토콜을 채택한 열거형(enum)으로, 여러 종류의 네트워크 오류를 정의하고 있다.
오류 던지기와 잡기
Swift에서 오류를 던지기 위해서는 throw 키워드를 사용한다. 오류를 던질 수 있는 함수는 throws 키워드를 사용하여 선언해야 한다. 오류를 처리하기 위해서는 do-catch 구문을 사용한다.
func fetchData(from url: String) throws -> String {
guard url != "" else {
throw NetworkError.badURL
}
// 네트워크 요청을 시뮬레이션하는 코드
let success = Bool.random()
if success {
return "데이터를 성공적으로 가져왔습니다."
} else {
throw NetworkError.requestFailed
}
}
do {
let data = try fetchData(from: "")
print(data)
} catch NetworkError.badURL {
print("잘못된 URL입니다.")
} catch NetworkError.requestFailed {
print("요청이 실패했습니다.")
} catch {
print("알 수 없는 오류가 발생했습니다: \\(error)")
}
위 예제에서 fetchData 함수는 URL이 빈 문자열인 경우 NetworkError.badURL 오류를 던진다. do-catch 구문을 사용하여 오류를 잡고, 각 오류에 대해 적절한 처리를 할 수 있다.
import UIKit
enum ErrorList : Error {
case numberMismatch
case nameMismatch
}
func guessMyName(name input : String) throws{
print("called guess name")
if(input != "힛해"){
print("틀립니다")
throw ErrorList.nameMismatch
}
print("맞았습니다")
}
do{
try guessMyName(name: "ㅇ")
}catch{
print(error)
}
이렇게 하면 에러에서 함수 호출이 종료되고 에러가 무엇인지 catch문에서 볼 수 있다
실행결과
이때 try? 를 하면 에러를 안볼 수 있다.
Result 타입을 사용한 오류 처리
Swift 5에서는 Result 타입을 도입하여 더 간결하고 명확하게 오류를 처리할 수 있다. Result 타입은 성공과 실패를 모두 표현할 수 있는 열거형이다.
func fetchData(from url: String) -> Result<String, NetworkError> {
guard url != "" else {
return .failure(.badURL)
}
let success = Bool.random()
if success {
return .success("데이터를 성공적으로 가져왔습니다.")
} else {
return .failure(.requestFailed)
}
}
let result = fetchData(from: "")
switch result {
case .success(let data):
print(data)
case .failure(let error):
switch error {
case .badURL:
print("잘못된 URL입니다.")
case .requestFailed:
print("요청이 실패했습니다.")
case .unknown:
print("알 수 없는 오류가 발생했습니다.")
}
}
위 예제에서 fetchData 함수는 Result 타입을 반환하며, 성공 시 데이터 문자열을, 실패 시 NetworkError를 반환한다. switch 구문을 사용하여 결과를 처리할 수 있다.
Return 값과 에러를 같이 내보내고 싶을 때
아래와 같이 코드를 만들면 된다!
// option + command + /
/// <#Description#> 메소드에 대한 설명
/// - Parameter input: input 값이 무엇인지
/// - Returns: return되는 건 무엇인지
func guessMyNumber(num input : Int) throws -> Bool{
print("called guess name")
if(input != 10){
print("틀립니다")
throw ErrorList.numberMismatch
}
print("맞았습니다")
return true
}
do{
let receivedValue = try guessMyNumber(num: 9)
}catch{
print(error)
}
실행결과
만약 매개변수로 10이 들어간다면 값의 반환이 잘 될 것이다.
커스텀 오류 타입
사용자 정의 오류 타입을 만들 때는 주로 열거형(enum)을 사용한다. 각 오류 케이스에 관련 데이터를 추가할 수도 있다.
swiftenum FileError: Error {
case fileNotFound(String)
case unreadable
case encodingFailed
}
func readFile(named fileName: String) throws -> String {
if fileName == "missing.txt" {
throw FileError.fileNotFound(fileName)
} else if fileName == "unreadable.txt" {
throw FileError.unreadable
} else {
return "파일 내용을 성공적으로 읽었습니다."
}
}
do {
let content = try readFile(named: "missing.txt")
print(content)
} catch FileError.fileNotFound(let fileName) {
print("\\(fileName) 파일을 찾을 수 없습니다.")
} catch {
print("알 수 없는 오류가 발생했습니다: \\(error)")
}
위 예제에서 FileError는 파일 관련 오류를 나타내는 사용자 정의 오류 타입이다. readFile 함수는 파일 이름에 따라 적절한 오류를 던진다.
결론
Swift의 Error 프로토콜과 관련된 기능들은 오류를 명확하고 안전하게 처리할 수 있도록 도와준다. do-catch 구문, Result 타입, 그리고 커스텀 오류 타입을 통해 다양한 방식으로 오류를 처리할 수 있다. 이를 통해 더 견고하고 유지보수하기 쉬운 코드를 작성할 수 있다.