Swift에서 **에러 처리(Error Handling)**는 프로그램 실행 중 발생할 수 있는 예외 상황에 대비하여 오류를 처리하고 안정적으로 코드가 실행될 수 있도록 도와줍니다. Swift는 명확하고 안전한 에러 처리를 제공하며, Error 프로토콜과 do-catch 구문, throws 키워드, try 구문 등을 사용해 에러를 처리합니다.
Error 프로토콜
- Swift의 에러 타입은 Error 프로토콜을 준수하는 타입으로 정의됩니다.
- enum을 사용해 에러의 종류를 구체적으로 정의하고, 상황에 맞게 사용할 수 있습니다.
enum NetworkError: Error {
case invalidURL
case noData
case decodingFailed
}
throws 키워드
- throws 키워드는 함수가 에러를 발생시킬 수 있음을 표시하며, 에러를 던질 수 있는 함수를 정의합니다.
- 함수 내부에서 에러가 발생할 가능성이 있는 코드를 포함하고, 호출 시 에러 처리를 요구합니다.
func fetchData(from url: String) throws -> Data {
guard let validURL = URL(string: url) else {
throw NetworkError.invalidURL
}
// 나머지 데이터 처리 코드
return Data()
}
try 구문
- try 키워드는 에러가 발생할 수 있는 함수 호출 앞에 붙이며, 호출 시 에러가 발생할 경우 이를 상위 코드로 전달합니다.
- 에러 처리 방법에 따라 **try, try?, try!**를 사용할 수 있습니다.
- try: 에러를 던질 가능성이 있는 코드 앞에 사용하며, do-catch 구문으로 에러를 처리해야 합니다.
- try?: 에러가 발생할 경우 nil을 반환하고, 옵셔널로 결과를 처리합니다.
- try!: 에러가 발생하지 않을 것을 확신할 때 사용하며, 에러가 발생하면 런타임 오류가 발생합니다.
do {
let data = try fetchData(from: "https://example.com")
print("Data fetched successfully: \(data)")
} catch {
print("Error fetching data: \(error)")
}
do-catch 구문
- do-catch 구문은 에러가 발생할 가능성이 있는 코드를 실행하고, 에러가 발생할 경우 해당 에러를 처리하는 구문입니다.
- catch 블록에서 특정 에러를 처리할 수 있으며, 에러의 종류에 따라 분기 처리가 가능합니다.
do {
let data = try fetchData(from: "https://example.com")
print("Data fetched successfully: \(data)")
} catch NetworkError.invalidURL {
print("Invalid URL provided.")
} catch NetworkError.noData {
print("No data received from the server.")
} catch {
print("An unknown error occurred: \(error)")
}
defer 구문
- defer 구문은 코드 블록이 종료되기 직전에 실행할 코드를 작성할 때 사용합니다. 예를 들어, 파일 닫기, 리소스 해제와 같은 작업을 수행할 때 유용합니다.
- do-catch 구문 내부에서도 defer를 사용하여 에러 발생 여부와 관계없이 마지막에 반드시 실행될 코드를 지정할 수 있습니다.
func readFile() throws {
let file = openFile()
defer {
closeFile(file)
}
// 파일 처리 코드
}
아래는 fetchData 함수에서 발생할 수 있는 네트워크 에러를 처리하는 예시입니다. throws와 do-catch 구문을 활용해 에러 발생 시 상황에 맞는 메시지를 출력합니다.
enum NetworkError: Error {
case invalidURL
case noData
case decodingFailed
}
func fetchData(from url: String) throws -> Data {
guard let validURL = URL(string: url) else {
throw NetworkError.invalidURL
}
// 서버에서 데이터 가져오기 시도 (예시)
let data: Data? = nil // 실제 네트워크 코드 대신 예시로 nil을 사용
guard let fetchedData = data else {
throw NetworkError.noData
}
return fetchedData
}
do {
let data = try fetchData(from: "https://example.com")
print("Data fetched successfully: \(data)")
} catch NetworkError.invalidURL {
print("Invalid URL provided.")
} catch NetworkError.noData {
print("No data received from the server.")
} catch {
print("An unknown error occurred: \(error)")
}
요약
- Error 프로토콜: 에러 타입을 정의하여 다양한 에러 상황에 대비합니다.
- throws 키워드: 에러를 던질 수 있는 함수임을 표시합니다.
- try 구문: 에러 발생 가능성이 있는 함수 호출 앞에 사용하며, try, try?, try!를 상황에 맞게 선택합니다.
- do-catch 구문: 에러 발생 시 catch 블록에서 에러를 처리합니다.
- defer 구문: 코드 블록 종료 전에 반드시 실행될 코드를 지정합니다.
옵셔널을 사용한 에러 처리와 do-catch를 사용하는 에러 처리의 차이는 무엇인가요?
옵셔널을 사용한 에러 처리와 do-catch를 사용한 에러 처리의 차이는 에러의 발생 여부를 어떻게 다루고, 에러에 대해 얼마나 상세히 처리할 수 있는지에서 발생합니다. 옵셔널은 에러 발생 시 nil로 결과를 표현하는 반면, do-catch는 에러를 구체적으로 처리할 수 있는 구조를 제공합니다. 이 차이는 주로 에러를 간단히 무시할지 또는 에러를 구체적으로 다뤄야 하는지에 따라 선택하게 됩니다.
1. 옵셔널을 사용한 에러 처리 (try?)
try? 구문을 사용하면, 에러가 발생할 수 있는 함수 호출 결과를 옵셔널 타입으로 반환합니다. 이 방식은 에러가 발생했을 때 해당 값을 nil로 표현하여, 에러에 대한 구체적인 처리를 생략하고 단순히 값이 없음을 표시하는 데 유용합니다.
- 장점: 코드가 간결해지고, 에러 발생 여부를 무시하고 간단하게 nil로 처리할 수 있습니다.
- 단점: 에러가 발생한 원인을 알 수 없고, 에러에 대한 구체적인 처리가 어렵습니다.
func fetchData() throws -> String {
throw NSError(domain: "DataError", code: 1, userInfo: nil)
}
let result = try? fetchData()
print(result) // 출력: nil (에러 발생 시)
위 코드에서 try?를 사용해 fetchData() 함수가 던지는 에러를 자동으로 nil로 변환합니다. 에러가 발생했음을 알 수는 있지만, 에러의 상세한 원인에 대해서는 알 수 없습니다.
2. do-catch를 사용한 에러 처리
do-catch 구문은 에러를 명시적으로 처리하고, 발생한 에러를 분기하여 상세히 다룰 수 있는 구조를 제공합니다. do 블록 내에서 에러가 발생하면 catch 블록에서 해당 에러를 잡아내며, 에러의 종류에 따라 다양한 대응 방식을 설정할 수 있습니다.
- 장점: 에러의 원인과 종류에 따라 구체적인 처리가 가능하며, 필요한 대응을 설정할 수 있습니다.
- 단점: 코드가 다소 길어지지만, 에러 처리가 필요한 상황에서는 명확하고 안정적인 구조를 제공합니다.
enum NetworkError: Error {
case noData
case invalidURL
}
func fetchData() throws -> String {
throw NetworkError.noData
}
do {
let result = try fetchData()
print("Data received: \(result)")
} catch NetworkError.noData {
print("Error: No data received.")
} catch NetworkError.invalidURL {
print("Error: Invalid URL.")
} catch {
print("Unknown error: \(error)")
}
위 코드에서 do-catch 구문을 통해 에러의 종류에 따라 상세히 처리합니다. catch 블록을 여러 개 사용해, 각 에러 타입에 맞는 맞춤형 메시지를 출력할 수 있습니다.
요약
- **옵셔널을 사용한 에러 처리 (try?)**는 에러가 발생했을 때 간단히 nil로 처리하며, 에러 원인을 다루지 않고 결과가 없음을 표현하고자 할 때 사용됩니다.
- do-catch 에러 처리는 에러를 상세히 다루고, 발생한 에러의 종류에 따라 구체적인 처리를 할 때 사용됩니다. 각 에러에 대해 맞춤형 대응이 가능하므로, 안정성이 중요한 코드에 적합합니다.
옵셔널 에러 처리는 간단한 경우에 유용하고, do-catch는 더 복잡한 에러 처리 상황에서 사용됩니다. 상황에 맞게 선택하면 코드의 안정성과 가독성을 높일 수 있습니다.
에러를 전파하는 방법은 무엇인가요?
**에러 전파(Error Propagation)**는 하위 함수에서 발생한 에러를 상위 함수로 전달하여, 상위 함수에서 에러를 처리하도록 하는 방법입니다. Swift에서는 throws 키워드를 통해 에러를 발생시키고 전파할 수 있으며, 호출한 함수 쪽에서 이를 받아 적절히 처리하게 됩니다.
Swift에서 에러를 전파하는 방법
에러 전파는 throws와 try 키워드를 사용해 구현됩니다. 아래는 에러 전파의 구체적인 과정입니다.
- throws 키워드로 에러가 발생할 수 있음을 표시
- 함수 선언에서 throws 키워드를 사용해 해당 함수가 에러를 던질 수 있음을 표시합니다. 이 함수는 에러가 발생할 경우 이를 상위로 전파할 수 있습니다.
- 호출 시 try 키워드를 통해 에러가 발생할 수 있음을 표시
- throws 함수는 호출 시 try 키워드를 통해 호출해야 합니다. try는 에러가 발생할 수 있음을 나타내며, 호출한 함수로 에러를 전파하거나 처리할 준비를 하게 합니다.
- 상위 함수에서 에러 처리
- 상위 함수는 do-catch 구문을 사용해 에러를 처리하거나, 또다시 throws로 에러를 전파할 수 있습니다. 이처럼 에러를 연쇄적으로 전파하여 최종적으로 한 곳에서 처리하는 방식으로 구현할 수 있습니다.
enum NetworkError: Error {
case invalidURL
case noData
}
// 데이터 가져오기 함수 정의
func fetchData(from url: String) throws -> String {
guard url.hasPrefix("https") else {
throw NetworkError.invalidURL
}
// 데이터가 없는 상황을 가정
throw NetworkError.noData
}
// 데이터 출력 함수 정의
func printData(from url: String) throws {
let data = try fetchData(from: url)
print("Data received: \(data)")
}
// 최종적으로 에러 처리
do {
try printData(from: "http://example.com")
} catch NetworkError.invalidURL {
print("Error: Invalid URL.")
} catch NetworkError.noData {
print("Error: No data received.")
} catch {
print("Unknown error: \(error)")
}
에러 전파 과정 설명
- fetchData(from:) 함수에서 NetworkError.invalidURL 또는 NetworkError.noData 에러가 발생할 수 있으며, throws 키워드로 에러가 발생할 가능성을 표시합니다.
- printData(from:) 함수에서 fetchData(from:) 함수를 호출할 때 try 키워드를 사용하여 에러를 상위로 전파합니다.
- 최상위 do-catch 블록에서 printData(from:)의 에러를 catch 블록에서 처리합니다. 이 과정에서 에러의 종류에 따라 각기 다른 대응을 할 수 있습니다.
에러 전파와 관련된 주요 개념
- 연쇄 전파: 여러 함수 호출 단계에서 throws와 try를 통해 에러가 상위로 연쇄적으로 전파될 수 있습니다.
- 최종 처리: 에러 전파는 최종적으로 한 곳에서 처리되며, 최상위에서 에러를 처리하는 구조로 작성하면 코드의 가독성과 유지보수성이 높아집니다.
에러 전파의 요약
- throws: 함수 선언 시 throws를 사용하여 에러가 발생할 수 있음을 명시하고, 발생한 에러를 상위로 전파합니다.
- try: throws 함수 호출 시 사용하여 에러가 발생할 수 있음을 표시하고, 에러를 상위로 전파하거나 처리할 준비를 합니다.
- do-catch: 최상위 함수에서 에러를 구체적으로 처리하여 안전한 코드 실행을 보장합니다.
에러 전파는 하위 함수에서 발생한 에러를 상위 함수에서 처리하도록 전달함으로써, 코드의 흐름을 일관성 있게 관리하고 에러를 한 곳에서 효과적으로 처리할 수 있도록 돕습니다.
'정보 > 레벨 1' 카테고리의 다른 글
iOS 앱에서 Multi-threading을 구현하는 방법은 무엇인가요? (0) | 2024.11.13 |
---|---|
메모리 관리에서 강한 참조(Strong Reference)와 약한 참조(Weak Reference)의 차이점은 무엇인가요? (0) | 2024.11.13 |
Git에서 브랜치(Branch)를 사용하는 이유와 장점은 무엇인가요? (0) | 2024.11.12 |
Swift의 고차 함수(Higher-Order Functions)에 대해 설명해주세요. (0) | 2024.11.12 |
의존성 관리 도구(CocoaPods, Carthage, Swift Package Manager)의 종류와 차이점은 무엇인가요? (2) | 2024.11.12 |