java.lang.IllegalArgumentException: navigation destination Splash is not a direct child of this NavGraph
Navigation은 화면의 이동을 담당하는 NavController와 이동할 컴포저블 대상을 매핑하는 NavGraph, NavGraph의 현재 대상을 표시하는 컨테이너 역활의 컴포저블인 NavHost 총 3가지로 구성된다.
사용하는 방법으로는 우선 탐색을 할 경로를 정의해야하는데, 이는 Constants를 통해 정의되어있다. 다음으로 지정된 경로를 기반으로 다른 컴포저블 대상을 표시하는 컴포저블인 NavHost를 추가해야하는데 문법은 아래와 같다.
NavHost(
navController,
startDestination,
modifier,
)
navController는 NavHostController의 클래스 인스턴스로 navigate()메서드를 호출하여 다른 대상으로 이동하는 방식으로 화면 간 이동에 이 객체를 사용한다. 구성 가능한 함수에서 rememberNavController()를 호출하여 NavHostController를 가져올 수 있다. startDestination은 앱에서 NavHost를 처음 표시할 때 기본적으로 표시되는 대상을 정의하는 문자열 경로이다.(Constants)
NavHost에서 경로처리하기위해 composable(route){content} 유형을 사용하는데, route는 고유 문자열(Constants), content는 특정 경로에 표시할 컴포저블을 호출할 수 있다.
그 후 경로간 이동시에는 rememberNavController()로 얻은 NavHostController가 담당하는데, 이를 매개변수로 각 컴포저블에 전달하면 된다?
잠시 정리하면, navigation은 화면간 이동을 말하는 것이다. 다시 맨 처음의 버그 메시지를 잘 보자. NavGraph는 jetpack navigation사용 시 필수 요소로 모든 네비게이션을 명세한 xml 리소스이다. 각 요소들은 action속성 내에 destination을 가지고 있다. 하지만 현재 프로젝트에는 graph xml도, kt파일도 존재하지 않는다.
https://small-stepping.tistory.com/926
https://velog.io/@jeongminji4490/Error-Navigation-destination-is-not-a-direct-child-of-this-NavGraph
자 점심도 먹었겠다 이미 베이스 공부는 진행했기에 두뇌를 초기화한 다음에 문제를 해결해보자.
코드를 기반으로 이해해보니 화면 이동이 필요한 화면인 경우 인자로 해당 화면으로 이동하는 navController.navigate()를 건네주는 듯 하다. navController.popBackStack()은 뭘까? 안드로이드는 뒤로가기를 위해 방문한 페이지들의 navigation stack을 사용한다. 즉, 위 함수를 호출시키면 이전화면으로 가는 navController.navigate()가 실행되는 것이다.
우선 어느 부분에서 문제가 발생하는지 명확히 파악하고자 주석처리를 통해 정상작동 되는 범위까지 도달하였다. 문제는 InstaApp에서 발생하고 있었다. MainActivity에서 호출된 InstaApp에서 navhost를 사용하지 않고 바로 SignInScreen을 수행하니 작동이 되었다. 그 후 AnimatedNavHost를 기본 추가하자마자 오류가 발생하는 것을 확인할 수 있었다.
AnimatedNavHost, composable 등 작은 단위씩 추가하며 실행해보았는데, 정상적으로 실행이 되었다.
문제의 원인은 다음과 같다. 이제야 이해를 한 로직이, AnimatedNavHost는 내부적으로 NavGraph를 이용해 화면을 관리한다. 이때 최초로 실행할 화면은 AnimatedNavHost의 startDestination인자로 Constants.ROUTE_SIGN_IN으로 지정하였다. 그 후 아래 여러 composable이 존재하고 startDestination에서 수행하는 사용자 조작에 따라 알맞은 composable화면을 찾아 띄우는 것이다. startDestination을 Constants.ROUTE_SIGN_IN으로 지정해두었기에 해당 composable을 추가하자.
composable(Constants.ROUTE_SIGN_IN)으로 명시한다면 바로 AnimatedNavHost가 찾을 수 있고 내부적으로 호출한 SignInScreen을 정상적으로 호출하여 실행시켜주는 것이다.
이 문제는 AnimatedNavHost내부에 composable을 명시하지 않아 발생한 오류였다.
composable(
route= ,
enterTransition= ,
exitTransition= ,
popEnterTransition= ,
popExitTransition=
) {}
꼴의 상태에서
composable(Constants.ROUTE_SIGN_IN){}
로 바꾸어 문제를 해결하였다. 두 방식의 차이는 추후 사수분에게 물어봐야겠다.
withContext(Dispatchers.Main){
updateIsSignedIn(true)
}
최대한 샘플코드를 그대로 구현하기 위해, SignInViewModel의 signIn function에서 호출되도록 코드를 보았다. 코드의 시작을 병렬로 진행하기 위해 viewModelScope.launch(Dispatchers.IO)로 감쌌는데, 내 코드에서는 로그인 성공을 가정했을 때 updateIsSignedIn(true)를 바로 호출했다. 비동기적인 상황에서는 withContext(Dispatchers.Main)으로 감싼 뒤에 특정 작업을 수행해야하나보다. 이를 추가해주니 정상적으로 SignInScreen의 onClick콜백에서 SignInViewModel을 거쳐 updateIsSignedIn(true)를 통해 화면 전환이 성공적으로 진행되었다.
implementation("io.coil-kt:coil-compose:2.7.0")
https://coil-kt.github.io/coil/
오픈소스를 참조하여 정말 많은 디자인적 요소(홈)을 구성하고, 실행시키니 아래의 오류가 발생하였다.
28 issues were found when checking AAR metadata:
Dependency 'androidx.compose.animation:animation-core-android:1.6.8' requires libraries and applications that
depend on it to compile against version 34 or later of the
Android APIs.
GPT의 도움을 받아 build.gradle의 android.compileSdk를 33에서 34로 변경하겠다. 하지만 제대로 변경되지 않았고 모든 코드를 옮겨야 하는 상황같아보인다. 우선 해본 뒤 Clean Project, Rebuild Project를 수행하였다. 하지만 앱이 실행되었다가 바로 종료되었고, AnimatedContentScope관련 아래와 같은 오류가 발생하였다. 의존성 문제... 오픈소스를 사용한 대가인 듯 하다.
java.lang.ClassCastException: androidx.compose.animation.AnimatedContentTransitionScopeImpl cannot be cast to androidx.compose.animation.AnimatedContentScope
아까 dsk 33버전으로 다시하라 했었으니 새 프로젝트를 생성해보자.
res.drawable, res.values, lib.nemonic, gradle까지 했는데 이 단계에서
Manifest merger failed : Attribute application@theme value=(@style/Theme.Instagram_clone_try4) from AndroidManifest.xml:13:9-58
is also present at [nemonic.aar] AndroidManifest.xml:18:9-40 value=(@style/AppTheme).
Suggestion: add 'tools:replace="android:theme"' to <application> element at AndroidManifest.xml:5:5-26:19 to override.
가 발생하여 AndroidMenifest에 추가해주자. 이전에도 본 내용 같다. 그러자 아래의 오류가 발생하였다. https://velog.io/@kados22/Solved-Theme.AppCompat.Light.NoActionBar-not-found 을 참고하여 디펜던시를 추가해주자.
Android resource linking failed
error: resource style/Theme.AppCompat.Light.DarkActionBar (aka com.example.instagram_clone_try4:style/Theme.AppCompat.Light.DarkActionBar) not found.
com.example.instagram_clone_try4.app-mergeDebugResources-63:/values/values.xml:515: error: style attribute 'attr/colorPrimary (aka com.example.instagram_clone_try4:attr/colorPrimary)' not found.
com.example.instagram_clone_try4.app-mergeDebugResources-63:/values/values.xml:516: error: style attribute 'attr/colorPrimaryDark (aka com.example.instagram_clone_try4:attr/colorPrimaryDark)' not found.
com.example.instagram_clone_try4.app-mergeDebugResources-63:/values/values.xml:517: error: style attribute 'attr/colorAccent (aka com.example.instagram_clone_try4:attr/colorAccent)' not found.
error: failed linking references.
우리 갓 지피티 형님께서 추가하라한 implementation 'androidx.appcompat:appcompat:1.6.1' 로 해결되었다.
그리고 모든 파일을 옮기고 이름 변경을 모두 마친 후 첫 실행...
패키지 이름 오류 LikeButton.kt Material3로 전부 변경
java.lang.IllegalStateException: Hilt Activity must be attached to an @HiltAndroidApp Application. Did you forget to specify your Application's class name in your manifest's <application />'s android:name attribute?
반가운 오류로 AndroidMenifest.xml 수정, 인식못해서 Clean Project Rebuild Project후 실행
와우 같은 오류가 뜬다.. 그래도 성공적으로 버전업을 진행한 것이기에 축배를 들고 화장실 다녀와서 욕심 버리고 디버깅해보자.
Process: com.example.instagram_clone_try4, PID: 27951
java.lang.ClassCastException: androidx.compose.animation.AnimatedContentTransitionScopeImpl cannot be cast to androidx.compose.animation.AnimatedContentScope
at com.google.accompanist.navigation.animation.AnimatedNavHostKt$AnimatedNavHost$7$1.invoke(AnimatedNavHost.kt:198)
at androidx.compose.animation.AnimatedContentKt.AnimatedContent(AnimatedContent.kt:761)
at com.google.accompanist.navigation.animation.AnimatedNavHostKt.AnimatedNavHost(AnimatedNavHost.kt:196)
관점을 바꾸어보자. 이것은 의존성 문제가 아니다. 정말 마지막 지푸라기를 잡는 심정으로, GPT에게 전체 오류메시지를 물어보았다. 여러가지 해결책 중 gradle을 건드리는 방법, API사용법을 검토하라는 말(이미 오픈소스고 잘 작동하는거 확인했는데..) 그런데 Query에 제발을 붙여서일까 답변이 하나 추가되었다.
이를 시도해보기 위해 공식 github release에서 가장 안정적인 버전인 v0.34.0을 찾아내었고 https://github.com/google/accompanist/releases 시도해보았다.
된건 된거고 홈화면에 툴바가 켭쳐보이는 에러가 있기에 간단히만 정리하겠다.
AnimatedContentTransitionScopeImpl cannot be cast to androidx.compose.animation.AnimatedContentScope
에러는 Jetpack Compose와 Animated Navigation을 위해 사용하는 com.google.accompanist 버전이 호환jetpack compose와 호환되지 않는 버전을 사용하기에 발생하는 에러이다. 위에 언급한 accompanish공식 github release에서 안정적인 최신 버전으로 gradle에서 업데이트한다면 에러가 해결된다. 추후 시간이 되면 accompanish의 역활도 찾아봐보자.
확인해보니 Toolbar의 Icon부분의 Modifier.Icon부분을 버전문제로 생략해버렸다. 이를 버전에 맞추어 추가해주자. 그럼에도 해결되지 않았다.
@ExperimentalFoundationApi를 추가해도 해결되지 않았다.
Divider() 를 최신버전인 HorizontalDivider()로 바꾸어도 해결되지 않앗다.
퇴근시간이라 갓 gpt에게 물어보니 Scaffold에 contentPadding을 추가하여 Toolbar와 LazyColumn이 겹치지 않게 해준다고 한다. 이는 자동으로 여백을 추가해주는 옵션이다. 다만 버전차이로 Scaffold에 padding option이 없어 LazyColumns에 옵션으로 추가하여 해결되었다.
gg!