kotlin 1주차 (24.09.04)

최지웅·2024년 9월 4일
0

PIXELRO

목록 보기
2/7
  1. 어제 발생한 부분부터 다시 디버깅을 해볼 예정이다. 어제 퇴근 직전에 cache flush가 완료되어 실행시켜보았는데도 문제가 해결되지 않았었다. 오늘 시작할 문제는 아래와 같다.
  • 우선 새로운 프로젝트를 만들고 build.gradle의 세팅만 가져왔기에 문제의 원인이 gradle 파일에 있다고 생각하였다. 그리고 gradle파일을 그대로 가져온 나머지 build.gradle파일에 android 속성 namespace를 비롯한 여러 항목의 프로젝트 이름이 현재 프로젝트가 아닌 회사 다른 프로젝트의 이름으로 되어있음을 발견할 수 있었다. 이를 수정해주자. 오류가 정상적으로 해결되었다.
  1. try2의 모든 파일들을 옮긴 후 try3을 실행해보았는데 아래와 같은 오류가 발생하였다.
    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 's android:name attribute?

  • 위 오류 메시지는 Hilt 라이브러리 세팅 기본조건 4가지 중 @HiltAndroidApp이 없다는 내용이었다. 사실 4가지 조건이어서 InstaApplication 클래스를 해당 어노테이션을 추가하여 만들어두긴했는데, 어떻게 연결되는지 제대로 이해하지도, 연결하지도 않고 만들어만 둔 탓이다. 디버거 내용처럼 manifest파일을 수정해주자. 성공적으로 정상실행됨을 볼 수 있었다.

  1. 앱 개발하며 정상적으로 실행되다가 아래와 같은 오류가 발생하였다. 그리고 안드로이드 스튜디오와 기기의 연결이 끊겼다.
  • 안드로이드 스튜디오 Trobleshoot device connection issues 기능을 이용하여 ADB(Android Debug Bridge) server를 Restart하였다. 그 후 정상적으로 실행되었다. 아마 잦은 재실행으로 인해 둘의 연결이 꼬인 듯 하다.
  1. 로그인 기능을 구현하기 위해 SignInViewModel과 InstaApp, InstaViewModel, SignInRepository, Constants등을 만들어 실행을 했는데 오류가 발생하였다.

java.lang.IllegalArgumentException: navigation destination Splash is not a direct child of this NavGraph

  • 문제는 InstaApp에서 AnimatedNavHost()인자로 navController를 사용할 때 발생하는 것 같다. 우선 이에 대해 공부를 해보자.

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

자 점심도 먹었겠다 이미 베이스 공부는 진행했기에 두뇌를 초기화한 다음에 문제를 해결해보자.

  • 실마리를 찾았다. 현재 SignInScreen에 적용한 composable route설정이 첫 화면이 아닌 다른 화면에서 다른 화면으로 넘어가는 방법의 Navigation을 사용한 것 같다. 발견은 참조한 코드에 별도의 첫 시작 화면이 존재함에서 알아차릴 수 있었다. 로그인 화면을 첫 화면으로 설정해주..기보단 나도 첫 화면을 별도로 만들어서 코드의 수정을 최소화하자..! 를 하려했는데 확인해보니 참조코드의 첫 화면파일이 실제 첫 화면 파일이 아니었다.. 진짜 첫 화면을 찾아보자. 찾았지만 코드에서 크게 다를 게 없었다.

코드를 기반으로 이해해보니 화면 이동이 필요한 화면인 경우 인자로 해당 화면으로 이동하는 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){}

로 바꾸어 문제를 해결하였다. 두 방식의 차이는 추후 사수분에게 물어봐야겠다.

  1. 하지만 로그인 버튼을 눌러도 다음 화면으로 넘어가지 않았다.
  • 이는 GPT의 도움을 받아 해결하였는데, 현재의 InstaApp은 이전 단계에서의 공부를 통해 AnimatedNavHost와 composable들을 정상적으로 작성한 상태였다. SignInScreen에서 인자를 보낼 때 navController.navigate(Constants.ROUTEHOME)을 같이 전달하여 해당 화면에서 특정 이벤트가 발생했을 때 콜백으로 이 인자를 실행해주면 navigation을 통해 이동할 수 있는 것이다. 문제는 여기서 발생되었었는데, SignInScreen내의 로그인 버튼 SignIn onClick 콜백 이벤트로 updateIsSignIn을 내부 변수에 저장만하고 호출하지 않았다는 것을 발견할 수 있었다. 이를 명시적으로 호출해줌으로써 다음 화면으로 넘어갈 수 있었다. 추가적으로, 참고한 코드에서는 직접적인 호출이 아예 없고 ViewModel에 넘기는데 이 부분에서 어떻게 다음 화면으로 navigation되는건지는 사수분께 물어봐야겠다. 아하! SignInViewModel에서 서버 연결을 확인한 뒤에 아래의 코드로 반환을 한다. Screen에서만 navigation을 사용할 수 있는 게 아니라 ViewModel에서도 사용할 수 있음을 새로이 알게되는 순간이다.
