ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift 6 : Typed Throws ( 에러도 타입을 줘서 더 명확히 처리하자.)
    Apple🍎/Swift 2025. 4. 14. 16:52

    Swift 6의 Typed Throws: 오류 처리의 새로운 패러다임

    Swift 6에서 도입된 "typed throws"는 Swift의 오류 처리 시스템에 중요한 발전을 가져왔습니다. 이 기능을 통해 함수가 발생시킬 수 있는 구체적인 오류 유형을 명시적으로 선언할 수 있게 되었습니다. 

    Swift의 기존 오류 처리 방식

    Swift 5까지는 함수가 오류를 던질 수 있다는 것만 표시할 수 있었고, 어떤 종류의 오류를 던질 수 있는지는 명시할 수 없었습니다:

    enum DatabaseError: Error {
        case connectionFailed
        case queryFailed
    }
    
    enum NetworkError: Error {
        case timeout
        case serverDown
    }
    
    // 이 함수는 어떤 오류든 던질 수 있음을 나타내지만, 구체적으로 어떤 오류인지는 명시하지 않습니다
    func fetchUserData() throws -> UserData {
        // 구현...
    }
    

    이 접근 방식의 한계는 호출하는 쪽에서 이 함수가 어떤 종류의 오류를 발생시킬 수 있는지 알 수 없다는 것입니다. 문서나 코드를 살펴봐야만 알 수 있었습니다.

    Swift 6의 Typed Throws 소개

    Swift 6에서는 함수가 던질 수 있는 오류 유형을 명시적으로 선언할 수 있게 되었습니다.

    // 이 함수는 DatabaseError만 던질 수 있음을 명시합니다
    func fetchUserData() throws(DatabaseError) -> UserData {
        // 구현...
    }
    
    // 이 함수는 DatabaseError와 NetworkError를 던질 수 있음을 명시합니다
    func syncData() throws(DatabaseError, NetworkError) -> Bool {
        // 구현...
    }
    

    Typed Throws의 장점

    1. 명확한 계약: 함수 시그니처를 통해 함수가 던질 수 있는 오류 유형이 명확하게 드러납니다.
    2. 컴파일 타임 검사: 컴파일러가 함수가 선언되지 않은 오류 유형을 던지는 것을 방지할 수 있습니다.
    3. 더 정확한 오류 처리: 호출하는 쪽에서 처리해야 할 오류 유형을 정확히 알 수 있습니다.
    4. 코드 문서화: 코드 자체가 어떤 오류가 발생할 수 있는지 문서화합니다.

    실제 사용 예시

    실제 코드에서 typed throws를 어떻게 사용하고 처리하는지 살펴보겠습니다.

    enum DatabaseError: Error {
        case connectionFailed
        case queryFailed
        case insufficientPermissions
    }
    
    enum ValidationError: Error {
        case invalidInput
        case missingField(String)
    }
    
    // 함수 선언 - 특정 오류 유형만 던짐
    func saveUser(_ user: User) throws(DatabaseError, ValidationError) -> Bool {
        guard user.isValid() else {
            throw ValidationError.invalidInput
        }
        
        guard user.name.count > 0 else {
            throw ValidationError.missingField("name")
        }
        
        if !database.isConnected {
            throw DatabaseError.connectionFailed
        }
        
        // 데이터 저장 로직...
        return true
    }
    
    // 호출 측에서의 오류 처리
    func createNewUser() {
        do {
            let success = try saveUser(newUser)
            print("User saved successfully")
        } catch let error as DatabaseError {
            switch error {
            case .connectionFailed:
                print("Could not connect to database")
            case .queryFailed:
                print("Database query failed")
            case .insufficientPermissions:
                print("Not enough permissions to save user")
            }
        } catch let error as ValidationError {
            switch error {
            case .invalidInput:
                print("User data is invalid")
            case .missingField(let fieldName):
                print("Required field missing: \(fieldName)")
            }
        } catch {
            // 이 부분은 실행되지 않을 것입니다. 왜냐하면 saveUser 함수는
            // DatabaseError와 ValidationError만 던지기 때문입니다.
            print("Unknown error: \(error)")
        }
    }
    

    기존 throws와의 호환성

    Swift 6는 기존 코드와의 호환성을 유지하기 위해 일반적인 throws도 계속 지원합니다. 이는 모든 가능한 오류를 던질 수 있음을 의미합니다:

    // 여전히 유효한 코드입니다 - 어떤 Error든 던질 수 있음
    func legacyFunction() throws -> String {
        // 구현...
    }
    

    함수 합성과 오류 전파

    Typed throws는 함수 합성과 오류 전파에서도 이점을 제공합니다.

    func processUserData() throws(DatabaseError, ValidationError, NetworkError) -> ProcessedData {
        // saveUser는 DatabaseError와 ValidationError만 던질 수 있습니다
        let saved = try saveUser(user)
        
        // fetchAdditionalData는 NetworkError만 던질 수 있습니다
        let additionalData = try fetchAdditionalData(user.id)
        
        // 이 함수는 세 가지 오류 유형을 모두 전파할 수 있습니다
        return ProcessedData(saved: saved, additional: additionalData)
    }
    

    서브타이핑과 다형성

    Typed throws는 서브타이핑 규칙을 따릅니다. 부모 클래스에서 특정 오류를 던지는 메서드를 선언하면, 자식 클래스는 같은 오류나 그 하위 집합만 던질 수 있습니다.

    class DataService {
        func fetchData() throws(NetworkError, DatabaseError) -> Data {
            // 구현...
        }
    }
    
    class CachedDataService: DataService {
        // 유효함: 부모보다 적은 오류 유형을 던짐
        override func fetchData() throws(NetworkError) -> Data {
            // 구현...
        }
    }
    
    class InvalidService: DataService {
        // 컴파일 오류: 부모가 선언하지 않은 추가 오류 유형을 던질 수 없음
        override func fetchData() throws(NetworkError, DatabaseError, ValidationError) -> Data {
            // 구현...
        }
    }
    

    제네릭과의 상호 작용

    Typed throws는 제네릭 프로그래밍과도 잘 통합됩니다.

    // E는 Error 프로토콜을 준수하는 어떤 타입이든 될 수 있습니다
    func processGeneric<T, E: Error>(value: T) throws(E) -> T {
        // 구현...
    }
    
    // 여러 제네릭 오류 타입을 사용할 수도 있습니다
    func complexOperation<E1: Error, E2: Error>(data: Data) throws(E1, E2) -> Result {
        // 구현...
    }
    

     

    정리

    Swift 6의 typed throws는 코드의 안전성과 명확성을 크게 향상시키는 기능입니다. 이 기능을 통해 프로그래머는 함수가 발생시킬 수 있는 오류 유형을 명시적으로 문서화할 수 있으며, 컴파일러는 이를 기반으로 더 엄격한 검사를 수행할 수 있습니다. 이는 더 견고하고 유지보수가 용이한 코드를 작성하는 데 도움이 되며, 런타임에 발견될 오류를 컴파일 타임에 잡아낼 수 있게 해줍니다.

     

    댓글

Designed by Tistory.