[iOS] Error 예외처리: 사용자와 개발자 모두를 위한

Madeline👩🏻‍💻·2024년 4월 2일
2

iOS study

목록 보기
44/61

안녕하세요, 매들린입니다!
제가 지난 토요일 'let us go' iOS 컨퍼런스에 다녀왔는데요,
특히 감명받았던 곰튀김님의 스피치를 듣고, 제가 이해하고 적용한 내용을 공유해보고자 합니다!
(곰튀김님의 스피치를 듣고 쓰는 글이지만, 제 피셜로 작성해서, 틀린 내용은 언제든지 피드백 부탁드립니다🙏🏻)

🐻

우선 Swift에서의 Exception, 그러니까 예외는 Error이다.
이 에러를 어떻게 처리하느냐가 예외를 처리하는 것과 일맥상통한다고 볼 수 있겠다.
특히, 예외라는 건.. 예측할 수 없는 것들이기에, 개발 과정에서 예외 처리를 다양한 경우에 대해 해두는 것이 좋다. 그리고 에러를 어떻게 관리하느냐는 우리가 만드는 서비스의 질을 결정할 수 있다.

하여튼 Swift에서 예외처리를 사용자 친화적으로 다루는 방법에 대해 이야기해보고자 한다.
특히, 네트워크 연결 문제와 같이, 사용자가 직접 해결할 수 없는 문제에 대해 어떻게 더 나은 경험을 제공할 수 있을지에 대해 말해보자.

Swift의 기본 에러처리: do-try-catch

이는 에러를 발생시킬 수 있는 코드를 'try'로 실행하고,
'catch' 블록에서 에러를 잡아 처리하는 구조이다.
이렇게 코드의 흐름을 명확하게 유지하면서, 예외 처리를 도와준다.

func fetchDataFromServer() throws {
    // 여기서 에러가 발생할 수 있는 코드
}

do {
    try fetchDataFromServer()
} catch {
    print("데이터를 가져오는 중 오류가 발생했습니다.")
}

-> 서버에서 데이터를 가져오는 함수가 에러를 던질 경우, 그 에러를 잡아내고 콘솔에 메시지를 출력한다.

그리고 네트워크 연결 오류를 처리할 때, 보통 사용하는 방법을 코드로 나타내보겠다.

enum NetworkError: Error {
    case disconnected
    case timeout
}

func fetchData() throws {
    // 네트워크 연결 상태를 체크하는 가상의 코드
    let isConnected = false
    if !isConnected {
        throw NetworkError.disconnected
    }
}

do {
    try fetchData()
} catch NetworkError.disconnected {
    print("네트워크 연결이 끊겼습니다.")
} catch {
    print("알 수 없는 오류가 발생했습니다.")
}

Custom Error로 UX 개선하기

앱에서 네트워크 오류가 발생했다고 생각해보자. 수많은 경우의 네트워크 오류는 아무리 자세히, 쉽게 설명해도 유저가 굳이 알 필요가 없다.
내가 한게 올바른 주소인지, 접근 권한이 없는지, 알 수 없는 오류가 발생했는지, .. 이런 구체적인 상황보다는 유저 입장에서 "그래서 내가 뭐 어떻게 해야하는데?"를 알려주는 것이 더 중요하다.
네트워크 에러가 나서, 그래서 유저는 어떤 조치를 취해야 하는지, 그 다음 액션을 잘 안내해주는 것이 좋겠다.

(Custom) UserFriendlyError Protocol

우리는 UserFriendlyError라는 커스텀 에러 프로토콜을 만들어 사용할 수 있다. 이 프로토콜은 Error, CustomStringConvertible, LocalizedError를 준수하며, 사용자에게 보여줄 메시지를 더 친근하게 제공할 수 있도록 한다.

예를 들어, 네트워크 연결이 끊겼을 때 사용자에게 다음과 같은 메시지를 제공할 수 있다: "인터넷 연결이 원활하지 않습니다. 연결 상태를 확인하고 다시 시도해주세요."

LocalizedError Protocol

이 프로토콜을 채택하면, 에러에 대한 설명(errorDescription)을 제공할 수 있다. 이를 통해 개발자는 에러를 더 세분화하여, 발생 가능한 다양한 시나리오를 고려한 메시지를 정의할 수 있다. 또한, do-catch 구문 내에서 여러 catch 블록을 사용하여 다양한 에러 유형에 대해 다른 처리를 할 수 있다.

LocalizedError는 사용자에게 직접 보여주기 위한 것이 아니라, 개발자가 에러를 더 잘 이해하고 처리할 수 있도록 돕는 것이다. 하지만, 이를 통해 사용자 친화적인 메시지로 변환하는 것이 중요하다.

enum NetworkError: Error, LocalizedError {
    case disconnected
    case timeout
    
    var errorDescription: String? {
        switch self {
        case .disconnected:
            return "인터넷 연결이 끊겼습니다. 연결 상태를 확인해주세요."
        case .timeout:
            return "요청 시간이 초과되었습니다. 나중에 다시 시도해주세요."
        }
    }
}

