0Pricing
Kotlin Academy · 강의

봉인 계층 구조 중첩하고 결합하기

봉인 타입을 중첩해 복잡한 상태 머신을 만들어 보세요.

봉인 계층 구조 중첩하고 결합하기은(는) CoddyKit의 무료 Kotlin Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Kotlin Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Kotlin Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

상태 트리 구성하기

실제 상태에는 여러 차원이 있는 경우가 많습니다. 복잡한 영역을 깔끔하게 모델링하려면 봉인 타입을 중첩하거나 별도의 계층 구조를 결합합니다.

중첩된 봉인 클래스

바깥쪽 봉인 타입 안에 자식 봉인 타입을 선언합니다. 컴파일러는 전체 트리에서 모든 경우가 처리되었는지 계속 확인합니다.

sealed class UiState {
    object Loading : UiState()
    sealed class Loaded : UiState() {
        data class Success(val items: List<String>) : Loaded()
        data class Empty(val message: String) : Loaded()
    }
    data class Error(val msg: String) : UiState()
}
fun render(s: UiState) = when (s) {
    UiState.Loading -> "loading"
    is UiState.Loaded.Success -> "${s.items.size} items"
    is UiState.Loaded.Empty -> "empty: ${s.message}"
    is UiState.Error -> "err: ${s.msg}"
}
fun main() {
    println(render(UiState.Loaded.Success(listOf("a", "b"))))
}

믹스인을 위한 봉인 인터페이스

상태에 서로 독립적인 여러 측면이 있을 때는 봉인 인터페이스를 사용합니다. 각 측면을 서로 다른 인터페이스로 표현합니다.

sealed interface Authenticated
data class User(val id: Int) : Authenticated
object Guest
sealed interface Permission
object Read : Permission
object Write : Permission
fun describe(a: Authenticated, p: Permission): String = when {
    a is User && p is Write -> "user ${a.id} can write"
    a is User -> "user ${a.id} can read"
    else -> "?"
}
fun main() {
    println(describe(User(1), Write))
}

계층 구조 결합하기

두 봉인 값을 전달하고 그 조합을 기준으로 분기합니다. 조건이 있는 when을 사용합니다.

sealed class Theme { object Light : Theme(); object Dark : Theme() }
sealed class Lang { object En : Lang(); object Tr : Lang() }
fun greet(theme: Theme, lang: Lang) = when {
    theme is Theme.Dark && lang is Lang.Tr -> "Karanlik Merhaba"
    theme is Theme.Light && lang is Lang.Tr -> "Aydinlik Merhaba"
    theme is Theme.Dark -> "Dark Hello"
    theme is Theme.Light -> "Light Hello"
    else -> "?"
}
fun main() {
    println(greet(Theme.Dark, Lang.Tr))
}

중첩된 변형을 사용하는 상태 머신

여러 단계의 작업 흐름은 중첩된 봉인 타입으로 모델링합니다. 각 단계에는 고유한 변형이 있습니다.

sealed class Checkout {
    object Start : Checkout()
    sealed class Payment : Checkout() {
        object Pending : Payment()
        data class Failed(val reason: String) : Payment()
        object Captured : Payment()
    }
    object Shipped : Checkout()
    object Delivered : Checkout()
}
fun describe(c: Checkout) = when (c) {
    Checkout.Start -> "starting"
    Checkout.Payment.Pending -> "awaiting payment"
    is Checkout.Payment.Failed -> "payment failed: ${c.reason}"
    Checkout.Payment.Captured -> "paid"
    Checkout.Shipped -> "shipped"
    Checkout.Delivered -> "delivered"
}
fun main() {
    println(describe(Checkout.Payment.Failed("card declined")))
}

모듈 간 봉인 계층 구조 재사용하기

봉인 타입은 모듈별로 정의하는 것이 가장 좋습니다. 모듈 간에 구성하려면 봉인 인터페이스를 공개하여 각 모듈이 자체 구현을 추가할 수 있게 합니다.

sealed interface Notification
data class Email(val to: String) : Notification
data class Sms(val phone: String) : Notification
fun send(n: Notification) = when (n) {
    is Email -> "sending email to ${n.to}"
    is Sms -> "texting ${n.phone}"
}
fun main() {
    println(send(Email("a@example.com")))
}

공통 기반 속성

봉인 클래스는 공통 속성을 선언할 수 있으며, 모든 하위 타입이 이를 상속합니다. 이를 통해 상태와 메타데이터를 DRY하게 유지할 수 있습니다.

