아리의 iOS 탐구생활

[Swift] 오류처리(Error Handling)는 어떻게 할까? 본문

Swift/문법

[Swift] 오류처리(Error Handling)는 어떻게 할까?

Ari Lee 2021. 8. 25. 16:00
반응형

🔍  try do-catch

 

Error Handling — The Swift Programming Language (Swift 5.5)

Error Handling Error handling is the process of responding to and recovering from error conditions in your program. Swift provides first-class support for throwing, catching, propagating, and manipulating recoverable errors at runtime. Some operations aren

docs.swift.org

 

오류처리(Error Handling)는 프로그램이 오류를 일으켰을 때 감지하고 복구하는 프로세스이다. 일부 프로그램은 모든 기능이 항상 원하는대로 정확히 동작한다는 보장이 없다. 특히나 전달받아야 하는 값이 까다롭거나 데이터를 가공하는데 소비하는 자원이 많을 경우 오류가 발생할 확률이 높아진다. 이런 가능성이 있는 기능을 구현할 때는 오류가 발생할 수 있음을 항상 고려해야 한다.

 

오류처리를 통해 이런 가능성들을 구별하여 프로그램 자체적으로 오류를 해결하거나 사용자와 상호작용을 통해 오류를 어떤 방향으로 풀어나갈지 제어할 수 있다.

 

 

 

 

🔍  오류의 표현

Swift에서 오류(Error)는 Error라는 프로토콜을 준수하는 타입의 값을 통해 표현된다. Error 프로토콜은 사실상 요구사항이 없는 빈 프로토콜일 뿐이지만 오류를 표현하기 위한 타입(주로 열거형)은 이 프로토콜을 채택한다.

Swift의 enum은 오류의 종류를 나타내기에 아주 적합한 기능이다. 연관값을 통해 오류에 관한 부가 정보를 제공할 수도 있다.

 

 

 

 

🔍  오류의 종류 정의하기

아래는 HTTP 상태 코드를 참고하여 예시로 만들어본 열거형이다.

enum HttpError: Error {
    case badRequest
    case unauthorized
    case forbidden
    case notFound
    case requestTimeOut
    case conflict
}

 

 

 

 

🔍  발생한 오류 던지기

오류는 throws를 이용해 던진다. 던진다는 말이 애매할 수도 있는데,

오류를 던진다 -> 오류를 처리해주는 곳으로 전달해준다 라고 생각하면 될 것 같다.

 

 

함수 혹은 메서드 또는 이니셜라이저에서 오류가 발생할 수 있음을 나타내려면 throws를 함수 선언부 중에 매개변수 뒤에 작성해주면 된다.

func printNumber(_ number: Int) throws {} // void의 경우
func printNumber(_ number: Int) throws -> Int {} // 리턴타입의 경우

 

throws 키워드를 입력해주었다면 함수 구현부에서 조건문과 함께 오류를 return문 대신에 throw 키워드로 다음 예제와 같이 던져줄 수 있다.

func printNumber(_ number: Int) throws -> Bool {
    let text = "숫자출력"
    guard number > 0 else {
        throw HttpError.badRequest
    }
    print("\(text): \(number)")
    return true
}

 

정리하자면,

  1. throws는 오류가 발생할 가능성이 있는 메소드 옆에 써준다.
  2. throw는 오류가 발생할 구간에 써준다.

 

 

예시로 만약 다음과 같이 변수를 받는 케이스가 있다면,

enum HttpError: Error {
    case badRequest
    case unauthorized
    case forbidden
    case notFound(_ errorPrint: String)
    case requestTimeOut
    case conflict
}

 

아래와 에러와 함께 값(String)을 넘겨줄 수도 있다.

꼭 String이 아니어도 넘겨주고 싶은 값을 넘겨주면 된다.

func printNumber(_ number: Int) throws -> Bool {
    let text = "숫자출력"
    guard number > 0 else {
        throw HttpError.notFound("찾을 수가 없습니다.")
    }
    print("\(text): \(number)")
    return true
}

 

 

 

 

🔍  던진 오류 처리하기

오류를 처리할 때에는 try do-catch로 처리할 수 있다.

throw로 예외를 던져주었다면 예외를 받는 곳 printNumber() 메서드를 사용하는 곳이 되겠다.

 

예를 들어 아래와 같이 메서드가 타입 안에 있다고 가정해보자.

struct Test{
    enum HttpError: Error {
        case badRequest
        case unauthorized
        case forbidden
        case notFound(_ errorPrint: String)
        case requestTimeOut
        case conflict
    }
    
    func printNumber(_ number: Int) throws -> Bool {
        let text = "숫자출력"
        guard number > 0 else {
            throw HttpError.notFound("찾을 수가 없습니다.")
        }
        print("\(text): \(number)")
        return true
    }
}

 

그리고 인스턴스 생성 후 메소드를 호출한다.

let test = Test()
test.printNumber(-1)
// error: An error was thrown and was not caught

 

그럼 위에 호출로 에러가 발생하는데 이때 오류가 발생하는 메소드는 앞에 try를 꼭 함께 작성해줘야 한다.

try의 의미는 ‘이 함수가 오류가 발생할 수도 있는데 한번 시도해볼게’ 라고 생각하면 될 것 같다. 말 그대로 시도해보겠다는 선언이다. 

 

그래서 try 선언으로 끝나는 것이 아니라 do-catch로 감싸줘야 오류를 처리할 수 있다.

do {
    try test.printNumber(-1)
} catch {
    print(error)
}

 

위 do-catch문은 오류를 처리하는 가장 간단한 방법이다.

 

활용한다면 아래처럼 오류케이스를 케이스별로 나누어줄 수도 있다.

do {
    try test.printNumber(-1)
} catch Test.HttpError.notFound("찾을 수가 없습니다.") {
    print("숫자 입력을 다시해주세요.")
} catch Test.HttpError.badRequest {
    print("입력을 잘못했습니다. 다시 입력해주세요.")
}

 

케이스별로 처리하기 때문에 switch문으로도 구현할 수 있다.

do {
    try test.printNumber(-1)
} catch {
    switch error {
    case Test.HttpError.notFound(""):
        print("숫자 입력을 다시해주세요.")
    case Test.HttpError.badRequest:
        print("입력을 잘못했습니다. 다시 입력해주세요.")
    default:
        print("")
    }
}

 

 

 

 

🔍  try? 와 try!

let test = Test()
try? test.printNumber(1) // Optional(true)

try? 는 printNumber() 메서드가 오류가 발생할 수도 있다는 것을 뜻한다.

따라서 printNumber가 리턴값을 옵셔널 타입 또는 nil로 받을 수 있게 된다.

에러 대신에 nil을 반환한다고 생각하면 된다.

 

try? test.printNumber(-1) // nil

try! 는 printNumber() 메서드가 오류가 절대 발생하지 않을 자신이 있으면 쓴다.

오류 발생시 런타임 에러가 발생하고 프로그램이 강제종료되니 주의해야 한다.

 

 

 

 

Reference

 

[Swift] 예외처리 (throws, do-catch, try) 하기

안녕하세요 에밀리입니다.

twih1203.medium.com

 

오류처리

야곰의 스위프트 기본 문법 강좌입니다.

yagom.github.io

 

HTTP 상태 코드 - HTTP | MDN

HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

developer.mozilla.org

 

반응형
Comments