func fetchData() throws {
    let isConnected = false
    if !isConnected {
        throw NetworkError.disconnected
    }
}

do {
    try fetchData()
} catch let error as LocalizedError {
    print(error.errorDescription ?? "알 수 없는 오류가 발생했습니다.")
}

CustomStringConvertible

CustomStringConvertible 프로토콜은 타입에 대한 사용자 정의 설명을 제공할 수 있게 해주는 스위프트의 기본 프로토콜이다. 이를 구현함으로써 인스턴스를 문자열로 변환할 때 원하는 형태로 출력을 정의할 수 있습니다. 이는 로깅이나 디버깅 때 매우 유용하게 사용된다.

아래는 NetworkError 열거형에 CustomStringConvertible을 적용한 예시이다. 이를 통해 에러 메시지를 보다 상세하게 제공하면서, 개발자가 문제를 더 쉽게 이해하고 디버깅할 수 있도록 돕는다.

enum NetworkError: Error, LocalizedError, CustomStringConvertible {
    case disconnected
    case timeout
    
    // LocalizedError 프로토콜 구현
    var errorDescription: String? {
        switch self {
        case .disconnected:
            return "인터넷 연결이 끊겼습니다. 연결 상태를 확인해주세요."
        case .timeout:
            return "요청 시간이 초과되었습니다. 나중에 다시 시도해주세요."
        }
    }
    
    // CustomStringConvertible 프로토콜 구현
    var description: String {
        switch self {
        case .disconnected:
            return "NetworkError: 연결 끊김"
        case .timeout:
            return "NetworkError: 시간 초과"
        }
    }
}

func fetchData() throws {
    let isConnected = false
    if !isConnected {
        throw NetworkError.disconnected
    }
}

do {
    try fetchData()
} catch let error as LocalizedError {
    print(error.errorDescription ?? "알 수 없는 오류가 발생했습니다.")
    print(error) // CustomStringConvertible을 통한 출력
}

이 코드에서 NetworkError는 LocalizedError와 CustomStringConvertible 두 프로토콜을 준수한다. errorDescription은 사용자에게 보여줄 오류 메시지에 사용되고,
description은 개발자가 로깅이나 디버깅을 할 때 보게 될 오류이다.

CustomStringConvertible의 description 프로퍼티를 통해, 에러를 문자열로 출력할 때 "NetworkError: 연결 끊김" 또는 "NetworkError: 시간 초과"와 같이 더 구체적인 정보를 제공할 수 있다.
이는 개발 과정에서 문제를 더 빠르게 진단하고 해결하는 데 도움을 줍니다.

스위프트에서의 예외 처리는 단순히 에러를 잡아내고 로깅하는 것 이상이다. 사용자에게 더 나은 경험을 제공하고, 개발자가 에러를 더 효율적으로 관리할 수 있도록 해보자!

네트워크 문제에 대한 예외 처리를 할 때도 이러한 베스트 프랙티스를 적용할 수 있다. 아래는 네트워크 문제를 예로 든 좋은 예외 처리 습관이다:

베스트 프랙티스: 네트워크 문제에 대한 좋은 예외 처리 습관

구체적으로 처리하기

  • 일반적인 오류 메시지: "네트워크에 연결할 수 없습니다."
  • 구체적인 오류 메시지: "와이파이 신호가 약해 연결할 수 없습니다. 더 강한 신호의 와이파이에 연결해주세요."

이렇게 구체적으로 문제를 설명해주면 사용자가 왜 네트워크 문제가 발생했는지 이해하고, 어떻게 해결해야 할지 알 수 있다.

사용자 친화적으로 변환하기

  • 기술적인 오류 메시지: "서버 응답 없음 (Timeout Error)."
  • 사용자 친화적인 오류 메시지: "서버가 응답하지 않고 있어요. 잠시 후 다시 시도해주세요."

사용자가 기술 용어를 몰라도 이해할 수 있도록, 보다 친절하고 일상적인 언어로 오류 메시지를 제공한다.

로그 남기기

  • 문제 발생 시: 사용자에게는 친절한 메시지를 보여주면서, 동시에 개발자를 위한 로그에는 "2024-04-03 10:00: 와이파이 연결 실패, 신호 강도 약함", "2024-04-03 10:05: 서버 응답 시간 초과"와 같이 구체적인 기술적 정보를 남겨둔다.

이렇게 로그를 남기면, 나중에 문제가 발생했을 때 원인을 더 빠르게 찾아내고 해결할 수 있다.

이런 습관을 네트워크 문제 처리에 적용하면, 사용자는 현재 상황을 더 잘 이해하고, 필요한 조치를 취할 수 있게 됩니다. 동시에 개발자는 문제 해결에 필요한 구체적인 정보를 로그에서 쉽게 찾아낼 수 있다. 이 모든 것이 앱의 안정성을 높이고, 사용자 경험을 개선하는 데 큰 도움이 된다.

profile
🍎 Apple Developer Academy@POSTECH 2기, 🍀 SeSAC iOS 4기

2개의 댓글

comment-user-thumbnail
2024년 4월 4일

잘봤습니당

1개의 답글