
이전 글에서는 Scaffold를 활용해 앱의 기본 골격을 만들고, TopAppBar, BottomAppBar, NavigationBar 같은 컴포넌트를 조합해 하나의 화면을 구성해보았습니다.
하지만 실제 앱은 단일 화면으로 끝나지 않습니다. 로그인 → 메인화면 → 상세화면 → 프로필 등 여러 화면 간의 이동이 필요하죠. 이번 글에서는 Jetpack Compose Navigation을 통해 여러 화면을 연결하고, 사용자의 내비게이션 흐름을 제어하는 방법을 배워보겠습니다.
기존 View 시스템에서는 Activity나 Fragment를 사용해 화면을 구성하고, Intent나 FragmentTransaction으로 화면 전환을 처리했습니다. Jetpack Compose에서는 이 모든 과정을 더 선언적이고 직관적으로 처리할 수 있는 Navigation 라이브러리를 제공합니다.
✅ 기존 View 시스템 vs Compose Navigation
| 구분 | 기존 View 시스템 | Jetpack Compose Navigation |
|---|---|---|
| 화면 단위 | Activity, Fragment | @Composable 함수 |
| 화면 전환 | Intent, FragmentTransaction | NavController.navigate() |
| 데이터 전달 | Intent extra, Bundle | Navigation arguments |
| 백스택 관리 | FragmentManager, TaskStack | NavController 자동 관리 |
Compose Navigation의 가장 큰 장점은 모든 화면이 단순한 컴포저블 함수라는 점입니다. 복잡한 생명주기나 Fragment 트랜잭션을 고려할 필요 없이, 함수 호출하듯 자연스럽게 화면을 전환할 수 있습니다.
Jetpack Compose Navigation은 세 가지 핵심 구성 요소로 이루어져 있습니다.
NavController
화면 이동을 제어하는 컨트롤러입니다. 현재 어떤 화면이 표시되고 있는지 추적하고, 새로운 화면으로의 이동이나 뒤로가기를 담당합니다.
val navController = rememberNavController()
NavHost
실제 화면들이 표시되는 컨테이너입니다. NavController와 연결되어, 현재 경로(Route)에 해당하는 컴포저블을 화면에 보여줍니다.
NavHost(
navController = navController,
startDestination = "home"
) {
// 화면 정의
}
Destination
각각의 화면을 의미하며, 고유한 경로를 가집니다. NavHost 내부에서 composable 함수로 정의됩니다.
Destination은 NavHost 안에 정의되는 각각의 화면(컴포저블 함수)을 의미하며, 고유한 Route(경로)로 식별됩니다.
composable("home") { HomeScreen() }에서 "home"이 Route이고, HomeScreen()이 해당 Destination입니다.composable("home") { HomeScreen() }
composable("profile") { ProfileScreen() }
⚠️ 앞으로는 Route를 '경로' 또는 '라우트'라고도 표현할 예정입니다. 모두 같은 의미로 이해하시면 됩니다.
Navigation을 사용하기 위해서는 먼저 의존성을 추가해야 합니다.
build.gradle(:app) 파일에 Navigation Compose 의존성을 추가합니다.
dependencies {
implementation ("androidx.navigation:navigation-compose:2.9.0")
// 다른 의존성들...
}
📌 버전은 프로젝트 생성 시점에 따라 달라질 수 있으니, 공식 문서에서 최신 버전을 확인하세요.
가장 간단한 두 화면 간의 이동을 구현해보겠습니다.

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.composebeginnerguide.ui.theme.ComposeBeginnerGuideTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBeginnerGuideTheme {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home",
) {
composable("home") {
HomeScreen(navController = navController)
}
composable("profile") {
ProfileScreen(navController = navController)
}
}
}
}
}
}
@Composable
fun HomeScreen(navController: NavController) {
Scaffold { innerPadding ->
Column(
modifier =
Modifier
.fillMaxSize()
.padding(innerPadding),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = "홈 화면",
fontSize = 24.sp,
modifier = Modifier.padding(bottom = 16.dp),
)
Button(
onClick = { navController.navigate("profile") },
) {
Text("프로필로 이동")
}
}
}
}
@Composable
fun ProfileScreen(navController: NavController) {
Scaffold { innerPadding ->
Column(
modifier =
Modifier
.fillMaxSize()
.padding(innerPadding),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = "프로필 화면",
fontSize = 24.sp,
modifier = Modifier.padding(bottom = 16.dp),
)
Button(
onClick = { navController.popBackStack() },
) {
Text("뒤로가기")
}
}
}
}
📌 학습 포인트
navigate()로 이동, popBackStack()으로 뒤로가기를 구현합니다.popBackStack()대신 navigateUp()을 사용할 수 있습니다. 하지만, 초심자 가이드에서는 popBackStack()만을 다루려고 합니다.NavHost 설정
startDestination = "home": 앱이 시작될 때 처음 보여줄 화면을 지정composable("home"): "home" 경로에 해당하는 화면을 정의화면 이동
navController.navigate("profile"): "profile" 경로로 이동navController.popBackStack(): 백스택에서 현재 화면을 제거하고 이전 화면으로 돌아감NavController 전달
실제 앱에서는 화면 이동 시 데이터를 함께 전달해야 하는 경우가 많습니다. 예를 들어 사용자 목록에서 특정 사용자를 선택했을 때, 그 사용자의 ID나 정보를 상세 화면으로 전달해야 합니다.
URL 경로처럼 Route(경로)에 파라미터를 포함시켜 데이터를 전달할 수 있습니다.

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBeginnerGuideTheme {
// navController를 포함한 Composable함수를 호출하는 방식을 사용할 수 있습니다.
// 이후부터는 MainActivity의 코드를 제외하고 작성할 예정입니다.
// 이 부분에 가장 최상위 컴포저블 함수를 넣으시면 됩니다.
NavigationWithDataExample()
}
}
}
}
@Composable
fun NavigationWithDataExample() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "user_list"
) {
composable("user_list") {
UserListScreen(navController = navController)
}
composable("user_detail/{userId}") { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId") ?: ""
UserDetailScreen(
userId = userId,
navController = navController
)
}
}
}
@Composable
fun UserListScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp)
) {
Text("사용자 목록", fontSize = 20.sp)
// 예시 사용자들
Button(
onClick = { navController.navigate("user_detail/123") },
modifier = Modifier.padding(vertical = 4.dp)
) {
Text("김철수 (ID: 123)")
}
Button(
onClick = { navController.navigate("user_detail/456") },
modifier = Modifier.padding(vertical = 4.dp)
) {
Text("이영희 (ID: 456)")
}
Button(
onClick = { navController.navigate("user_detail/789") },
modifier = Modifier.padding(vertical = 4.dp)
) {
Text("박민수 (ID: 789)")
}
}
}
@Composable
fun UserDetailScreen(
userId: String,
navController: NavController
) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "사용자 상세 정보",
fontSize = 20.sp,
modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = "사용자 ID: $userId",
fontSize = 16.sp,
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = "여기에 $userId 사용자의 상세 정보가 표시됩니다.",
modifier = Modifier.padding(bottom = 16.dp)
)
Button(
onClick = { navController.popBackStack() }
) {
Text("목록으로 돌아가기")
}
}
}
파라미터가 없어도 동작하도록 하거나, 기본값을 설정할 수 있습니다.
예를 들어, 사용자 상세 화면(UserDetailScreen)을 "보기(view)" 모드 또는 "편집(edit)" 모드로 나누어 표시하고 싶다고 해봅시다.
이때 mode라는 선택적 파라미터를 설정하면, 전달 여부에 따라 화면의 동작을 유연하게 제어할 수 있습니다.
composable(
route = "user_detail/{userId}?mode={mode}",
arguments = listOf(
navArgument("userId") { type = NavType.StringType },
navArgument("mode") {
type = NavType.StringType
defaultValue = "view"
}
)
) { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId") ?: ""
val mode = backStackEntry.arguments?.getString("mode") ?: "view"
UserDetailScreen(
userId = userId,
mode = mode,
navController = navController
)
}
이 코드는 다음과 같이 사용할 수 있습니다.
navController.navigate("user_detail/123") // mode의 기본값은 "info"
navController.navigate("user_detail/123?mode=edit") // mode를 "edit"으로 설정
📌 여기서 핵심은 ?mode={mode} 구문입니다.
mode는 선택적 파라미터로, 경로에서 생략 가능하며, 생략 시에는 navArgument에서 정의한 defaultValue가 자동으로 적용됩니다.UserListScreen에서는 단순히 userId만 전달해도, UserDetailScreen은 mode를 인식하고 다르게 동작할 수 있습니다.많은 앱에서는 하단 탭 메뉴를 통해 주요 화면 간을 빠르게 오갈 수 있도록 구성합니다. 예를 들어 인스타그램은 "홈 / 검색 / 릴스 / 알림 / 프로필", 쿠팡은 "홈 / 로켓배송 / 장바구니 / 마이쿠팡"처럼 항상 화면 하단에 탭이 고정되어 있죠.
이런 UI를 구현할 때 Jetpack Compose에서는 NavigationBar를 활용할 수 있습니다. 이번에는 NavigationBar와 Compose Navigation을 연동해서, 탭을 누를 때마다 실제 화면이 전환되는 구조를 만들어 보겠습니다.

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.navigation.NavController
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.currentBackStackEntryAsState
data class BottomNavItem(
val route: String,
val title: String,
val icon: ImageVector
)
@Composable
fun MainScreenWithBottomNav() {
val navController = rememberNavController()
val bottomNavItems = listOf(
BottomNavItem("home", "홈", Icons.Default.Home),
BottomNavItem("search", "검색", Icons.Default.Search),
BottomNavItem("favorites", "즐겨찾기", Icons.Default.Favorite),
BottomNavItem("profile", "프로필", Icons.Default.Person)
)
Scaffold(
bottomBar = {
NavigationBar {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
bottomNavItems.forEach { item ->
NavigationBarItem(
icon = {
Icon(item.icon, contentDescription = item.title)
},
label = { Text(item.title) },
selected = currentDestination?.hierarchy?.any {
it.route == item.route
} == true,
onClick = {
navController.navigate(item.route) {
// 백스택을 정리해서 동일한 화면이 여러 번 쌓이지 않도록 함
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// 동일한 탭을 다시 눌렀을 때 중복 화면이 생기지 않도록 함
launchSingleTop = true
// 탭을 바꿨다가 다시 돌아왔을 때 이전 상태를 복원
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(
navController = navController,
startDestination = "home",
modifier = Modifier.padding(innerPadding)
) {
composable("home") {
HomeTabScreen(navController)
}
composable("search") {
SearchTabScreen(navController)
}
composable("favorites") {
FavoritesTabScreen(navController)
}
composable("profile") {
ProfileTabScreen(navController)
}
}
}
}
@Composable
fun HomeTabScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("홈 탭", fontSize = 24.sp)
Button(
modifier = Modifier.padding(top = 16.dp)
) {
Text("사용자 상세로 이동")
}
}
}
@Composable
fun SearchTabScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("검색 탭", fontSize = 24.sp)
Text("여기에서 검색 기능을 구현할 수 있습니다.")
}
}
@Composable
fun FavoritesTabScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("즐겨찾기 탭", fontSize = 24.sp)
Text("즐겨찾기한 항목들이 표시됩니다.")
}
}
@Composable
fun ProfileTabScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("프로필 탭", fontSize = 24.sp)
Text("사용자 프로필 정보가 표시됩니다.")
}
}
BottomNavItem 데이터 클래스
currentBackStackEntryAsState()
navigate 옵션들
popUpTo: 지정된 화면까지 백스택을 정리합니다launchSingleTop: 동일한 화면이 백스택에 중복으로 쌓이는 것을 방지합니다restoreState/saveState: 탭 전환 시 각 탭의 상태를 저장하고 복원합니다📌 네비게이션 옵션이 중요한 이유
이 옵션들이 없으면 탭을 여러 번 누를 때마다 동일한 화면이 백스택에 계속 쌓여서, 뒤로가기 버튼을 여러 번 눌러야 앱이 종료되는 문제가 발생할 수 있습니다.
앱을 사용하다 보면 뒤로가기 버튼을 눌렀을 때 특정 화면으로 돌아가거나, 특정 조건에서는 이전 화면들을 모두 제거하고 새로운 화면으로 이동해야 하는 경우가 있습니다.
Jetpack Compose Navigation에서는 navigate() 함수에 다양한 옵션을 조합해 백스택의 상태를 제어할 수 있습니다. 자주 쓰이는 5가지 패턴을 확인해보고, 어떻게 사용할 수 있는지 확인해봅시다!
1. 기본 이동 (백스택에 쌓임)
첫 화면은
"screen_a"라고 가정합니다.
navController.navigate("screen_b")
[screen_a, screen_b]2. 현재 화면을 백스택에서 제거하고 이동
navController.navigate("screen_b") {
popUpTo("screen_a") { inclusive = true }
}
screen_a까지 백스택을 정리하고, screen_b로 이동합니다.inclusive = true: screen_a도 제거됩니다.[screen_b]3. 특정 화면까지 백스택 정리하고 이동
navController.navigate("screen_c") {
popUpTo("screen_a") { inclusive = false }
}
screen_a는 남기고, 그 위의 화면들을 모두 제거한 뒤 screen_c로 이동합니다.inclusive = false: screen_a는 보존됩니다.[screen_a, screen_c]4. 동일한 화면 중복 방지
navController.navigate("screen_b") {
launchSingleTop = true
}
screen_b일 경우, 새 인스턴스를 쌓지 않고 기존 것을 재사용합니다.[screen_a, screen_b] → 다시 navigate("screen_b") 해도 유지5. 백스택을 모두 정리하고 새로운 플로우 시작
navController.navigate("main") {
popUpTo(0) { inclusive = true }
}
[main]📌 주요 옵션 요약
| 옵션 | 설명 | 사용 예시 |
|---|---|---|
popUpTo(route) | 지정된 라우트까지 백스택 정리 | 탭 중복 제거, 로그인 후 흐름 전환 |
inclusive = true | popUpTo 대상 화면도 함께 제거 | 로그인 화면 제거 |
launchSingleTop = true | 동일한 화면 중복 생성 방지 | 탭을 여러 번 눌렀을 때 |
saveState/restoreState | 화면 상태 저장 및 복원 | 탭 전환 시 상태 유지 |
| Case | 설명 | 백스택 상태 |
|---|---|---|
| 기본 이동 | 기존 화면 위에 새 화면 추가 | [A, B] |
popUpTo("A") + inclusive = true | A까지 제거 후 B로 이동 | [B] |
popUpTo("A") + inclusive = false | A 남기고 위 화면 제거 | [A, C] |
launchSingleTop = true | 동일 화면 중복 방지 | [A, B] 유지 |
popUpTo(0) + inclusive = true | 전체 백스택 제거 | [Main] |
로그인 앱에서 자주 사용되는 네비게이션 패턴을 구현해보겠습니다.
@Composable
fun LoginFlowExample() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "splash"
) {
composable("splash") {
SplashScreen(navController)
}
composable("login") {
LoginScreen(navController)
}
composable("signUp") {
SignUpScreen(navController)
}
composable("main") {
MainScreen(navController)
}
}
}
@Composable
fun SplashScreen(navController: NavController) {
// (예시) 2초 이후에 로그인 화면으로 넘어가는 스플래시 화면입니다.
LaunchedEffect(Unit) {
delay(2000)
navController.navigate("login") {
popUpTo("splash") { inclusive = true }
}
}
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("앱 로고", fontSize = 32.sp)
Text("로딩 중...", fontSize = 16.sp)
}
}
@Composable
fun LoginScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("로그인", fontSize = 24.sp, modifier = Modifier.padding(bottom = 32.dp))
Button(
onClick = {
// 로그인 성공 시 이전 화면들을 모두 제거하고 메인으로 이동
navController.navigate("main") {
popUpTo(0) { inclusive = true }
}
},
modifier = Modifier.padding(bottom = 16.dp)
) {
Text("로그인")
}
Button(
onClick = {
navController.navigate("signUp")
}
) {
Text("회원가입")
}
}
}
@Composable
fun SignUpScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("회원가입", fontSize = 24.sp, modifier = Modifier.padding(bottom = 32.dp))
Button(
onClick = {
// 회원가입 성공 시 메인으로 이동
// 로그인/회원가입 화면들은 백스택에서 제거
navController.navigate("main") {
popUpTo(0) { inclusive = true }
}
},
modifier = Modifier.padding(bottom = 16.dp)
) {
Text("가입 완료")
}
Button(
onClick = {
navController.popBackStack()
}
) {
Text("로그인으로 돌아가기")
}
}
}
@Composable
fun MainScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("메인 화면", fontSize = 24.sp, modifier = Modifier.padding(bottom = 32.dp))
Text("로그인 성공!", modifier = Modifier.padding(bottom = 16.dp))
Button(
onClick = {
// 로그아웃 시 다시 로그인 화면으로 이동
navController.navigate("login") {
popUpTo(0) { inclusive = true }
}
}
) {
Text("로그아웃")
}
}
}
Compose Navigation은 선언적이고 유연한 구조를 제공하지만, 몇 가지 자주 발생하는 실수나 주의할 점이 있습니다. 이 섹션에서는 그동안 초심자들이 자주 실수한 패턴과, 더 나은 설계 방법, 그리고 고급 활용 팁을 소개합니다.
NavController 전달 방식
// ❌ 잘못된 방식: 모든 컴포저블에 NavController 전달
@Composable
fun SomeScreen(navController: NavController) { ... }
// ✅ 더 좋은 방식: 필요한 곳에만 람다로 전달
@Composable
fun SomeScreen(onNavigateToDetail: (String) -> Unit) { ... }
NavController는 rememberNavController()로 생성
// ❌ 잘못된 방식: 직접 생성하면 Lifecycle 연결이 안 됨
val navController = remember { NavController(context) }
// ✅ 더 좋은 방식: Navigation 전용 헬퍼를 사용
val navController = rememberNavController()
딥 링크 지원
Navigation Compose는 딥 링크를 지원하여 외부에서 특정 화면으로 바로 이동할 수 있습니다.
composable(
"user_detail/{userId}",
deepLinks = listOf(navDeepLink { uriPattern = "myapp://user/{userId}" })
) { backStackEntry ->
// 딥 링크로 접근 가능한 화면
}
애니메이션 효과
화면 전환시 슬라이드 애니메이션을 적용할 수 있습니다.
// Navigation Animation 의존성 추가 필요
composable(
"screen_name",
enterTransition = { slideInHorizontally(initialOffsetX = { 1000 }) },
exitTransition = { slideOutHorizontally(targetOffsetX = { -1000 }) }
) {
// 화면 내용
}
📌 Navigation 설계 원칙
Jetpack Compose Navigation은 선언적이고 유연한 구조를 제공하지만, 더 유연한 설계를 하기 위해 참고할만한 사항들이 있습니다. 이 섹션에서는 자주 발생하는 실수, 그리고 더 안정적인 Navigation 설계를 위한 원칙을 정리해보았습니다.
NavController 전달 방식 (NavController를 하위 컴포저블에 직접 전달하지 마세요)
// ❌ 덜 유연한 방식: 모든 컴포저블에 NavController 전달
@Composable
fun SomeScreen(navController: NavController) { ... }
// ✅ 더 나은 설계 방식: 필요한 곳에만 람다로 전달
@Composable
fun SomeScreen(onNavigateToDetail: (String) -> Unit) { ... }
NavController는 rememberNavController()로 생성
// ❌ 잘못된 방식: 직접 생성하면 Lifecycle 연결이 안 됨
val navController = remember { NavController(context) }
// ✅ 더 좋은 방식: Navigation 전용 헬퍼를 사용
val navController = rememberNavController()
rememberNavController()는 Lifecycle, BackDispatcher 등 Navigation에 필요한 내부 요소를 자동으로 연결해줍니다.📌 다음은 앱 구조를 깔끔하게 유지하기 위한 Navigation 설계 팁입니다.
이처럼 Navigation 설계에서 초기에 방향을 잘 잡는 것만으로도 뒤에서 발생할 수 있는 버그나 복잡한 흐름을 줄일 수 있습니다. 특히 테스트, 확장, 상태 복원 등을 고려할 때는 설계 원칙을 의식적으로 적용하는 것이 큰 도움이 됩니다.
이번 글에서는 Jetpack Compose Navigation을 활용해 여러 화면 간의 전환을 구현하는 방법을 단계적으로 살펴보았습니다.
📌 주요 학습 내용은 아래와 같습니다.
navigate, popBackStack 등을 이용한 기본 화면 전환 구현Route Parameter를 통한 데이터 전달과 선택적 파라미터 처리 방법popUpTo, launchSingleTop, restoreState 등의 백스택 제어 옵션Jetpack Compose Navigation은 기존 View 기반 Navigation보다 훨씬 선언적이고 구조적이며, 상태 중심의 Compose 철학에 잘 어울리는 방식입니다. 이번 글을 통해 하나의 앱이 어떻게 여러 화면으로 구성되고, 그 흐름을 제어하는지를 체계적으로 익히셨기를 바랍니다.
이번 글에서는 여러 화면을 전환하고 연결하는 방법을 통해, 앱의 기본 구조와 흐름을 설계해보았습니다. 이제 화면 전환을 넘어서 앱 전체에 일관된 스타일과 분위기를 적용하는 방법으로 넘어가보려 합니다.
다음 글에서는 Jetpack Compose에서 테마와 스타일을 정의하고 적용하는 방법을 다뤄보려고 합니다.MaterialTheme을 활용해 색상, 글꼴, 모양 등 디자인 시스템을 구성하고, 다크 모드 대응과 커스텀 테마 설정까지 실습해볼 예정입니다.
앱에 개성과 통일감을 더해줄 스타일링을 알아가보도록 합시다! 기대해주세요! 🚀