(Android/Navigation) Navigation Component — Safe Args 타입 안전 전달


1. 왜 Safe Args인가?

Bundle로 인자를 전달하면 키 문자열 오타와 타입 불일치가 런타임에야 드러납니다.

// ❌ Bundle: 키 오타, 타입 오류가 컴파일 타임에 잡히지 않음
navController.navigate(R.id.detailFragment, bundleOf("produtId" to 42))
val id = arguments?.getString("productId")  // null 또는 ClassCastException

Safe Args는 그래프 정의를 읽어 전용 클래스를 생성해 컴파일 타임에 검증합니다.


2. 설정

// 프로젝트 build.gradle.kts
plugins {
    id("androidx.navigation.safeargs.kotlin") version "2.8.0" apply false
}

// 앱 모듈 build.gradle.kts
plugins {
    id("androidx.navigation.safeargs.kotlin")
}

3. 그래프에 인자 선언

<fragment
    android:id="@+id/detailFragment"
    android:name="com.example.app.DetailFragment">

    <argument
        android:name="productId"
        app:argType="integer" />          <!-- 필수 인자 -->

    <argument
        android:name="source"
        app:argType="string"
        android:defaultValue="home" />    <!-- 기본값이 있으면 선택 인자 -->

    <argument
        android:name="product"
        app:argType="com.example.app.Product"
        app:nullable="true"
        android:defaultValue="@null" />   <!-- Parcelable 객체, null 허용 -->
</fragment>

4. 보내기 — Directions 클래스

빌드하면 HomeFragmentDirections가 생성되고, 필수 인자는 함수 파라미터가 됩니다.

binding.itemClick = { productId ->
    // 필수 인자를 빠뜨리면 컴파일 에러
    val action = HomeFragmentDirections.actionHomeToDetail(productId = productId)
    findNavController().navigate(action)
}

5. 받기 — navArgs()

DetailFragmentArgs가 생성되며 navArgs() 위임으로 꺼냅니다.

class DetailFragment : Fragment(R.layout.fragment_detail) {

    // 타입이 보장된 인자 접근 (Int, String 등)
    private val args: DetailFragmentArgs by navArgs()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val id: Int = args.productId
        val source: String = args.source   // 지정 안 하면 "home"
        viewModel.load(id)
    }
}

ViewModel에서는 SavedStateHandle로 같은 인자를 받을 수 있습니다 (savedStateHandle.get<Int>("productId")). Hilt ViewModel이면 별도 전달 코드가 필요 없습니다.


6. 지원 타입

argType Kotlin 타입
integer Int
long, float, boolean Long, Float, Boolean
string String
reference 리소스 ID (@IdRes 등)
클래스 경로 Parcelable, Serializable, enum
integer[] 등 배열 (app:argType="integer[]")

7. 화면 결과 돌려받기

Safe Args는 보내는 방향만 담당합니다. 결과 반환은 SavedStateHandle이나 Fragment Result API를 사용합니다.

// 결과를 돌려주는 쪽 (SelectFragment)
findNavController().previousBackStackEntry
    ?.savedStateHandle?.set("selected_color", "red")
findNavController().popBackStack()

// 결과를 받는 쪽 (HomeFragment)
findNavController().currentBackStackEntry
    ?.savedStateHandle
    ?.getLiveData<String>("selected_color")
    ?.observe(viewLifecycleOwner) { color -> /* 반영 */ }

8. 정리



Related Posts