Smart Box

Kotlin "when"은 언제나 exhaustive 해야 한다. 본문

Programming

Kotlin "when"은 언제나 exhaustive 해야 한다.

프매씨 2022. 4. 11. 22:06

Kotlin에서의 when 문법은 기존 Java의 switch과 달리 개발자를 위해 정말 많이 진화해 있다. 그 중 가장 중요한 것은 컴파일 단계에서 내가 사용한 변수에 대한 모든 경우의 수를 핸들링 했는지 검사해주는 것이다. 이를 Exhaustive When Statement이라고 부르는데, 아래와 같다고 생각하면 된다.


enum class Currency
{
    WON,
    YEN,
    DOLLAR,
}


fun main()
{
    val currency = Currency.WON
    val sign = when(currency) //'when' expression must be exhaustive, add necessary 'YEN' branch or 'else' branch instead
    {
        Currency.WON -> "₩"
        Currency.DOLLAR -> "$"
    }
}

위 코드는 Currency라는 Enum Class의 경우들을 실제 사람들이 알아보는 기호로 변경하는 코드이다. when 문법이 모든 경우를 다루고 있지 않아서 Compile 단계에서 오류가 발생하고 있다. 이럴 경우, 3번째 경우인 Currency.YEN에 대한 경우를 핸들링하거나 else를 통해 나머지 경우를 모두 핸들링 하는 경우가 있다. 여기서 당신은 어떻게 처리할 것인가?

else로 처리하는 것은 되도록 피하자.

else로 나머지 경우의 수를 처리하는 것도 나쁘지 않다. 그래야만 하는 경우일 때 뿐이다. 만약, else로 나머지 case들을 처리하고, Currency에 새로운 경우가 추가된다면? 컴파일 단계에서 오류가 발생하지 않으므로, 개발자는 해당 로직 부분을 놓칠 수 밖에 없다. 하지만, 직접 경우의 수를 모두 처리했다면, 새로운 경우가 추가 됬을 때에 미처 수정하지 못한 부분들이 있다면 컴파일 오류가 발생하여 미리 방지할 수 있을 것이다.

1.7 이전에서는 완벽한 Exhaustive가 되지 않는다.

koltin 1.7 이전 버전 경우 아직은 컴파일이 모든 경우에 대해 Exhaustive인지 검사를 진행하지 않는다. 아래 경우를 보자.

fun main()
{
    val currency = Currency.WON
    when(currency) //Non exhaustive 'when' statements on enum will be prohibited in 1.7, add 'YEN' branch or 'else' branch instead
    {
        Currency.WON -> {
            //로직 1
        }
        Currency.DOLLAR -> {
            //로직 2
        }
    }
}

이렇게 when statement에서 발생된 값을 실제로 사용하지 않는 경우에 대해서는 검사 않고, 경고 문구를 발생시킨다. 이러한 경우, 새로운 Currency enum에 새로운 경우가 추가됬다면, 컴파일은 경고만 할 뿐 문제없이 진행되며, 큰 프로젝트를 수정하고 있다면, 개발자들은 놓칠 수 밖에 없다.

Non-Exhaustive When StatementExhaustive When Statement

koltin의 강력한 extension 기능 덕분에 이 경우 또한 핸들링 할 수 있다. extension을 통해 반드시 발생된 결과를 이용하면 되는 것이다. 아래 예제를 확인해보자.

//https://github.com/pemassi/pemassi-kotlin-extensions
fun Any?.exhaustive() = Unit

fun main()
{
    val currency = Currency.WON
    when(currency) //'when' expression must be exhaustive, add necessary 'YEN' branch or 'else' branch instead
    {
        Currency.WON -> {
            //로직 1
        }
        Currency.DOLLAR -> {
            //로직 2
        }
    }.exhaustive()
}

모든 객체에 대해 exhaustive()라는 extension method를 추가하여 when에서 발생된 값으로부터 아무것도 하지 않는 메서드를 호출하는 코드이다. 이런 경우 기존과 동일하게 컴파일 오류가 발생되며, 문제를 해결할 수 있다. 실제로 운영하는 팀에서 이러한 코드 컨밴션을 가지고 있다면, 여러 문제를 미리 예방할 수 있을 것이다.

'Programming' 카테고리의 다른 글

Rancher(k8s)를 도입하면서 느낀 점  (0) 2022.02.19
Comments