sealed class HttpResponse(val code: Int) {
    class Ok(val body: String) : HttpResponse(200)
    class NotFound(val path: String) : HttpResponse(404)
    class ServerError(val cause: String) : HttpResponse(500)
}
fun describe(r: HttpResponse) = "[${r.code}] " + when (r) {
    is HttpResponse.Ok -> r.body
    is HttpResponse.NotFound -> "not found: ${r.path}"
    is HttpResponse.ServerError -> "fail: ${r.cause}"
}
fun main() {
    println(describe(HttpResponse.Ok("hello")))
}

재귀 트리 모델링

봉인 타입은 식 트리와 같은 재귀 구조를 자연스럽게 표현합니다.

sealed class Expr {
    data class Num(val value: Int) : Expr()
    data class Add(val left: Expr, val right: Expr) : Expr()
    data class Mul(val left: Expr, val right: Expr) : Expr()
}
fun eval(e: Expr): Int = when (e) {
    is Expr.Num -> e.value
    is Expr.Add -> eval(e.left) + eval(e.right)
    is Expr.Mul -> eval(e.left) * eval(e.right)
}
fun main() {
    val expr = Expr.Add(Expr.Num(3), Expr.Mul(Expr.Num(4), Expr.Num(5)))
    println(eval(expr)) // 23
}

제네릭과 함께 사용하기

봉인 타입은 제네릭일 수 있으므로 Result<T>와 같이 타입이 지정된 결과 래퍼를 만들 수 있습니다.

sealed class ApiResult<out T> {
    data class Success<T>(val data: T) : ApiResult<T>()
    data class Failure(val message: String) : ApiResult<Nothing>()
}
fun handle(r: ApiResult<String>) = when (r) {
    is ApiResult.Success -> "got ${r.data}"
    is ApiResult.Failure -> "err: ${r.message}"
}
fun main() {
    println(handle(ApiResult.Success("hi")))
    println(handle(ApiResult.Failure("nope")))
}

안티패턴: 지나치게 깊은 중첩

깊은 중첩은 의도를 숨깁니다. 봉인 트리가 2단계보다 깊다면 평탄화하거나 하위 트리를 별도의 파일로 추출하는 것을 고려합니다.

sealed class Top {
    sealed class Mid : Top() {
        sealed class Deep : Mid() {
            object Leaf : Deep()
        }
    }
}
fun main() {
    val l = Top.Mid.Deep.Leaf
    println(l::class.simpleName)
}

구성을 통한 결합

두 상태 차원이 서로 독립적이라면 깊게 중첩된 계층 구조보다 구성을 우선합니다. 예를 들어 두 봉인 값을 감싸는 데이터 클래스를 사용합니다.

sealed class Loading { object Active : Loading(); object Idle : Loading() }
sealed class Auth { data class Signed(val id: Int) : Auth(); object Anon : Auth() }
data class AppState(val loading: Loading, val auth: Auth)
fun main() {
    val s = AppState(Loading.Active, Auth.Signed(1))
    println(s)
}

빠른 확인

두 상태 차원이 서로 독립적일 때 권장되는 접근 방식은 무엇입니까?

복습

트리 형태의 상태에는 봉인 타입을 중첩하고, 독립적인 봉인 계층 구조는 구성으로 결합합니다(래퍼 데이터 클래스 사용). 봉인 기반 타입에 공통 속성을 추가하고, 타입이 지정된 결과에는 제네릭을 사용하며, 재귀 구조를 자연스럽게 모델링합니다. 중첩은 얕게 유지합니다.

자주 묻는 질문

“봉인 계층 구조 중첩하고 결합하기” 강의는 무료인가요?

네 — “봉인 계층 구조 중첩하고 결합하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Kotlin Academy 강의 전체를 잠금 해제할 수 있습니다. Kotlin Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“봉인 계층 구조 중첩하고 결합하기”에서 뭘 배우나요?

봉인 타입을 중첩해 복잡한 상태 머신을 만들어 보세요. 브라우저에서 직접 실행하는 실습 코드로 Kotlin Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Kotlin Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Kotlin Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.

“봉인 계층 구조 중첩하고 결합하기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Kotlin Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Kotlin Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. sealed class와 sealed interface: 각각 언제 사용할까요
  2. 봉인 계층 구조와 함께 사용하는 완전한 when
  3. 봉인 클래스로 UI 상태 모델링하기
  4. 봉인 계층 구조 중첩하고 결합하기
← Kotlin Academy(으)로 돌아가기