상태 기반 UI 구조 이해
- Compose는 상태 기반 UI
- 값(state) 변경 → UI도 같이 변함(업데이트)
- recomposition : 자동으로 UI 로드
mutableStateOf(value): 상태값을 생성하는 함수remember {...}- 컴포저블 함수 재구성 시 상태를 유지
- 람다 결과를 캐싱 → 컴포저블 상태를 저장
val variable = remenber { mutableStateOf(<T>) }variable.value를 통해 값을 읽고 쓸 수 있음variable.value변경 시 자동으로 UI recompositeby와의 차이점 :by사용 시value만 가짐, mutableStateOf 객체의 경우 객체로 사용
@Composable
fun CountEx(){
val count = remember { mutableStateOf(0) }
// val countBy by remember { mutableStateOf(0) }
Column {
Text("count: ${count.value}")
Button(onClick = { count.value++ }) {
Text("+1 증가")
}
// Text("count: ${countBy}")
// Button(onClick = { countBy++ }) {
// Text("+1 증가")
// }
}
}
Navigation Compose
- 라우팅(url) 기반
- SAA(Sing Activity Architecture)
- 여러 화면 전환
- 네비게이션 그래프(NavGraph) 기반으로 전환/이동할 수 있게 해주는 라이브러리
- 기존 XML의 FragementManager 또는 Intent 역할 → NavController + NavHost

