본문 바로가기

안드로이드/코틀린

[안드로이드 코틀린 Sealed class] 설명 및 코드 예제 #1


1. 안드로이드 코틀린의 Enum class의 한계

안녕하세요. 지난 포스팅에서는 enum class에 대해 학습했었습니다. 코틀린의 enum class는 속성, 함수를 구현할 수 있어서 다른 언어에 비해서 강력한 기능을 제공해 줬었습니다. 그런데 이러한 enum class에도 한계가 있었는데요. 아래 코드처럼 상수값 별로 다른 데이터 타입이 필요한 경우였습니다.

enum class TrafficLight(val guide: String, val additionalData: String?) {
    RED("Stop", null), // null을 추가해줘야함
    YELLOW("Slow down", null), // null을 추가해줘야함
    GREEN("Go", "Additional data for green")
}

fun displayTrafficLightStatus(light: TrafficLight) {
    when (light) {
        TrafficLight.RED -> println(light.guide)
        TrafficLight.YELLOW -> println(light.guide)
        TrafficLight.GREEN -> println(light.guide + light.additionalData)
    }
}

이러한 한계를 극복하고 더욱 유연하게 사용할 수 있는 방법은 코틀린의 Sealed class를 사용하는 것입니다. 그럼 이제부터 Sealed class에 대해 설명하겠습니다.  

2. 안드로이드 코틀린의 Sealed class 소개

Kotlin에서 sealed class는 enum class의 한계를 극복하고, 더욱 유연하게 사용할 수 있는 구조를 제공합니다. 특히, 상수 값 별로 다른 데이터 타입이나 추가적인 정보가 필요할 때 sealed class가 매우 유용하게 사용됩니다. sealed class를 이용하면, 각각의 경우(case)를 별도의 클래스로 정의할 수 있으며, 각 클래스는 서로 다른 속성과 메서드를 가질 수 있습니다.

sealed class 사용예시
API 통신 결과로 자주 사용되는 sealed class

2-1. Sealed Class의 기본 개념

sealed class는 자기 자신이 추상 클래스이고 자신을 상속받는 여러 서브 클래스를 구성할 수 있습니다. 즉, 서브 클래스가 Enum의 상수값 역할을 한다고 생각하면 이해하기 쉽습니다. sealed class의 주요 특징은 모든 서브 클래스가 같은 파일 내에 선언되어야 한다는 것입니다. 이로 인해, sealed class의 서브 클래스는 미리 정의된 범위 내에서만 확장될 수 있습니다. 이러한 특성은 컴파일 타임에 타입 안전성을 높이고, Kotlin의 when 표현식을 사용할 때 모든 가능한 케이스를 처리했는지 컴파일러가 검사할 수 있게 합니다.

2-2. Sealed class 코드 예제

enum class를 설명하면서 사용했던 TrafficLight 예제를 sealed class를 사용해 리팩토링 해보겠습니다. 이를 통해, 각 케이스별로 다른 타입의 데이터를 가질 수 있게 됩니다.

sealed class TrafficLight(val guide: String) {
    class RED : TrafficLight("Stop")
    class YELLOW : TrafficLight("Slow down")
    class GREEN(additionalData: String) : TrafficLight("Go") {
        private val data = additionalData
        fun displayGreenData() {
            println("$guide: $data")
        }
    }
}

fun displayTrafficLightStatus(light: TrafficLight) {
    when (light) {
        is TrafficLight.RED -> println(light.guide)
        is TrafficLight.YELLOW -> println(light.guide)
        is TrafficLight.GREEN -> light.displayGreenData()
    }
}

enum class를 사용했을 때와는 달리 불필요한 null 값 전달이 사라졌습니다. 그리고 GREEN Class에만 additionalData 속성이 있고 displayGreenData라는 함수가 존재하게 됩니다. 즉, 타입별로 서로 다른 유형의 속성과 함수를 가질 수 있게 됩니다. 그래서 코틀린의 sealed class는 API 응답을 처리할 때도 유용하게 사용됩니다. API 호출에 대한 성공과 에러를 처리할 때 enum처럼 쓰고 싶지만, 처리하는 데이터 타입이 다른 대표적인 상황이기 때문입니다.

sealed class Result<T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error<T>(val error: Throwable) : Result<T>()
}
  • Success: 성공에 대한 데이터 처리
  • Error: 실패에 대한 예외 타입 처리

3. Sealed class 정리

코틀린의 sealed class는 enum class의 한계를 극복할 수 있습니다. enum class보다 더욱 유연하고, 타입을 체크하는 등 안전한 방법으로 상태나 결과를 처리할 수 있게 해 줍니다. 즉, enum class와 마찬가지로 컴파일 타임에서 모든 가능한 케이스를 처리했는지 확인하여 런타임 에러의 가능성을 크게 줄일 수 있습니다.

그러나 enum class보다 sealed class가 무조건 좋다고 설명한 글은 아닙니다. 상수값만을 관리할 때는 enum class를 사용하고, 각 타입별로 다른 값이 처리되어야 한다면 sealed class를 사용하는 형태로, 상황에 맞춰서 사용하는 게 좋다고 생각합니다.

다음 포스팅에서는 sealed class에 대해 설명하겠습니다.