🔥 에러 처리

517자
6분

프로그래밍을 하다 보면 예외 상황이 발생할 수 있어요. 이런 상황을 적절히 처리하는 것이 중요하죠. Swift에서는 Error 프로토콜을 채택한 타입을 사용하여 에러를 표현해요.

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}
swift

위의 코드에서는 PrinterError라는 열거형을 정의하고 있어요. 이 열거형은 프린터 관련 에러를 나타내는데, outOfPaper, noToner, onFire와 같은 케이스를 가지고 있네요.

에러를 던지려면 throw 키워드를 사용하고, 에러를 던질 수 있는 함수라는 것을 표시하기 위해 throws 키워드를 사용해요. 함수 내에서 에러를 던지면, 함수는 즉시 반환되고 함수를 호출한 코드에서 에러를 처리하게 되죠.

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}
swift

위의 함수를 살펴볼까요? send(job:toPrinter:) 함수는 Int 타입의 jobString 타입의 printerName을 매개변수로 받아요. 만약 printerName이 "Never Has Toner"라면, PrinterError.noToner 에러를 던지게 되죠. 에러가 발생하지 않으면 "Job sent"라는 문자열을 반환해요.

에러를 처리하는 방법에는 여러 가지가 있어요. 그 중 하나는 do-catch 구문을 사용하는 거예요. do 블록 내에서 에러를 던질 수 있는 코드 앞에 try를 붙이고, catch 블록에서 에러를 처리하죠. 별도의 이름을 지정하지 않으면 에러는 자동으로 error라는 이름을 갖게 돼요.

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
// "Job sent" 출력
swift

위의 코드에서는 send(job:toPrinter:) 함수를 호출하고 있어요. 에러가 발생하지 않으면 printerResponse에 "Job sent"가 할당되고, 이를 출력하죠. 에러가 발생하면 catch 블록에서 에러를 출력해요.

특정 에러를 처리하기 위해 여러 개의 catch 블록을 제공할 수도 있어요. switch문의 case처럼 catch 뒤에 패턴을 작성하면 되죠.

do {
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} catch PrinterError.onFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
    print("Printer error: \(printerError).")
} catch {
    print(error)
}
// "Job sent" 출력
 
swift

위의 코드에서는 PrinterError.onFire 에러를 별도로 처리하고 있어요. 그 외의 PrinterError 에러는 printerError라는 이름으로 바인딩하여 처리하고 있죠. 마지막 catch 블록은 그 외의 모든 에러를 처리해요.

에러를 처리하는 또 다른 방법은 try?를 사용하여 결과를 옵셔널로 변환하는 거예요. 함수가 에러를 던지면 특정 에러는 무시되고 결과는 nil이 되죠. 에러가 발생하지 않으면 함수가 반환한 값을 담은 옵셔널이 결과가 돼요.

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")
swift

위의 코드에서 printerSuccess는 옵셔널 문자열이 되고, printerFailurenil이 되겠죠?

defer 키워드를 사용하면 함수의 다른 모든 코드가 실행된 후, 함수가 반환되기 직전에 실행되는 코드 블록을 작성할 수 있어요. 함수가 에러를 던지는지 여부와 관계없이 코드가 실행되죠. defer를 사용하면 설정 코드와 정리 코드를 나란히 작성할 수 있어요. 실행 시점은 달라도 말이죠.

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
 
func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }
 
    let result = fridgeContent.contains(food)
    return result
}
 
if fridgeContains("banana") {
    print("Found a banana")
}
print(fridgeIsOpen)
// "false" 출력
swift

위의 코드에서 fridgeContains(_:) 함수는 fridgeIsOpentrue로 설정한 후, defer 블록에서 fridgeIsOpen을 다시 false로 설정하고 있어요. 함수가 반환되기 직전에 defer 블록이 실행되므로, fridgeIsOpen은 항상 false로 설정되겠죠?

에러 처리는 프로그래밍에서 매우 중요해요. Swift는 에러 처리를 위한 다양한 방법을 제공하고 있죠. 상황에 맞는 적절한 에러 처리 방식을 선택하여 사용하면 됩니다. 그러면 더 안정적이고 예외 상황에 잘 대응할 수 있는 코드를 작성할 수 있을 거예요!