- 의존성 필요(Gradle)
- project build.gradle이 아닌 module build.gradle
// build.gradle.kts (:app)
dependencies {
val nav_version = "2.9.3"
implementation("androidx.navigation:navigation-compose:${nav_version}")
}
- 기타 dependencies
plugins {
// Kotlin serialization plugin for type safe routes and navigation arguments
kotlin("plugin.serialization") version "2.0.21"
}
dependencies {
val nav_version = "2.9.3"
// Jetpack Compose integration
implementation("androidx.navigation:navigation-compose:$nav_version")
// Views/Fragments integration
implementation("androidx.navigation:navigation-fragment:$nav_version")
implementation("androidx.navigation:navigation-ui:$nav_version")
// Feature module support for Fragments
implementation("androidx.navigation:navigation-dynamic-features-fragment:$nav_version")
// Testing Navigation
androidTestImplementation("androidx.navigation:navigation-testing:$nav_version")
// JSON serialization library, works with the Kotlin serialization plugin
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
}
- 구성 요소
| 구성 요소 | 설명 | 역할 / 특징 |
|---|---|---|
| NavController | 네비게이션을 제어하는 핵심 객체 | |
| 화면 이동 담당 | - 현재 화면 상태 추적- navigate(), popBackStack() 등으로 화면 전환 관리 |
|
| NavHost | 네비게이션 그래프를 표시하는 컨테이너 | |
| 화면의 출발점 | - NavController와 연결- 어떤 Composable 화면이 보여질지 결정 |
|
| NavGraph | 화면 간 이동 경로와 구조 정의 | |
| route | - 화면들의 관계, 시작 지점(startDestination) 정의 | |
| Composable Destination | 실제로 표시되는 화면(Composable 함수) | - composable("routeName") { ... } 형태로 정의- 하나의 화면을 네비게이션 목적지로 등록 |
| Route (경로) | 각 화면을 식별하는 고유 문자열 | - composable("home") 처럼 지정- 이동 시 navController.navigate("home") 로 호출 |
| Arguments | 화면 간 데이터 전달 요소 | - navArgument 를 사용- 예: composable("detail/{itemId}") |
| BackStack | 이동 기록을 저장하는 스택 구조 | - popBackStack() 호출 시 이전 화면으로 돌아감 |
| rememberNavController() | Composable 내부에서 NavController 생성 | - 상태를 기억(remember)하여 재구성 시에도 NavController 유지 |
NavHost
시작 base경로를 설정,
이후 이동 상태를 stack 형태로 저장해 실행 관리
NavController와 탐색 그래프를 연결하는 Composable이다.
현재 NavController의 상태에 따른 Composable 대상을 표시
@Composable
fun NavHost(
navController: NavHostController,
graph: NavGraph,
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.TopStart,
enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
fadeIn(animationSpec = tween(700))
},
exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
fadeOut(animationSpec = tween(700))
},
popEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = enterTransition,
popExitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = exitTransition,
sizeTransform: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform?)? = null
): Unit
| Parameters | |
|---|---|
navController: NavHostController |
the navController for this host |
startDestination: String |
the route for the start destination |
modifier: Modifier = Modifier |
The modifier to be applied to the layout. |
contentAlignment: Alignment = Alignment.TopStart |
The Alignment of the AnimatedContent |
route: String? = null |
the route for the graph |
enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = { fadeIn(animationSpec = tween(700)) } |
callback to define enter transitions for destination in this host |
exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = { fadeOut(animationSpec = tween(700)) } |
callback to define exit transitions for destination in this host |
popEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = enterTransition |
callback to define popEnter transitions for destination in this host |
popExitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = exitTransition |
callback to define popExit transitions for destination in this host |
sizeTransform: (AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform?)? = null |
callback to define the size transform for destinations in this host |
builder: NavGraphBuilder.() -> Unit |
the builder used to construct the graph |
rememberNavController(): navigation stack- NavHostController 객체
- Composable 함수 내에서 사용
- 파라미터 전달을 통해 화면 재구성 시에도 instance 유지
// MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyApplication02Theme {
MyAppNavHost()
}
}
}
}
@Composable
fun MyAppNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
) {
var countMainButton by remember { mutableStateOf(1) }
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
NavHost(
navController = navController,
startDestination = MAIN_SCREEN_ROOT,
) {
composable(MAIN_SCREEN_ROOT) {
GreetingMain(
name = "Android",
modifier = Modifier.padding(innerPadding),
navController = navController,
count = countMainButton,
onIncrementCount = { countMainButton++ },
)
}
composable(SCREEN01_SCREEN_ROOT) {
Screen(navController = navController)
}
}
}
}
// Screen01.kt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Screen(navController: NavHostController) {
Scaffold(modifier = Modifier.fillMaxSize(),
topBar = { TopAppBar(title = { Text("탑바") }) },
floatingActionButton = {
FloatingActionButton(onClick = {}) { Text("+") }
},
bottomBar = {
BottomAppBar {
Text(
"Bottom Bar",
modifier = Modifier.padding(16.dp)
)
}
},
) { innerPadding ->
Surface(
modifier = Modifier.fillMaxSize(0.8f),
contentColor = Color.Red
) {
Greeting(
name = "한글",
modifier = Modifier.padding(innerPadding),
navController = navController
)
}
}
}
화면 간 데이터 전달
/이용composable("tagetNav/{data}") {val userName = ...}- 가능은 하나 보안에 취약(인코딩 등의 과정 필요)
// MyAppNavHost
...
NavHost(
navController = navController,
startDestination = MAIN_SCREEN_ROOT,
) {
composable("$MAIN_SCREEN_ROOT") { backStackEntry ->
GreetingMain(
name = "",
modifier = Modifier.padding(innerPadding),
navController = navController,
count = countMainButton,
onIncrementCount = { countMainButton++ },
)
}
composable("$MAIN_SCREEN_ROOT/{value}") { backStackEntry ->
val value = backStackEntry.arguments?.getString("value") ?: ""
GreetingMain(
name = value,
modifier = Modifier.padding(innerPadding),
navController = navController,
count = countMainButton,
onIncrementCount = { countMainButton++ },
)
}
...
...
// Greeting02(Screen02)
...
Button(onClick = { navController.navigate("$MAIN_SCREEN_ROOT/${value.value}") }) {
Text("홈으로 데이터 전송")
}
...


C
Contents
