Swift 예외 처리 기술 배우기: try, catch, throws 사용 방법

작성일 :

Swift 예외 처리 기술 배우기: try, catch, throws 사용 방법

Swift는 안전하고 간결한 예외 처리를 위해 try, catch, throws 키워드를 제공합니다. 이 글에서는 이 키워드들이 어떤 역할을 하고, 어떻게 활용할 수 있는지에 대해 살펴보겠습니다. 먼저 예외 처리의 기본 개념을 살펴보고, 다양한 사용 사례를 통해 이해를 깊이도록 하겠습니다.

예외 처리 기본 개념

Swift에서는 함수나 메서드가 오류를 발생시킬 수 있는 경우 이를 명시적으로 선언하고 처리할 수 있습니다. 예외 처리는 주로 함수의 정상적인 흐름을 방해할 수 있는 예외 상황을 처리하는데 사용됩니다. 이를 통해 예기치 못한 오류로 인해 프로그램이 중단되는 것을 방지할 수 있습니다.

Swift에서 예외 처리를 하기 위해서는 세 가지 주요 키워드를 사용합니다:

  1. throws: 함수가 오류를 던질 수 있음을 표시합니다.
  2. try: 오류가 발생할 수 있는 코드 앞에 사용합니다.
  3. catch: 던져진 오류를 처리합니다.

throws 키워드

throws 키워드는 함수나 메서드가 오류를 던질 수 있음을 나타냅니다. 이는 함수 선언부에서 사용하는데, 함수의 반환 타입 뒤에 throws 키워드를 추가합니다. 예를 들어, 다음은 파일을 읽는 과정에서 오류가 발생할 수 있는 함수를 정의한 것입니다:

swift
func readFile(at path: String) throws -> String {
    // 파일을 읽는 코드
}

이 함수는 파일 경로를 입력으로 받아 파일 내용을 문자열로 반환하려고 하지만, 파일이 존재하지 않거나 읽기 권한이 없는 등의 상황에서 오류를 던질 수 있습니다.

try 키워드

try 키워드는 오류를 던질 수 있는 함수를 호출할 때 사용됩니다. try 키워드는 오류가 발생할 수 있는 코드 블록 앞에 위치하여 예외 처리를 위한 준비가 되었음을 나타냅니다. 예를 들어, readFile 함수를 사용하는 코드에서 다음과 같이 사용할 수 있습니다:

swift
let filePath = "path/to/file.txt"
let fileContent: String

 do {
    fileContent = try readFile(at: filePath)
} catch {
    print("파일을 읽는 도중 오류 발생: \(error)")
}

여기서 try 키워드는 readFile 함수가 오류를 던질 수 있다는 것을 명시합니다. 실제로 오류가 발생하면 catch 블록으로 제어가 이동합니다.

do-catch 구문

do-catch 구문은 오류 처리의 핵심 구성 요소입니다. do 블록 내에서 오류가 발생할 수 있는 코드를 실행하고, catch 블록 내에서 발생한 오류를 처리합니다. 예를 들어, 다음은 파일을 읽는 예제입니다:

swift
func readFileContents() {
    let filePath = "path/to/file.txt"
    do {
        let content = try readFile(at: filePath)
        print(content)
    } catch let error as FileError {
        switch error {
        case .notFound:
            print("파일을 찾을 수 없습니다.")
        case .noPermission:
            print("파일을 읽을 권한이 없습니다.")
        default:
            print("알 수 없는 오류가 발생했습니다: \(error)")
        }
    } catch {
        print("기타 오류 발생: \(error)")
    }
}

여기서는 여러 종류의 오류를 처리하기 위해 catch 블록 안에 오류 타입을 명시적으로 구분하여 처리하고 있습니다. 이는 오류의 종류에 따라 적절한 대응을 할 수 있도록 도와줍니다.

rethrows 키워드

rethrows 키워드는 함수가 인자로 받은 클로저가 오류를 던질 경우, 해당 오류를 다시 던질 수 있음을 나타냅니다. rethrows 키워드는 주로 고차 함수(high-order function)에서 사용됩니다. 예를 들어, 다음은 오류를 던질 수 있는 클로저를 인자로 받는 함수입니다:

swift
func performOperation(with closure: () throws -> Void) rethrows {
    try closure()
}

이 함수는 closure가 오류를 던질 경우, 이를 다시 호출자에게 던질 수 있습니다. 이렇게 하면 오류 처리 로직을 재사용 가능하게 작성할 수 있습니다.

실제 사용 사례

파일 처리

파일 시스템과 상호작용할 때는 다양한 예외 상황이 발생할 수 있습니다. 파일이 존재하지 않거나, 파일을 읽을 권한이 없거나, 파일 읽기/쓰기 중 오류가 발생할 수 있습니다. 아래는 파일을 읽는 예제입니다:

swift
enum FileError: Error {
    case notFound
    case noPermission
}

func readFile(at path: String) throws -> String {
    // 실제 파일 읽기 코드 (예외 처리를 위해 단순화됨)
    guard path == "valid/path" else {
        throw FileError.notFound
    }
    return "파일 내용"
}

let filePath = "valid/path"
do {
    let content = try readFile(at: filePath)
    print(content)
} catch FileError.notFound {
    print("파일을 찾을 수 없습니다.")
} catch FileError.noPermission {
    print("파일을 읽을 권한이 없습니다.")
} catch {
    print("알 수 없는 오류가 발생했습니다: \(error)")
}

네트워크 요청

네트워크 작업도 많은 예외 상황을 포함합니다. 예를 들어, 서버가 응답하지 않거나, 네트워크 연결이 끊기거나, 데이터가 사용할 수 없는 형식일 수 있습니다. 다음은 네트워크 요청의 예제입니다:

swift
enum NetworkError: Error {
    case badURL
    case requestFailed
    case unknown
}

func fetchData(from urlString: String) throws -> Data {
    guard let url = URL(string: urlString) else {
        throw NetworkError.badURL
    }

    // 실제 네트워크 요청 코드 단순화됨
    guard let data = Data(base64Encoded: "response data") else {
        throw NetworkError.requestFailed
    }
    return data
}

let urlString = "http://valid.url"
do {
    let data = try fetchData(from: urlString)
    print("데이터 받음: \(data)")
} catch NetworkError.badURL {
    print("잘못된 URL입니다.")
} catch NetworkError.requestFailed {
    print("네트워크 요청에 실패했습니다.")
} catch {
    print("알 수 없는 오류가 발생했습니다: \(error)")
}

결론

Swift의 예외 처리 시스템은 매우 강력하면서도 간결하게 사용할 수 있습니다. try, catch, throws, rethrows 키워드를 이용하여 오류를 명확하게 처리할 수 있습니다. 이를 통해 코드의 신뢰성을 높이고, 예외 상황에서도 안정적으로 동작하는 애플리케이션을 만들 수 있습니다. 이러한 예외 처리 기술을 제대로 이해하고 활용하면, 더욱 견고한 Swift 애플리케이션을 개발할 수 있을 것입니다.