Do-Catch: Swift에서의 구문과 에러 처리 개요

저자: IT Sectr 게시일: 2026-05-26 읽는 시간: 8 분

Do-Catch는 throw를 통해 발생된 예외 사항을 잡아 해당 코드 블록을 실행하는 Swift 언어의 에러 처리 구문입니다. 다른 언어의 전통적인 try-catch와 달리 Swift는 throws 수식자를 사용하여 에러를 발생시킬 수 있는 함수를 명시적으로 표시해야 합니다. Swift.org, 2026에 따르면 Do-Catch는 Swift에서 타임 안전성을 제공하는 주요 에러 처리 메커니즘입니다.

주요 포인트

  • Do-Catch는 try와 throw가 있는 블록에서 에러를 잡는 Swift 구문입니다.
  • throws는 함수가 에러를 발생시킬 수 있다는 것을 나타내는 함수 마커입니다.
  • try는 do 블록 내에서 throwing 함수를 호출하기 위한 키워드입니다.
  • catch는 유형변 패턴 매칭이 있는 에러 처리 블록입니다.
  • try?try!는 에러를 nil로 변환하거나 크래시를 일으키는 대체 형태입니다.

Do-Catch란 뭐인가?

Do-Catch는 에러를 발생시킬 수 있는 코드가 실행되는 do 블록과 해당 에러를 처리하는 하나 이상의 catch 블록으로 구성된 Swift의 구문입니다. do 블록에는 try로 표시된 throwing 함수의 호출이 포함되고, catch 블록은 유형에 따라 에러를 매칭합니다.

Swift는 Objective-C의 예왘과 달리 구조화된 에러 처리 모델을 구현합니다. 여기서 에러는 스택 언와인딩이 있는 예왘가 아니라 Error 프로토콜에 따르는 값입니다. throw를 통한 에러 발생은 스택 언와인딩의 오버헤드 없이 가장 가까운 catch 블록으로 제어를 전달합니다.

Swift의 핵심 기능은 명시적 처리입니다. 컴파일러는 try, do-catch 또는 대체 메커니즘(try?, try!) 없이 throwing 함수를 호출하는 것을 허용하지 않습니다. 이를 통해 에러가 무시되는 상황이 제거됩니다.

Swift의 에러 모델: Error 프로토콜과 throwing 함수

Swift에서 에러 처리의 핵심에는 Error 프로토콜이 있습니다. 이 프로토콜에 따르는 임의 유형은 throw를 통해 발생시킬 수 있습니다. 에러는 일반적으로 Error에 따르는 enum으로 정의됩니다.

Error 프로토콜

Error는 빈(마커) 프로토콜입니다. 메서드 구현이 필요하지 않고, 준수만 필요합니다. Swift 컴파일러는 정적 검사에 Error를 사용합니다: throws가 있는 함수는 Error에 따르는 값만 발생시킬 수 있습니다.

Throwing 함수

throws로 표시된 함수는 try로 호출해야 합니다. 해당 함수 내에서 throw가 발생하면 제어가 호출한 do-catch로 전달됩니다. 호출 체인 어디에서도 에러가 처리되지 않으면 상위 레벨로 전파됩니다.

swift
enum FileError: Error {
    case notFound
    case permissionDenied
    case corrupted(String)
}

func readFile(path: String) throws -> String {
    guard FileManager.default.fileExists(atPath: path) else {
        throw FileError.notFound
    }
    return try String(contentsOfFile: path)
}

Rethrows

rethrows는 파라미터로 throwing 클로저를 받는 함수에 대한 수식자입니다. 전달된 클로저가 에러를 발생시키지 않으면 rethrows 함수를 try 없이 호출할 수 있습니다. 이를 통해 throwing과 non-throwing 클로저 모두에 호환되는 제네릭 고차원 함수를 작성할 수 있습니다.

Do-Catch 구문: 여러 catch 블록

Swift는 유형 기반 에러 매칭을 통한 여러 catch 블록을 지원합니다. 이는 Kotlin의 when과 유사하거나 유형변 switch와 비슷합니다: 처음으로 일치하는 catch가 실행되고 나머지는 스패팡됩니다.

유형별 매칭

각 catch 블록은 특정 에러 유형과 일치하는 패털을 포함할 수 있습니다. Swift는 블록을 순서대로 검사하므로 더 구체적인 유형이 일반적인 유형 보다 먼저 와야 합니다. 어떤 패털도 일치하지 않으면 패털이 없는 catch 블록이 실행됩니다.