withContext(Dispatchers.Main){
	updateIsSignedIn(true)
}

최대한 샘플코드를 그대로 구현하기 위해, SignInViewModel의 signIn function에서 호출되도록 코드를 보았다. 코드의 시작을 병렬로 진행하기 위해 viewModelScope.launch(Dispatchers.IO)로 감쌌는데, 내 코드에서는 로그인 성공을 가정했을 때 updateIsSignedIn(true)를 바로 호출했다. 비동기적인 상황에서는 withContext(Dispatchers.Main)으로 감싼 뒤에 특정 작업을 수행해야하나보다. 이를 추가해주니 정상적으로 SignInScreen의 onClick콜백에서 SignInViewModel을 거쳐 updateIsSignedIn(true)를 통해 화면 전환이 성공적으로 진행되었다.

  1. 참조코드가 인스타 Post클래스 data class로 되어있는데 이는 무엇인가? ( https://github.com/mshivam019/CC-Instagram )
  1. 샘플코드의 PostRepository에서 suspend fun이 등장하는데 이는 무슨역활인가?
  • 코루틴은 언제든지 일시 중단하고, 스레드를 양보할 수 있는 특성이 있다. 이 때 일시 중단을 수행할 수도 있는 함수를 suspend fun으로 선언하다. 이는 함수 내에 일시 중단 지점을 포함할 수 있는 특별한 함수로 delay같은 함수를 사용할 수 있다.
    https://kotlinworld.com/144
  1. 샘플코드를 참고하여 스토리 리스트를 만들던 도중, StoryImage 스타일을 지정하는 StoryUI.kt에서 모듈 추가설치가 필요해보인다.
  • coil 공식 github를 통해 설치하였다.
implementation("io.coil-kt:coil-compose:2.7.0")

https://coil-kt.github.io/coil/

  1. 오픈소스를 참조하여 정말 많은 디자인적 요소(홈)을 구성하고, 실행시키니 아래의 오류가 발생하였다.
    28 issues were found when checking AAR metadata:

  2. 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)
  • 그러다 기똥찬 생각이 들었다. 버전이 문제라면 Clone 프로젝트의 gradle을 가져오면 되는게 아닐까? 물론 전부가져오면 또다른 의존성 문제가 생기기에 현재의 프로젝트를 다시한번 실행시켜보았다.
    androidx.compose.animation에서 문제가 발생하고 있었고, compose관련 라이브러리들을 하나씩 옮겨보자. 문제는 역시 해결되지 않았다. 시도해볼만한 걸로는 아예 프로젝트 버전을 오픈소스와 동일하게 맞추는 것이지만, 아마 전전 프로젝트가 같은 버전이었을 것이다.

관점을 바꾸어보자. 이것은 의존성 문제가 아니다. 정말 마지막 지푸라기를 잡는 심정으로, 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의 역활도 찾아봐보자.

  1. 인스타 클론 상단 툴바 겹침오류
  • 확인해보니 Toolbar의 Icon부분의 Modifier.Icon부분을 버전문제로 생략해버렸다. 이를 버전에 맞추어 추가해주자. 그럼에도 해결되지 않았다.

  • @ExperimentalFoundationApi를 추가해도 해결되지 않았다.

  • Divider() 를 최신버전인 HorizontalDivider()로 바꾸어도 해결되지 않앗다.

  • 퇴근시간이라 갓 gpt에게 물어보니 Scaffold에 contentPadding을 추가하여 Toolbar와 LazyColumn이 겹치지 않게 해준다고 한다. 이는 자동으로 여백을 추가해주는 옵션이다. 다만 버전차이로 Scaffold에 padding option이 없어 LazyColumns에 옵션으로 추가하여 해결되었다.

gg!

profile
이제 3학년..

0개의 댓글