Kotlin Preconditions

cotton·2025년 2월 19일

Preconditions

Preconditions : 사전 조건. 특정한 일이 발생하기 전에, 충족되어야 하는 조건

Kotlin에서는 Preconditions은 특정 조건이 충족되지 않은 경우 예외를 발생하는 함수들을 담아 둔 stdlib 함수들입니다.

대표적으로 require, check, error가 있습니다.

require

@kotlin.internal.InlineOnly
public inline fun require(value: Boolean, lazyMessage: () -> Any): Unit {
    contract {
        returns() implies value
    }
    if (!value) {
        val message = lazyMessage()
        throw IllegalArgumentException(message.toString())
    }
}

주어진 조건 (value) 이 false인 경우, lazyMessage로 설정한 값과 함꼐 IllegalArgumentException 을 발생시킵니다.

주로 주어진 값을 검증하는 역할로 사용합니다.

requireNotNull

@kotlin.internal.InlineOnly
public inline fun <T : Any> requireNotNull(value: T?, lazyMessage: () -> Any): T {
    contract {
        returns() implies (value != null)
    }

    if (value == null) {
        val message = lazyMessage()
        throw IllegalArgumentException(message.toString())
    } else {
        return value
    }
}

require과는 다르게 value로 받는 값이 null인 경우 exception을 발생시킵니다.

주로 주어진 값이 유효한지 확인하는 데 사용합니다.

check, checkNotNull

@kotlin.internal.InlineOnly
public inline fun check(value: Boolean, lazyMessage: () -> Any): Unit {
    contract {
        returns() implies value
    }
    if (!value) {
        val message = lazyMessage()
        throw IllegalStateException(message.toString())
    }
}

@kotlin.internal.InlineOnly
public inline fun <T : Any> checkNotNull(value: T?, lazyMessage: () -> Any): T {
    contract {
        returns() implies (value != null)
    }

    if (value == null) {
        val message = lazyMessage()
        throw IllegalStateException(message.toString())
    } else {
        return value
    }
}

checkrequire, checkNotNullrequireNotNull과 동일하게 동작하지만 require, requireNotNull 와는 다르게 IllegalStateException 를 발생시킵니다.

error

@kotlin.internal.InlineOnly
public inline fun error(message: Any): Nothing = throw IllegalStateException(message.toString())

해당 함수는 간편하게 에러를 발생시킬 수 있는 함수입니다.

어떠한 경우에던 IllegalStateException 을 발생시키는 로직을 가지고 있습니다.

check vs require, checkNotNull vs requireNotNull

코드 자체는 같은 동작을 하는데, 왜 똑같은 방식의 함수를 여러 개를 만들었을까요? 이는 반환하는 Exception에서 힌트를 얻을 수 있습니다.

require의 경우에는 함수의 입력값, 즉 매개변수(Argument)를 검증하는데 주로 사용하며, check의 경우에는 코드 로직 내부의 상태(State)를 검증하는 것이 주 목적입니다.

예시

이런 Preconditions 함수들은 일반적인 비즈니스 로직 뿐만 아니라 테스트 코드에서도 사용할 수 있으며, Compose에서도 조건에 맞지 않는 경우 뷰를 그리지 않는 방식으로 사용할 수 있습니다.

@Composable
fun UserInfoView(
    name: String?
) {

	val stateFlow = viewModel.stateFlow.collectAsState().value

    check(stateFlow?.historyList?.isNullOrEmpty()) { "history list is empty" }
    requireNotNull(name) { "Name is not valid" }
}

사용 시 주의

해당 함수들은 조건에 올바르지 않으면 Exception을 반환합니다. Exception을 잘 처리하지 않는다면, Android 앱 위에서는 앱이 충돌할 수 있습니다.

이런 Preconditions 함수들을 사용하는 경우 try-catch를 이용하여 예외를 처리하거나 runCatching을 이용하여 처리해야 합니다.

profile
안드로이드 개발자

0개의 댓글