여러 유형 처리

Do-Catch의 타입 안전성을 통해 각 에러를 별도로 처리하거나 쉬표 구분된 패턴 매칭을 통해 한 블록에서 여러 유형을 결합할 수 있습니다. 이를 통해 반환 코드나 도메인과 코드 검사가 있는 NSError를 통해 에러 분석이 이루어지던 Objective-C에 특징적인 복잡한 if-else 체인이 제거됩니다.

Swift는 추가 조건이 있는 매칭을 위한 catch where도 지원합니다. 예를 들어, 특정 코드나 텍스트로 에러를 필터링할 수 있습니다: catch URLError where error.code == .notConnectedToInternet. 이를 통해 정확성을 유지하면서 catch 블록수를 줄일 수 있습니다.

throwing 함수 디버깅을 위해 Swift는 에러와 예왘 구분을 제공합니다. Swift 에러는 스택 언와인딩이 필요하지 않은 반환 값입니다. Objective-C 예왘(@try/@catch)은 런타임 레벨에서 작동하고 치명적 실패에만 사용됩니다. Do-Catch는 예왘이 아닌 에러를 처리하므로 예측 가능하고 성능이 뛰어납니다.

라이브러리와 SDK를 개발할 때 throwing 함수가 공개 계약의 일부라는 것을 기억하는 것이 중요합니다. 에러 유형은 시그니처에 지정되지 않으므로 주석이나 Result 유사 enum을 통해 함수가 발생시킬 수 있는 에러를 문서화하세요. 이를 통해 API 사용자가 구현을 살펴보지 않고 올바른 catch 블록을 작성할 수 있습니다.

비동기 프로그래밍 문맥에서 async/await와 함께하는 Do-Catch는 콜백 지옥 문제를 해결합니다. 이전에 Swift에서는 비동기 코드의 에러 처리를 위해 completion handler에서 Error? 검사가 있는 중첩 클로저가 필요했습니다. async/await과 Do-Catch를 사용하면 비동기 throwing 코드가 동기 코드처럼 보이므로 각 단계에서 에러 처리가 있는 복잡한 요청 체인의 읽기와 유지보수가 간소화됩니다.

swift
do {
    let content = try readFile(path: "/data/config.json")
    process(content)
} catch FileError.notFound {
    createDefaultConfig()
} catch FileError.permissionDenied {
    requestAccess()
} catch FileError.corrupted(let detail) {
    logCorruption(detail)
} catch {
    print("알 수 없는 오류: \(error)")
}

Try 변형: try, try?, try!와 사용 시기

Swift는 throwing 함수를 호출하는 세 가지 방법을 제공합니다: try, try?, try!. 각 옵션은 특정 작업을 해결하고 제한사항이 있습니다.

try — 표준 호출

try는 do 블록 내에서 사용되고 에러는 catch에서 처리됩니다. 이것이 throwing 함수를 호출하는 주요 방법입니다. 컴파일러는 try가 에러를 잡을 수 있는 문맥에 있도록 요구합니다.

try? — Optional로 변환

try?는 throwing 함수의 결과를 Optional로 변환합니다. 함수가 에러를 발생시키면 try?는 nil을 반환하고, 그렇지 않으면 성공적인 값으로 Optional을 반환합니다. 에러를 무시할 수 있지만 성공 여부가 필요한 호출에 유용합니다.

try! — 강제 실행

try!는 에러 처리를 제억합니다. throwing 함수가 에러를 발생시키면 앱이 런타임 오류로 종료됩니다. try!는 에러가 확실히 불가능할 때만 사용하세요 — 예를 들어 번들된 리소스를 로드하는 경우.

swift
// try? — 에러를 무시하고 실패 시 nil 획득
if let data = try? Data(contentsOf: url) {
    processData(data)
}

// try! — 에러가 확실히 불가능한 경우에만
let bundled = try! String(contentsOfFile: "Assets/default.txt")

// try — do-catch에서 표준 옵션
do {
    let result = try performNetworkRequest()
    updateUI(result)
} catch {
    showError(error)
}

iOS 개발에서의 Do-Catch: 실제 예시

Do-Catch는 iOS 개발에서 시스템 API를 다루는 데 활발히 사용됩니다. 일반적인 시나리오를 살펴봅시다: 파일 시스템, Core Data 그리고 네트워크 요청.

파일 시스템

FileManager의 많은 메서드가 throwing입니다. Do-Catch를 통해 파일 미존재, 리소스 사용 중 또는 권한 부족을 적절히 처리할 수 있습니다.

