# Jetpack Navigation Compose 2026年版:型安全ナビゲーションと面接対策 > Navigation Compose 2.10の型安全APIを詳細解説。Kotlin Serializationによるルート定義、ネストグラフ、ディープリンク、面接頻出質問まで網羅。 - Published: 2026-09-01 - Updated: 2026-09-01 - Author: Anthony Fillion-Maillet - Tags: android, jetpack-compose, navigation, kotlin, type-safe - Reading time: 12 min --- Jetpack Navigation Compose 2.10は、Kotlin Serializationを活用した型安全なナビゲーションをAndroid開発に導入しています。このアプローチにより、引数の不一致によるランタイムクラッシュが排除され、大規模コードベースでのリファクタリングが大幅に簡素化されます。 > **Navigation 2.10 安定版** > > Navigation Compose 2.10.0は2026年8月にリリースされ、最小SDK 24が必要です。型安全ルートは文字列ベースの宛先ではなく`@Serializable`データクラスを使用し、ナビゲーションエラーをコンパイル時に検出します。 ## Navigation 2.10における型安全ナビゲーションのセットアップ 型安全ナビゲーションAPIにはKotlin Serializationが必要です。モジュールレベルの`build.gradle.kts`に以下の依存関係を追加します: ```kotlin // build.gradle.kts plugins { id("org.jetbrains.kotlin.plugin.serialization") version "2.1.0" } dependencies { implementation("androidx.navigation:navigation-compose:2.10.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3") } ``` 宛先はシリアライズ可能な型として定義します。引数なしの画面には`object`を、パラメータを受け取る画面には`data class`を使用します: ```kotlin // Routes.kt import kotlinx.serialization.Serializable @Serializable object Home @Serializable object ProductList @Serializable data class ProductDetail(val productId: String) @Serializable data class Checkout(val cartId: String, val promoCode: String? = null) ``` コンパイラはすべてのルートクラスに`@Serializable`アノテーションが付与されていることを強制します。Navigation 2.9以降では、アノテーションの欠落はリントエラーを引き起こします。 ## シリアライズ可能ルートによるNavHostの構築 `NavHost`コンポーザブルはシリアライズ可能な型を直接受け入れます。各`composable`ブロックは特定の宛先型を処理します: ```kotlin // AppNavigation.kt @Composable fun AppNavigation( navController: NavHostController = rememberNavController() ) { NavHost( navController = navController, startDestination = Home ) { composable { HomeScreen( onNavigateToProducts = { navController.navigate(ProductList) } ) } composable { ProductListScreen( onProductClick = { productId -> navController.navigate(ProductDetail(productId)) } ) } composable { backStackEntry -> // バックスタックエントリから型付きルートを抽出 val route: ProductDetail = backStackEntry.toRoute() ProductDetailScreen( productId = route.productId, onCheckout = { cartId -> navController.navigate(Checkout(cartId)) } ) } composable { backStackEntry -> val route: Checkout = backStackEntry.toRoute() CheckoutScreen( cartId = route.cartId, promoCode = route.promoCode ) } } } ``` `toRoute()`拡張関数は、宛先の完全に型付けされたインスタンスを返します。データクラスのプロパティ名を変更すると、IDEリファクタリングを通じてすべての使用箇所が自動的に更新されます。 ## 機能モジュール向けネストナビゲーショングラフ 大規模アプリケーションではネストナビゲーショングラフが有効です。各機能モジュールは独自のグラフを定義し、ルートグラフがそれらを構成します: ```kotlin // AuthNavigation.kt @Serializable object AuthGraph // グラフ識別子 @Serializable object Login @Serializable object Register @Serializable data class PasswordReset(val email: String) fun NavGraphBuilder.authNavGraph(navController: NavHostController) { navigation(startDestination = Login) { composable { LoginScreen( onRegisterClick = { navController.navigate(Register) }, onForgotPassword = { email -> navController.navigate(PasswordReset(email)) }, onLoginSuccess = { navController.navigate(Home) { popUpTo { inclusive = true } } } ) } composable { RegisterScreen( onRegistrationComplete = { navController.navigate(Home) { popUpTo { inclusive = true } } } ) } composable { backStackEntry -> val route: PasswordReset = backStackEntry.toRoute() PasswordResetScreen(email = route.email) } } } ``` `popUpTo`と`inclusive = true`を組み合わせることで、ログイン成功後に認証フロー全体をバックスタックからクリアし、ユーザーがログイン画面に戻ることを防止します。 ## ルート引数としてのValue Class Navigation 2.9.0-alpha03では、ルートにおける[value class](https://kotlinlang.org/docs/inline-classes.html)のサポートが追加されました。このパターンはIDの型安全性を強制し、同じプリミティブ型のパラメータを混同することを防ぎます: ```kotlin // Domain.kt @JvmInline @Serializable value class ProductId(val value: String) @JvmInline @Serializable value class UserId(val value: String) @Serializable data class ProductDetail(val productId: ProductId) @Serializable data class UserProfile(val userId: UserId) ``` これにより、コンパイラは`ProductId`が期待される場所に`UserId`を渡すことを防止します。これは文字列ベースのナビゲーションでよく発生するバグです。 ## 型安全ルートによるディープリンク ディープリンクは外部URIをシリアライズ可能なルートにマッピングします。パターンは`composable`ブロックで定義します: ```kotlin // AppNavigation.kt composable( deepLinks = listOf( navDeepLink { uriPattern = "https://example.com/products/{productId}" } ) ) { backStackEntry -> val route: ProductDetail = backStackEntry.toRoute() ProductDetailScreen(productId = route.productId) } ``` `{productId}`プレースホルダーは`ProductDetail`データクラスの`productId`プロパティに直接マッピングされます。Navigationは引数を自動的に抽出してデシリアライズします。 `AndroidManifest.xml`にディープリンクを登録します: ```xml ``` ## Navigation Composeにおける予測バックジェスチャー Android 14以降では、ユーザーがコミット前に前の画面をプレビューできる予測バックジェスチャーがサポートされています。Navigation Compose 2.10は`SeekableTransitionState`を通じてこれと統合します: ```kotlin // AppNavigation.kt NavHost( navController = navController, startDestination = Home, enterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start) }, exitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start) }, popEnterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End) }, popExitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End) } ) { // destinations } ``` 予測バックジェスチャーは、ユーザーがスワイプすると前の宛先をリアルタイムで表示します。閾値に達する前にジェスチャーを離すと、ナビゲーションがキャンセルされ現在の画面に戻ります。 ## Navigation Composeの面接頻出質問 シニアAndroid面接では、ナビゲーションアーキテクチャの決定についてしばしば深掘りされます。以下は経験豊富な候補者を見分ける質問です。 ### なぜ文字列ベースルートより型安全ナビゲーションを使うのですか? `"product/{productId}"`のような文字列ベースルートは、引数のスペルミスや型の誤りでランタイムクラッシュを引き起こします。型安全ルートはこれらのエラーをコンパイル時に検出します。ルートパラメータのリファクタリングはIDEツールを通じてすべての使用箇所を更新しますが、文字列ベースルートはコードベース全体で手動の検索・置換が必要です。 ### 大きなオブジェクトを渡さずに宛先間でデータを共有するにはどうすればよいですか? > **大きなルート引数を避ける** > > ルートは保存インスタンス状態にシリアライズされます。商品カタログのような大きなオブジェクトを渡すと`TransactionTooLargeException`のリスクがあります。IDを渡してリポジトリまたは共有ViewModelからデータを読み込む方式を採用してください。 データ共有のパターンにはスコープ付きViewModelを使用します: ```kotlin // ProductViewModel.kt @HiltViewModel class ProductViewModel @Inject constructor( private val repository: ProductRepository, savedStateHandle: SavedStateHandle ) : ViewModel() { // SavedStateHandleから型付きルートを抽出 private val productId: String = savedStateHandle.toRoute().productId val product = repository.getProduct(productId) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null) } ``` ### Composeでナビゲーションをテストするにはどうすればよいですか? ナビゲーションテストは、ユーザーアクションが正しい宛先変更をトリガーすることを検証します: ```kotlin // NavigationTest.kt class NavigationTest { @get:Rule val composeTestRule = createComposeRule() @Test fun clickingProduct_navigatesToDetail() { val navController = TestNavHostController(ApplicationProvider.getApplicationContext()) navController.navigatorProvider.addNavigator(ComposeNavigator()) composeTestRule.setContent { AppNavigation(navController = navController) } // 商品リストに遷移 composeTestRule.onNodeWithText("View Products").performClick() // 最初の商品をクリック composeTestRule.onNodeWithTag("product_item_0").performClick() // 現在の宛先を検証 val currentRoute = navController.currentBackStackEntry?.toRoute() assertThat(currentRoute).isNotNull() } } ``` ### NavControllerが破棄された後に使用されるとどうなりますか? Navigation 2.9.0-alpha01でこの動作が変更されました。ホストする`Activity`または`Fragment`が破棄された後に`NavController`を使用すると、`IllegalStateException`がスローされるようになりました。以前のバージョンでは黙って失敗するか、未定義の動作を生じていました。この変更により、ライフサイクルのバグが開発の早い段階で表面化します。 ### 認証状態に基づいた条件付きナビゲーションをどのように処理しますか? 一般的なパターンは認証状態を監視し、それに応じてリダイレクトします: ```kotlin // MainScreen.kt @Composable fun MainScreen( authState: AuthState, navController: NavHostController = rememberNavController() ) { LaunchedEffect(authState) { when (authState) { is AuthState.Authenticated -> { navController.navigate(Home) { popUpTo(0) { inclusive = true } } } is AuthState.Unauthenticated -> { navController.navigate(AuthGraph) { popUpTo(0) { inclusive = true } } } else -> { /* loading, do nothing */ } } } NavHost( navController = navController, startDestination = if (authState is AuthState.Authenticated) Home else AuthGraph ) { authNavGraph(navController) composable { HomeScreen() } } } ``` `LaunchedEffect`は認証状態の変更に反応し、`popUpTo(0) { inclusive = true }`でバックスタック全体をクリアして、誤ったフローへの戻りナビゲーションを防止します。 ## 文字列ルートから型安全ナビゲーションへの移行 既存のプロジェクトは段階的に移行できます。文字列ルートと型安全ルートは同じ`NavHost`内で共存できます: ```kotlin // AppNavigation.kt NavHost( navController = navController, startDestination = "home" // レガシー文字列ルート ) { // レガシー文字列ベースの宛先 composable("home") { HomeScreen(onNavigateToProduct = { id -> navController.navigate(ProductDetail(id)) // 新しい型安全ナビゲーション }) } // 新しい型安全な宛先 composable { backStackEntry -> val route: ProductDetail = backStackEntry.toRoute() ProductDetailScreen(productId = route.productId) } } ``` このアプローチにより、ナビゲーショングラフ全体を書き換えることなく、一度に1画面ずつ移行できます。 ## 2026年版Jetpack Navigation Composeの重要ポイント - Navigation 2.10.0は最小SDK 24の現行安定版です - 型安全ルートは文字列宛先の代わりに`@Serializable`データクラスを使用します - Value classはIDに追加の型安全性を提供し、パラメータの混同を防止します - ネストナビゲーショングラフは独立したバックスタックで機能モジュールを整理します - ディープリンクはURIパターンをルートプロパティに直接マッピングします - 予測バックジェスチャーは標準トランジションと自動的に統合します - ルート引数は軽量に保つべきです:IDを渡し、リポジトリからデータを読み込みます - 文字列ルートからの移行は混合ナビゲーショングラフで段階的に行えます - [Android面接対策](/technologies/android)でこれらのパターンを深く学べます - 関連するUIコンセプトについては[Jetpack Compose面接質問](/blog/android/jetpack-compose-interview-questions)も参照してください --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/android/jetpack-navigation-compose-type-safe-2026