Kotlin/Class and Interface

[Kotlin] enum class란 무엇인가?

Dev.Cho 2021. 5. 1. 15:57

목표

  • enum class가 무엇인지 이해한다.
  • enum class가 사용되어야 하는 곳을 이해한다.

enum class를 왜 사용해야 하는가?

변수의 상태 별로 다른 결과값을 출력하는 코드를 짜야한다면 어떻게 짜야할까? 직관적으로 떠오르는 방법은 변수의 상태를 상수로 정의한다음 상태 별로 동작을 정의하는 것이다.

 

예를 들어 Task라는 작업 단위가 있고 해당 작업 단위의 상태값이 state를 출력해주는 클래스를 설계한다고 해보자. 그러면 해당 클래스는 아래와 같이 만들어질 수 있다.

class Task {
    var state: Int = WAITING

    fun printState() =
        when (state) {
            WAITING -> println("Waiting..")
            PROCESSING -> println("Processing..")
            else -> println("Process not configured")
        }


    companion object {
        const val WAITING: Int = 0
        const val PROCESSING: Int = 1
    }
}

fun main() {
    val task = Task()
    task.printState() // Waiting..
}

하지만 이러한 방식으로 코드를 짜게 되면 State의 종류가 늘어났을 때 처리 로직을 빼먹거나, 처리되지 말아야 하는 상수값(Int)가 들어갔을 때 처리가 될 수 있다. 또한 상수값이 Int값이므로 state에 Int 값을 직접 넣어 코딩을 하는 가능성을 만들 있다.

 

예를 들어 DONE이라는 새로운 state를 추가한 아래와 같은 상황이 생길 수 있다. 

class Task {
    var state: Int = DONE

    fun printState() =
        when (state) {
            WAITING -> println("Waiting..")
            PROCESSING -> println("Processing..")
            else -> println("Process not configured")
        }


    companion object {
        const val WAITING: Int = 0
        const val PROCESSING: Int = 1
        const val DONE: Int = 2
    }
}

fun main() {
    val task = Task()
    task.printState() // Process not configured
}

DONE이라는 state를 추가했는데 done에 대한 변수값을 처리하는 동작이 없다보니 Process not configured라는 문자가 뜨는 것이다.

 

이러한 상황 방지를 위해 enum class를 사용할 수 있다. enum 클래스는 상수 값의 타입 안전성을 보장해주며, when에 상수에 정의한 값에 대한 처리를 꼭 해주도록 만든다.

class Task {
    var state: State = State.WAITING

    fun printState() =
        when(state){
            State.WAITING -> println("Waiting..")
            State.PROCESSING -> println("Processing..")
        }

    enum class State {
        WAITING, PROCESSING
    }
}

위의 코드에서는 내부 클래스로 State라는 enum class를 생성하였고, 그 내부에 두가지 상수값 WAITING, PROCESSING을 정의하였다. Task class의 printState에서는 클래스 내부에 전역으로 잡힌 state변수를 가져와서 state의 상태마다 state를 알려주는 문자값을 print해준다.

 

이때 우리가 State에 DONE을 추가한다고 해보자. 이러한 상황에서 IDE는 DONE에 대한 처리 로직이 없으면 아래와 같은 오류를 생성한다.

 

 

그림1. state 중 DONE 처리가 없어 오류가 뜨는 현상

 

이를 해결하기 위해서는 아래와 같이 DONE에 대한 처리를 해주어야 한다.

class Task {
    var state: State = State.WAITING

    fun printState() =
        when(state){
            State.WAITING -> println("Waiting..")
            State.PROCESSING -> println("Processing..")
            State.DONE -> println("Task done")
        }

    enum class State {
        WAITING, PROCESSING, DONE
    }
}

즉, enum class를 사용해주면 상수값의 타입 안전성이 보장되며, 특정한 상태가 추가되었을 때 처리해야 하는 코드들을 IDE 단에서 잡아준다. 코드가 작을 때는 이러한 상태값에 빠진 동작이 있더라도 금방 잡아낼 수 있지만, 코드가 커지면 커질 수록 이러한 자잘한 잘못된 동작이 모여 시스템을 망가트린다.

 

enum class를 이용하면 다음과 같은 이점이 생긴다.

1. 상수 값의 타입 안전성이 보장된다.
2. 코드 가독성이 올라간다.
3. 상수 값에 대한 특정한 동작이 필요할 때 특정 상수값에 대한 동작을 빼먹는 것을 방지할 수 있다.
4. 상수와 연관된 변수를 상수에 저장할 수 있다.

앞의 예제에서 1번과 2번은 보았다. 이 둘만으로도 충분히 enum class를 쓸만한 이점이 있지만, 3, 4번 또한 중요한 이점 중 하나이다. 다음 글에서는 enum class의 선언 방법과 함께 3, 4 번에 대해 알아볼 것이다.

반응형