Codable을 통한 네트워크 요청

JSONDecoder를 통한 JSON 파싱은 throwing 작업입니다. Do-Catch는 디코딩 오류와 네트워크 실패를 별도로 잡아 사용자에게 정확한 에러 메시지를 제공합니다.

swift
struct User: Codable {
    let id: Int
    let name: String
}

func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// 처리를 포함한 호출
do {
    let user = try await fetchUser(id: 42)
    showUser(user)
} catch let error as URLError {
    showNetworkAlert(error)
} catch let error as DecodingError {
    showParseError(error)
} catch {
    showGenericError(error)
}

Do-Catch 사용시 일반적인 실수

개발자들, 특히 다른 언어에서 전환하는 경우, Do-Catch를 사용할 때 특징적인 실수를 자주 범합니다. 가장 일반적인 것들을 살펴봅시다.

  • 잠혼 try — try 없이 throwing 함수를 호출하면 컴파일 오류가 발생합니다. Swift는 암시적으로 에러를 무시하는 것을 허용하지 않습니다.
  • 빈 catch — 처리가 없는 catch 블록은 에러를 숨합니다. 빈 것 같아도 헤당 에러를 항상 로그하거나 처리하세요.
  • 매칭 없는 catch — 모든 에러에 대한 하나의 일반 catch는 타입 안전성의 이점을 옆습니다. 여러 블록을 통해 유형별로 처리를 분리하세요.
  • try!의 과다 사용 — 프로덕션 코드에서 try!는 예기치 않은 크래시의 원인입니다. 번들된 리소스나 테스트에만 사용하세요.

자주 묻는 질문

Swift의 Do-Catch는 다른 언어의 try-catch와 어떻게 다른가요?

Swift에서 에러는 Error 프로토콜에 따르는 값으로, 스택 언와인딩이 있는 예왘가 아닙니다. Throwing 함수는 명시적으로 throws로 표시되어야 하고, 컴파일러는 호출 시 try를 필요로 합니다 — 이를 통햄 컴파일 시간에 미처리된 에러가 제거됩니다.

Do-Catch를 async/await와 함께 사용할 수 있나요?

네, async 함수도 throws가 가능하고, do-catch가 작동합니다. async throwing 함수를 호출하려면 do 블록 내에서 try await이 필요합니다. 이것이 비동기 Swift 코드에서 에러를 처리하는 표준 방법입니다.

do-catch 없이 에러를 처리하려면?

대체 방법이 있습니다: try?는 에러를 nil로 변환하고, try!은 에러 시 크래시를 일으키며, rethrows는 클로저에서 에러를 전파할 수 있습니다. Do-catch는 명시적 처리의 주요 방법으로 남아 있습니다.

하나의 함수가 다양한 유형의 에러를 발생시킬 수 있나요?

Swift는 발생된 에러를 타입화하지 않습니다 — 임의 throwing 함수가 Error에 따르는 임의 유형을 발생시킬 수 있습니다. 특정 에러 유형으로 catch에서 매칭하여 처리를 분리하세요.

모든 에러를 커스텀 유형으로 래핑해야 하나요?

네, 그것이 바랄직한 실천입니다. Error에 따르는 enum으로 AppError를 정의하고 시스템 에러를 도메인 에러로 변환하세요. 이를 통해 처리가 통일되고 애플리케이션 계층이 시스템 세부사항으로부터 분리됩니다.

요약

  • Do-Catch는 함수에 필수적으로 throws를 표시하고 호출에 try를 사용하는 Swift의 에러 처리 구문입니다.
  • Swift 에러는 Error 프로토콜에 따르는 값으로, 스택 언와인딩이 있는 예왘가 아니므로 실패가 없을 때 오버헤드가 없습니다.
  • 여러 catch 블록을 통해 switch와 유사한 패턴 매칭으로 다양한 에러 유형을 별도로 처리할 수 있습니다.
  • try?는 단순한 상황에서 에러를 nil로 변환하고, try!는 크래시 위험이 있는 강제 호출로 확실한 성공 작업에만 사용됩니다.
  • Do-Catch와 async/await는 try await을 사용하여 비동기 Swift 코드에서 에러를 처리하는 표준 방법입니다.
  • 빈 catch 블록과 try!의 과다 사용을 피하세요 — 이는 에러를 숨기고 프로덕션에서 예기치 않은 크래시를 초래합니다.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기