공부 할 겸 TFT API를 이용하여 전적 검색하는 앱을 만들어볼려고 한다.
우선 https://developer.riotgames.com 에 접속하여 로그인을 프로젝트를 등록해야되는데 앱 이름과 앱 설명을 한 뒤 만들면 되는데 어떤 서비스를 할 것 인지 물어본다. 롤, TFT, 룬테라가 있는데 나는 롤만 해봤고 롤체는 안해본 터라 TFT의 풀네임을 모르고 lol에 포함되어있겠지 해서 리그오브레전드를 골랐는데 알고보니 TFT는 Teamfight Tactics의 약자였던 것이다. 난 처음 들어본 이름이라 몰랐었다.. 그래서 등록했던 프로젝트를 삭제 후 TFT를 제대로 골라서 등록했는데 LOL과 다르게 바로 승인을 안해주고 펜딩 상태가 되었다..
그래서 일단 api먼저 사용하기 전에 초기 설정부터 해주자라고 생각해서 우선 앱을 만들기로 했다.
뭐.. 당연하게 100% Compose를 이용할 생각이다. xml을 굳이 사용할 필요성을 못느꼈기 때문이다.
기존에 클린 아키텍쳐를 공부하고 있었는데, 안드로이드에서 직접 소개한 아키텍쳐가 있는데 그게 앱 아키텍쳐이다. 클린 아키텍쳐와 다르게 도메인레이어가 옵셔널이라는 점이다. 그래서 내가 만들 앱이 그리 크지 않는 점을 고려해 도메인레이어를 빼고 앱 아키텍쳐를 사용해서 구현할 생각이다.
기존에도 MVI를 사용해봤지만 누군가 이미 만든 구조를 그대로 써왔던터라 깊게 이해하고 쓰진 못했던 것 같아서 이번 기회에 내가 직접 구조를 설계하면서 사용해보고 싶어서 MVI를 사용하기로 했었다.
원래 Jetpack Navigation을 주로 사용해봤다. 그래서 이번에 Alpha 단계인
Navigation3 (개발자 문서)을 사용해볼려고 한다.
내가 만들 앱이 아직 초기 설정 단계라 화면 이동 및 삭제 그리고 초기 구현로직 정도 이다. 자세한 내용은 개발자 문서를 참고하면 된다.
의존성 추가는 공식문서에 나온대로 모두 적용하면 된다.
androidx.compose.material3.adaptive:adaptive-navigation3
근데 이 의존성을 추가할려면 maven에 추가도 필요해서 추가해주었다.
// settings.gradle.kts
pluginManagement {
repositories {
google()
gradlePluginPortal()
mavenCentral()
maven {
// You can find the maven URL for other artifacts (e.g. KMP, METALAVA) on their
// build pages.
url = uri("https://androidx.dev/snapshots/builds/[buildId]/artifacts/repository")
}
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://androidx.dev/snapshots/builds/[buildId]/artifacts/repository")
}
}
}
buildId는 25.09.15 기준 14101258이 최신이여서 이걸로 적용하였다.
기존 navigation은 backStack을 직접 관리하진 않았다. 그래서 자유롭게 다룬적이 없는 것 같다. 하지만 이번에 Navigation3에서는 backStack을 직접 키로 관리하여 이동 및 뒤로가기를 한다.
// Route.kt
sealed interface Route {
@Serializable
data object Main : Route
@Serializable
data class Second(
val data: String
) : Route
}
// MainActivity.kt#onCreate
// 초기 화면을 Main으로 설정
setContent {
val backStack = remember { mutableStateListOf<Route>(Route.Main) }
...
우선 시작화면으로 초기값을 설정한 뒤 리스트 형태로 만들어주면 된다.
backStack.add(Route.Second("바보")) // Second 화면으로 이동
backStack.removeLastOrNull // 뒤로가기
이제 이동 및 뒤로가기는 해당 리스트를 이용하면 된다.
아직 제대로 깊게 사용하진 않았지만 직관적이면서 정말 간단한 것 같다.
기존과 다른점은 NavHost말고 NavDisplay를 구현하면 된다.
NavDisplay 역시 DSL로도 지원을 해준다. 그래서 나는 DSL를 사용하여 구현해봤다.
(DSL 사용 안한 예시코드는 개발자 문서에 있다.)
// TFTLogNavDisplay.kt
@Composable
fun TFTLogNavDisplay(
modifier: Modifier = Modifier,
backStack: List<Route>,
backStackAdd: (Route) -> Unit,
backStackRemove: () -> Unit
) {
NavDisplay(
backStack = backStack,
onBack = { backStackRemove() },
entryProvider = entryProvider {
entry<Route.Main> {
MainScreen(
modifier = modifier,
onNavigate = backStackAdd
)
}
entry<Route.Second> { key ->
SecondScreen(
modifier = modifier,
data = key.data
)
}
}
)
}
나는 backStack과 화면 이동을 최상위인 MainActivity에서 관리하게 하였다.
onBack의 프로퍼티는 뒤로가기 트리거 할 때 람다형태로 호출된다.
entryProvider는 backStack을 NavEntry로 변환해준뒤 현재 스택에 맞는 화면을 보여줄 수 있게 해준다. 나머지는 NavHost랑 비슷하다. data가 있는 경우에는 key를 이용하여 값을 가져올 수 있다.
아직 초기 설정 단계라 깊게 써본건 아니지만 매우 직관적이고 또 쉽게 구현 할 수 있었다. 좀 더 활용할 수 있는 방법을 찾아보고 싶다.