이펙티브 코틀린 item 7: 결과 부족이 발생할 경우 null과 Failure를 사용하라

woga·2023년 3월 18일
0

코틀린 공부

목록 보기
10/54
post-thumbnail

함수가 원하는 결과를 만들어 낼 수 없을 경우가 있다.

  • 서버로부터 데이터를 읽어 들이려고 했는데, 인터넷 연결 문제로 읽어 들이지 못한 경우
  • 조건에 맞는 첫 번째 요소를 찾으려 했는데, 조건에 맞는 요소가 없는 경우
  • 텍스트를 파싱해서 객체를 만들려고 했는데, 텍스트의 형식이 맞지 않는 경우

이럴 때는 두 가지로 처리된다.

  • null 또는 실패를 나타내는 sealed class(Failure 네이밍 된) 를 리턴한다.

  • 예외를 throw 한다.

그러나 예외는 정보를 전달하는 방법으로 사용되선 안 되고 예외적인 상황이 발생했을 때 사용하는 것이 좋다.
왜냐하면, 많은 개발자가 예외가 전파되는 과정을 제대로 추적하지 못하고 코틀린의 모든 예외는 unchecked 예외라 개발자가 이 예외를 처리하지 않을 수도 있기 때문이다.
또한, 예외는 예외적인 상황을 처리하기 만들어졌기 때문에 명시적인 테스트(explicit test)만큼 빠르게 동작하지 않는다.
그리고 try-catch 블록 내부에 코드를 배치하면 컴파일러가 할 수 있는 최적화가 제한된다.

그래서 null과 sealed class 상태 네이밍을 한 후 이용하는게 좋은데 관련해서 아래와 같은 예외를 참고하자

inline fun <reified T> String.readObjectOrNull(): T? {
    // ...
    if (incorrectSign) {
        return null
    }
    // ...
    return result
}

inline fun <reified T> String.readObject(): Result<T> {
    // ...
    if (incorrectSign) {
        return Failure(JsonParsingException())
    }
    // ...
    return Success(result)
}

sealed class Result<out T>
class Success<out T>(val result: T): Result<T>()
class Failure(val throwable: Throwable): Result<Nothing>()

class JsonParsingException: Exception()

이렇게 명시적이고 효율적이며 간단하게 처리할 수 있다.

null을 처리해야 한다면, safe call이나 Elvis 연산자 같은 다양한 널 안정성 기능을 활용하자

val age = userText.readObjectOrNull<Person>()?.age ?: -1

또 위에서 뱉은 Result 값을 아래처럼 표현할 수 있다.

val age = when(person) {
	is Success -> person.age
    is Failure -> -1
 }

이런 오류 처리 방식은 tyr-catch 보다 효율적이고 사용하기 쉽고 명확하다

보통 Failure 같인 sealed class를 이용할 때가 언제일까? 추가적인 정보가 있을 때 사용하면 좋다. 이 친구는 처리할 때 필요한 정보를 가질 수 있기 때문이다.

그 외는 일반적으로 null로 처리한다.

특히 List 컬렉션을 예를 들자

List는 여러 함수를 제공하는데 그 중에 List 속 인덱스의 객체를 갖고 오고 싶다면, 아래와 같이 쓸 수 있다

  • get()

  • getOrNull()

  • getOrDefault()

그러나 get()을 썼다가 특정 위치에 요소가 없다면 IndexOutOfBoundsException을 뱉는다, 그 외 함수는 null을 뱉거나 다르게 풀 수가 있다.

일반적으로 getOrNull() / 엘비스 연산자를 사용하는게 쉽다

마무리

개발자는 언제가 reader임을 명시하자. 예외를 쓰는 것도 좋지만 보통 결과에 대한 실패를 처리할 때 잘 쓰는 것도 개발자의 덕목인 듯 하다. 개발자에게 null이 발생할 수 있단 경고를 주는 getOrNull 같은 함수를 잘 써서 어떤 게 리턴되는지 예측할 수 있게 만들어보자.

우리 서비스는 그런면에서 sealed class를 애용하고 있다. 앞으로도 더 잘 활용해봐야겠다!

profile
와니와니와니와니 당근당근

0개의 댓글