학습
Swift 에러 처리 및 예외 처리
Swift 에러 처리 및 예외 처리 — 중급
에러 처리 및 예외 처리
1. 도입 (왜 배워야 하는가)
Swift의 에러 처리 및 예외 처리는 소프트웨어 개발에서 매우 중요한 주제입니다. 실무에서는 다양한 상황에서 에러가 발생할 수 있으며, 이를 적절히 처리하지 않으면 프로그램이 예기치 못한 방식으로 종료되거나 잘못된 데이터를 사용하게 됩니다. 예를 들어, 쇼핑몰 애플리케이션에서 결제 처리 중 네트워크 오류가 발생하면 사용자는 결제를 완료할 수 없게 되고, 이는 고객 경험에 큰 영향을 미칩니다. 따라서, 에러 처리를 통해 안정적인 사용자 경험을 보장하는 것이 중요합니다.
에러 처리를 소홀히 하면 다음과 같은 문제가 발생할 수 있습니다:
- 프로그램 크래시: 예외가 발생했을 때 적절한 처리가 이루어지지 않으면 프로그램이 비정상 종료될 수 있습니다.
- 잘못된 데이터 처리: 에러를 무시하고 프로그램이 계속 실행되면 잘못된 데이터를 기반으로 작업을 진행할 수 있습니다.
- 디버깅 어려움: 에러 처리가 제대로 이루어지지 않으면 문제를 찾고 수정하는 데 어려움을 겪을 수 있습니다.
- 에러(Error): 프로그램 실행 중 발생할 수 있는 문제를 의미합니다. Swift에서는
Error프로토콜을 통해 에러를 정의합니다. - 예외(Exception): 일반적으로 예상치 못한 상황에서 발생하는 문제를 말합니다. Swift에서는 예외를 처리하기 위해
do-catch구문을 사용합니다. - 옵셔널(Optional): 에러가 발생할 수 있는 값을 처리하기 위해 사용되는 Swift의 특수한 데이터 타입입니다. 값이 없을 수도 있는 상황을 안전하게 처리할 수 있게 도와줍니다.
2. 핵심 개념 설명
에러 처리의 핵심 개념은 다음과 같습니다.
| 용어 | 설명 |
|---|---|
| Error | 에러를 정의하는 프로토콜 |
| do-catch | 에러를 처리하는 구문 |
| Optional | 값이 있을 수도, 없을 수도 있는 타입 |
3. 기본 코드 예제
에러를 정의하고 처리하는 기본적인 사용법을 살펴보겠습니다.
enum MyError: Error {
case invalidInput
}
func divideNumbers(_ numerator: Int, _ denominator: Int) throws -> Int {
guard denominator != 0 else {
throw MyError.invalidInput
}
return numerator / denominator
}
do {
let result = try divideNumbers(10, 0)
print("결과: \(result)")
} catch MyError.invalidInput {
print("오류: 0으로 나눌 수 없습니다.")
} catch {
print("알 수 없는 오류가 발생했습니다.")
}
실행 결과:
오류: 0으로 나눌 수 없습니다.
코드 한 줄씩 동작 설명:
enum MyError: Error- 커스텀 에러 타입을 정의합니다.func divideNumbers- 두 숫자를 나누는 함수를 정의합니다. 0으로 나누는 경우 에러를 발생시킵니다.guard denominator != 0- 분모가 0인지 체크하고, 0일 경우 커스텀 에러를 던집니다.return numerator / denominator- 정상적으로 나누기를 수행합니다.do { ... }- 에러 발생 가능성이 있는 코드를 실행합니다.let result = try divideNumbers(10, 0)- 함수 호출 시 에러가 발생할 수 있으므로try키워드를 사용합니다.catch MyError.invalidInput- 특정 에러를 처리합니다.catch- 알 수 없는 기타 에러를 처리합니다.- 에러 처리는 안정적인 애플리케이션 개발에 필수적입니다.
do-catch구문을 사용하여 에러를 안전하게 처리할 수 있습니다.- 커스텀 에러 타입을 정의하여 구체적인 에러를 생성할 수 있습니다.
- 에러를 무시하거나 적절히 처리하지 않으면 디버깅이 어려워질 수 있습니다.
4. 실전 활용 예제
예제 1: 쇼핑몰의 장바구니 구현
장바구니에 상품을 추가할 때 재고가 부족한 경우를 처리합니다.
enum CartError: Error {
case outOfStock
}
class Product {
var name: String
var stock: Int
init(name: String, stock: Int) {
self.name = name
self.stock = stock
}
func addToCart(quantity: Int) throws {
guard stock >= quantity else {
throw CartError.outOfStock
}
stock -= quantity
print("\(quantity)개 \(name)을(를) 장바구니에 추가했습니다.")
}
}
let product = Product(name: "사과", stock: 2)
do {
try product.addToCart(quantity: 3)
} catch CartError.outOfStock {
print("재고가 부족하여 추가할 수 없습니다.")
}
상황 설명: 사용자가 장바구니에 3개의 사과를 추가하려 할 때, 재고가 부족한 경우를 처리합니다.
예제 2: 파일 읽기
파일을 읽을 때, 파일이 존재하지 않는 경우를 처리합니다.
func readFile(at path: String) throws -> String {
let fileManager = FileManager.default
guard fileManager.fileExists(atPath: path) else {
throw NSError(domain: "FileError", code: 404, userInfo: [NSLocalizedDescriptionKey: "파일을 찾을 수 없습니다."])
}
return "파일 내용"
}
do {
let content = try readFile(at: "path/to/file.txt")
print(content)
} catch {
print(error.localizedDescription)
}
상황 설명: 지정된 경로에 파일이 존재하지 않을 경우를 처리합니다.
5. 흔한 실수와 주의사항
실수 1: 에러를 무시하기
// 잘못된 코드
let result = try? divideNumbers(10, 0) // 에러를 무시
주요 문제: 에러를 무시하면, 나중에 문제가 발생할 수 있습니다.
실수 2: catch 블록에서 에러를 처리하지 않기
do {
try divideNumbers(10, 0)
} catch {
// 에러를 처리하지 않음
}
주요 문제: 에러를 처리하지 않으면 사용자가 무엇이 잘못되었는지 알 수 없습니다.
6. 정리
다음에 학습하면 좋은 주제: 비동기 프로그래밍과 에러 처리, 옵셔널 체이닝, Swift의 Result 타입.