Type-Safe Navigation 커스텀 파라미터 전달

covy·2025년 8월 30일

Type-Safe Navigation을 적용하면서 화면간 인자를 전달하는 것이 간단해졌다.

문제 상황

하지만 복잡한 구조의 인자를 전달하면서 에러가 발생했다.

typeMap received was {}

나가 전달하고자 하는 구조는 아래와 같다.

// RouteFriendProfileEditor
data class RouteFriendProfileEditor(
    val friend: Friend,
)
// Friend
data class Friend(
    val friendId: String,
    val imageUrl: String?,
    val relation: Relation,
    val name: String,
    val contactFrequency: ContactFrequency,
    val birthday: String?,
    val anniversaryList: List<Anniversary>,
    val memo: String?,
    val phone: String?,
    val lastContactAt: String?, // "2025-07-16"
) 

더 내부에 있는 클래스는 생략한다.

왜 오류가 발생할까?
Type-Safe Navigation 내부의 object에 대해 직렬화하는 로직이 없는 것으로 보인다.
++추가 정리 예정

해결 방법

해결 방법은 아래와 같다.

플러그인 추가

앱 수준의 모듈에 Parcelable 플러그인을 설치한다. (플러그인 없이 수동 구현을 할 수 있다)

plugins {
    id("kotlin-parcelize")
}

직렬화 적용

직렬화 적용 및 커스텀 type 전달을 위한 typeMap을 선언한다.
Friend 내부 커스텀 타입도 직렬화가 필요하다.

@Serializable
@Parcelize
data class RouteFriendProfileEditor(
    val friend: Friend,
) : Parcelable {
    companion object {
        val routeTypeMap =
            mapOf<KType, NavType<*>>(
                typeOf<Friend>() to FriendType,
            )
    }
}

직렬화 타입 선업

Navigation에 전달할 구현체를 가져오는 방법을 선언한다.

internal val FriendType =
    object : NavType<Friend>(
        isNullableAllowed = false,
    ) {
        override fun put(
            bundle: SavedState,
            key: String,
            value: Friend,
        ) {
            bundle.putParcelable(key, value)
        }

        override fun get(
            bundle: SavedState,
            key: String,
        ): Friend? =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                bundle.getParcelable(key, Friend::class.java)
            } else {
                @Suppress("DEPRECATION")
                bundle.getParcelable(key)
            }

        override fun parseValue(value: String): Friend = Json.decodeFromString<Friend>(value)

        override fun serializeAsValue(value: Friend): String = Json.encodeToString(value)
    }

ViewModel에서 savedStateHandle을 통해 Route를 가져온다.
RouteFriendProfileEditor.routeTypeMap 타입맵을 지정해주어야 한다.

// viewModel에서 사용
private val routeFriendProfileEditor: RouteFriendProfileEditor =
            savedStateHandle.toRoute<RouteFriendProfileEditor>(RouteFriendProfileEditor.routeTypeMap)

회고

생각지 못한 부분에서 트러블이 발생했다. 급하게 해결하기 위해 틀린 그림 찾기 마냥 해결방법을 찾고 있던 나를 발견했다.
내부 인자를 어떻게 전달하는지 원리를 알았더라면.. 공부해야지

Ref.
https://developer.android.com/guide/navigation/design/kotlin-dsl?hl=ko
https://github.com/easyhooon/NavigationCustomDataClassArgumentExample/issues/1
https://developer.android.com/kotlin/parcelize?hl=ko

0개의 댓글