hong's android

[android] 오류 메시지 어떻게 보여줘야할까? 본문

Android

[android] 오류 메시지 어떻게 보여줘야할까?

_hong 2024. 11. 3. 22:59

만약 앱이 아무런 메시지도 없이 죽어버리거나, 흰 화면이 나타난다면 사용자 경험이 낮아지고, 앱 이탈률도 높아질 것이라고 생각합니다. 어떻게 사용자에게 오류 메시지를 명확히 전달하고, 재시도를 유도할 수 있을까요? 그리고 개발자는 오류를 빠르게 디버깅, 수정해서 어떻게 안정적인 서비스를 운영할 수 있을지 생각하며 개선했던 경험을 작성해보려고 합니다.

 

먼저, 앱에서 발생할 수 있는 오류는 크게 세 가지로 수 있었습니다.

 

1. 시스템 논리 오류

 이 오류는 프로그램의 내부 로직에서 발생하는 오류로, 예기치 않은 흐름에 의해 RunTimeException과 같은 예외가 발생할 때 주로 나타납니다. 이러한 오류는 사용자가 일반적으로 트리거하지 않지만, 발생 시 앱이 예상대로 동작하지 않아 기능이 중단될 수 있습니다. 또한 RunTimeException 이외에 로직상 발생하는 오류입니다.

2. 네트워크 오류

API 요청 중 발생할 수 있는 404, 500 같은 서버 오류는 자주 발생하는 오류 유형 중 하나입니다. 네트워크 오류는 사용자나 클라이언트가 아닌 서버 측의 문제입니다.

3. 사용자의 환경에 의한 오류

네트워크 연결 불량 등 사용자의 환경적 요인으로 인한 오류입니다.

 

이외에도 사용자 입력에 의한 오류, 요청 시간 초과 오류, 앱 업데이트로 인한 오류 등 다양한 상황이 발생할 수 있지만, 이 세 가지 오류 유형을 중심으로 다루고자 합니다.

 

1. 예상치 못한 논리로 발생된 오류 즉, 논리오류는 exception을 던 지고 외부 크래시 리포트 도구 (firebase crashlytics, sentry, …)를 통해 디버깅합니다.

RunTimeException은 오류를 던지고 해당 오류를 리포트 도구를 사용해 로그를 작성할 수 있습니다. 하지만 논리상 예상치 못한 논리 오류, 즉 아래 코드와 같이 음수가 나오면 안 되는 상황입니다.

fun updateOrderQuanity(orderId: OrderId, quantity: Int) {
    require(quantity > 0) { "Quantity must be positive" }
    // proceed with update
}

 

논리 오류가 발생했을 땐 앱의 기능을 계속 사용해서 또 다른 사이드 이펙트가 발생하는 것을 막는 것이 앱의 안정성을 올릴 수 있을 수 있을 거라 생각했습니다. 또한 사용자가 경험했던 시스템 논리 오류를 디버깅하기 위해 해당 상황을 똑같이 재연해서 디버깅을 하면 버그를 수정하는 시간이 길어질 수 있었습니다.

 

그래서 논리 오류가 발생했을 경우 예외를 던지고, 파이어베이스 크래시틱스와 같은 외부 로그 툴을 사용합니다.

 

(사용자에겐 시스템 오류가 발생한 상황을 전달하기 위해 “현재 시스템 오류가 발생했습니다.”라는 메시지를 표시했습니다. 그리고 사용자의 앱 사용을 제한하고, “재 설치” 또는 “앱 종료 후 시도”하는 것을 유도합니다.)

 

2. 네트워크 오류 처리 (404, 500, …) 

오류 코드와 이해할 수 없는 메시지를 그대로 보여주는 것보다 사용자가 이해할 수 있는 언어로 오류 메시지를 보여주고, 재시도할 수 있는 방법 또는 기다려 달라는 메시지를 전달하는 것이 사용자를 고려한 좋은 에러 메시지라고 생각합니다.

 

그래서 서버 오류는 “서비스를 일시적으로 이용 불가능합니다.”라는 메시지와 함께 잠시 후 다시 시도해달라고 사용자에게 요청합니다. 하지만 피쳐마다 다르게 오류 메시지를 보여줘야 할 수도 있기 때문에 피쳐별 오류 코드에 따라서 해당 오류 메시지를 각각 보여줍니다. 피쳐별로 오류 메시지들을 처리한다면 더욱 명확한 메시지를 사용자에게 보여줄 수 있다고 생각했습니다.

 

네트워크 오류는 그 종류가 다양하기 때문에 커스텀 exeception을 사용했습니다.

sealed class와 custom exception을 활용

네트워크 예외를 호출자 입장에서 어떤 예외인지 확인할 수 있어야 했습니다. 안드로이드에선 viewmodel -> view 이러한 순으로 뷰모델의 오류를 확인하고 뷰에선 그에 따른 오류메시지를 보여주는 것이 필요했습니다.

 

try - catch를 사용할 경우 catch 블록 내부에서의 리턴 타입과 try 블록 내의 리턴 타입과 일치시켜야 합니다. 이 때문에 만약 네트워크를 통해 받아온 데이터가 비었을 경우 호출자 입장에선 exception이 발생한 것인지 모호해집니다.  이 때문에 sealed class를 통해 발생 가능한 excpetion을 작성한 후 exception을 전달합니다.

Fun fetchData(): String {
	return try {
		apiService.getData()
	} catch (e: IOException) {
		“”
	}
}

 

 

3. 네트워크 미연결 처리

네트워크 연결이 끊겼을 때, 사용자가 네트워크 환경을 재확인하고 재시도할 수 있도록 네트워크 오류 처리 플로우를 구현했습니다.

 

순서 1) 네트워크 끊김   

API 호출 중 네트워크가 끊긴 경우 IOException을 통해 예외가 발생하며, 이를 try-catch 구문으로 감싸 네트워크 예외를 잡아냅니다. 예외 발생 시 네트워크 문제로 인해 요청이 실패했음을 알 수 있습니다.

순서 2) 네트워크 오류 화면 

네트워크가 미연결 상태임을 감지하면 사용자를 "네트워크 연결이 원활하지 않습니다."라는 메시지를 포함한 네트워크 오류 화면으로 이동시킵니다. 이 화면에서는 연결 문제 해결 방법을 안내하고 재시도 버튼을 제공합니다.

순서 3) 사용자의 재시도 

재시도 시, 해당 화면의 API 요청을 재시도합니다. 아래는 쿠팡의 재시도 화면입니다.

쿠팡의 네트워크 미연결 화면

 

 

Reference.

https://toss.tech/article/how-to-write-error-message

https://blog.stackademic.com/effective-error-handling-in-android-strategies-and-best-practices-c9eb6fb4b864

https://elizarov.medium.com/kotlin-and-exceptions-8062f589d07

'Android' 카테고리의 다른 글

[안드로이드] viewModelScope에 대해서 알아보자  (0) 2024.11.14