[Swift] 18. 오류처리

Hoojeong Kim·2022년 3월 11일
0

Swift Base

목록 보기
20/22
post-thumbnail

오류처리

오류처리는 프로그램이 오류를 일으켰을 때 이것을 감지하고 회복시키는 일련의 과정이다. 프로그램의 모든 기능이 우리가 원하는대로 정확하게 동작한다는 보장은 없다. 특히나 전달받아야 하는 값이 까다롭거나 데이터를 가공하는 데 소비하는 자원이 많을 경우 오류가 발생할 확률이 높아진다.

이런 가능성이 있는 기능을 구현할 때는 오류가 발생할 수 있음을 고려해야 한다.

오류의 표현

오류를 표현하기 위해서는 Error 프로토콜을 준수해야 한다. 이 프로토콜은 사실 요구사항이 없는 빈 프로토콜이지만, 오류를 표현하기 위해서는 이 프로토콜을 채택해야 한다.


특히 열거형은 오류의 종류를 나타내가에 아주 적합한데, 연관 값을 통해 오류에 관한 부가 정보를 제공할 수도 있다.
enum PhoneError: Error {
	case unknown
    case batteryLow(batteryLevel: Int)
}

오류가 발생하면 정상적인 실행을 할 수 없다는 것을 나타내는데, 그럴 때는 오류를 던져주면 된다.
오류를 던질 때는 throw 구문을 사용한다.

throw PhoneError.batterLow(batteryLevel: 20)
Playground execution terminated: An error was thrown and was not caught:
▿ PhoneError
  ▿ batteryLow : 1 element
    - batteryLevel : 20

오류 포착, 처리

오류를 던질 수 있지만, 그에 따라 던져진 오류를 처리하는 코드도 작성해야 한다. 예를 들어 던져진 오류가 무엇인지 판단해 다시 문제를 해결하는 식으로 말이다.

스위프트에 오류를 처리하기 위한 방법은 4가지가 있다.

  • 함수에서 발생한 오류를 해당 함수를 호출한 코드에 알리는 방법
  • do-catch 구문을 이용하여 오류를 처리하는 방법
  • 옵셔널 값으로 오류를 처리하는 방법
  • 오류가 발생하지 않을 것이라고 확신하는 방법

그렇다면 한 가지씩 알아보자.

함수에서 발생한 오류 알리기

함수에서 발생한 오류를 해당 함수를 호출한 코드에 알리는 방법이다. 함수, 메서드, 이니셜라이저의 매개변수 뒤에 throws 키워드를 사용해 오류를 던질 수 있다.

이런 함수는 호출했을 때, 동작 도중 오류가 발생하면 자신을 호출한 코드에 오류를 알린다.

func checkPhoneBatteryStatus(batteryLevel: Int) throws -> String {
    guard batteryLevel != -1 else { throw PhoneError.unknown }
    guard batteryLevel > 20 else { throw PhoneError.batteryLow(batteryLevel: 20) }
    
    return "배터리 상태가 정상입니다."
}

guard 문의 조건이 false일 때 else문을 실행하여 throw로 인해 unkonwn 오류를 알린 뒤 종료한다.

이 메서드를 사용하기 위해서는, 오류가 발생할 수도 있기 때문에 오류를 처리하는 과정이 필요하다. 발생한 오류를 자신을 호출한 코드에게 알리지만, 받은 코드가 오류를 처리하지 않는다면 이후의 코드가 동작하지 않기 때문이다.

do-catch

함수, 메서드, 이니셜라이저 등에서 오류를 던져주면 오류 발생을 전달받은 코드 블록은 do-catch 구문을 사용해 오류를 처리해야 한다.
do 절 내부의 코드에서 오류를 던지면, catch 절에서 오류를 받아 적절하게 처리하면 된다.

do {
	try 오류 발생 가능 코드
    오류가 발생하지 않으면 실행할 코드
} catch 오류 패턴 1 {
	처리 코드
} catch 오류 패턴 2 {
	처리 코드
}

그렇다면, 위에서 작성한 함수의 오류를 처리해보자.
do {
    try checkPhoneBatteryStatus(batteryLevel: -1)
} catch PhoneError.unknown {
    print("알 수 없는 에러입니다.")
} catch PhoneError.batteryLow(let batteryLevel) {
    print("배터리 전원이 부족합니다. 남은 배터리는 \(batteryLevel)% 입니다.")
} catch {
    print("그 외 오류 발생 : \(error)")
}
알 수 없는 에러입니다.

checkPhoneBatteryStatus 함수를 호출할 때 매개변수로 -1(연관 값)을 전달했으므로 함수에서 unknown 에러를 던질 것이고, catch에서 던져진 unknown 에러를 받아 그에 맞는 블럭을 실행한다.

이때 만약 매개변수가 20이라면, 배터리 전원이 부족하다는 메시지가 출력될 것이다.

옵셔널 값, try?으로 오류처리

try?를 사용해 옵셔널 값으로 변호나하여 오류를 처리할 수도 있다. try? 표현을 통해 동작하던 코드가 오류를 던지면 그 코드의 반환 값은 nil이 된다.

오류가 발생할 수 있는 함수를 호출할 때, 함수 이름 앞에 try?를 붙여 표현한다.

let staus = try? checkPhoneBatteryStatus(batteryLevel: -1)
print(status)
nil

checkPhoneBatteryStatus 함수에서 unknown 에러를 던지기 때문에 nil이 반환된다.


만약 에러가 발생하지 않았다면, 아래와 같이 옵셔널 값이 출력된다.
let status = try? checkPhoneBatteryStatus(batteryLevel: 30)
print(status)
Optional("배터리 상태가 정상입니다.")

오류가 발생하지 않을 것이라고 확신하는 방법, try!

마지막으로 오류가 발생하지 않을 것이라는 확신을 갖고 처리하는 방법이다. 오류가 절대 발생하지 않을 것이라고 확신할 수 있는 상황이라면 이와 같은 전제 하에 try!를 사용할 수 있다.

이 표현은 다른 느낌표 표현(암시적 추출 옵셔널, 강제 타입캐스팅 등)과 마찬가지로 실제 오류가 발생하면 런타임 에러가 발생하여 프로그램이 강제 종료된다.

let status2 = try! checkPhoneBatteryStatus(batteryLevel: -1)
print(status2)
__lldb_expr_15/MyPlayground.playground:19: Fatal error: 'try!' expression unexpectedly raised an error: __lldb_expr_15.PhoneError.unknown
profile
나 애기 개발자 👶🏻

0개의 댓글