본문 바로가기
android

Jetpack Compose로 간편하게 화면 전환 구현하기 – NavController와 NavHost 완벽 가이드

by codingcampus 2025. 5. 13.
반응형
SMALL

Jetpack Compose는 Android의 최신 UI 툴킷으로, 간결하고 선언적인 코드 스타일을 제공합니다. 이번 포스트에서는 초보자도 쉽게 따라 할 수 있는 기본 화면 구성과 화면 전환을 포함한 간단한 예제를 다룹니다.



📁 프로젝트 생성하기

  1. Android Studio를 실행합니다.
  2. New Project를 클릭합니다.
  3. Empty Compose Activity를 선택하고 Next를 클릭합니다.

    4. 프로젝트 이름은 SampleLayoutApp으로 설정합니다.

    5. Minimum SDK는 **API Level 24 (Nougat)**로 설정하고 Finish를 클릭합니다.

 


📦 기본 화면 구성하기

MainActivity.kt

프로젝트를 생성하면 자동으로 생성하는 MainActivity.kt 파일은 다음과 같습니다.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            SampleLayoutAppTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        name = "Android",
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }
}

 

 OnCreGreating()함수에  SampleLayoutTheme 안에 있는 Greeting() 및 컴포저블로 정의된 Greeting 함수를 삭제 합니다.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            SampleLayoutAppTheme {
            //삭제
            // .... 
            }
        }
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

 

그리고 네비게이션 컨트롤러로 화면을 전환하는  MainScreen() 컴포저블 함수를 다음과 같이 정의 합니다. 

 

@Composable
fun MainScreen() {
}

 

NavController는 Navigation 구성요소의 중심 API로, 스테이트풀(Stateful)이며 앱의 화면과 각 화면 상태를 구성하는 컴포저블의 백 스택을 추적한다. 컴포즈 환경에서 NavController는 rememberNavController()를 이용하여 가져올 수 있습니다. 

 

@Composable
fun MainScreen() {
    val navController = rememberNavController()
}

 

rememberNavController()를 호출하여 NavContoller 인스턴스를 생성할 때 유의해야 할 점은 상태호이스팅에 유의해야 한다. 컴포저블 계층 구조에서 NavController를 만드는 위치는 이를 참조해야 하는 모든 컴포저블이 액세스할 수 있는 곳이어야 한다. 이러한 구조가 상태 호이스팅의 원칙을 준수하는 것이다.

 

NavHost는 자체 포함된 탐색이 발생할 수 있도록 Compose 계층 구조에 제공한다. 지정된 NavGraphBuilder 내의 모든 컴포저블을 제공된 navController에서 탐색할 수 있다. 즉  NavController를 단일 NavHost 컴포저블과 연결해야 한다. NavHost는 내부적으로 구성 가능한 대상을 지정하는 NavGraph와 NavController를 연결한다. 이 연결을 통해 NavController는 이동한 컴포저블 대상을 파악하고 이동할 수 있게 된다.

 

 

NavHost를 추가하고 내부에 NavGraphBuilder.composable() 함수를 이용하여 경로를 정의한다. composable은 2개의 매개변수가 필요하다.

  • route: 경로 이름에 해당하는 문자열입니다. 모든 고유 문자열을 사용할 수 있습니다. CupcakeScreen enum의 상수 이름 속성을 사용합니다.
  • content: 여기에서 특정 경로에 표시할 컴포저블을 호출할 수 있습니다.

route는 문자열로 route 매개변수를 전달하고, content는 컴포저블 함수로 Screen 등 표시할 컨텐츠가 포함된다.

 

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "main") {
        composable("main") { MainMenu(navController) }
    }
}

 

NavHost 의 매개변수를 살펴보면, 위에서 생성한 navController를 전달한다. 그리고 처음 시작점인 startDestination으로 "main"을 이용해 다음의 MainMenu 컴포저블 함수를 첫 경로를 설정하여 줍니다.

 

그리고 MainMenu 컴포저블 함수에 버튼을 클릭하면 navController의 navigate() 함수를 이용해서 화면 이동을 하고 있습니다. 다음  코드를 보면,  첫번째 버튼 클릭 이벤트에서 route 매개변수를 "linear"로 전달 합니다.

@Composable
fun MainMenu(navController: NavController) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(onClick = { navController.navigate("linear") }) {
            Text(text = "리니어레이아웃 사용하기")
        }
        Spacer(modifier = Modifier.height(16.dp))
        Button(onClick = { navController.navigate("table1") }) {
            Text(text = "테이블레이아웃 사용하기")
        }
        Spacer(modifier = Modifier.height(16.dp))
        Button(onClick = { navController.navigate("table2") }) {
            Text(text = "테이블레이아웃 사용하기 2")
        }
    }
}

 

화면이동을 할려면 위에 작성한 NavHost에 route 매개변수가 "linear"일때 실행하는 LinearLayoutScreen() 컴포저블 함수로 이동 할수 있도록 다음과 같이 코드를 추가합니다.

 

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "main") {
        composable("main") { MainMenu(navController) }
        composable("linear") { LinearLayoutScreen() }
    }
}

 

그리고 LinearLayoutScreen.kt 파일을 프로젝트에 추가하고 다음과 같이  LinearLayoutScreen() 컴포저블 함수를 작성 합니다.

 

LinearLayoutScreen.kt

@Composable
fun LinearLayoutScreen() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "리니어레이아웃 예제 화면", style = MaterialTheme.typography.headlineMedium)
        Button(onClick = { /* 추가 기능 구현 */ }) {
            Text(text = "버튼 1")
        }
        Spacer(modifier = Modifier.height(16.dp))
        Button(onClick = { /* 추가 기능 구현 */ }) {
            Text(text = "버튼 2")
        }
    }
}

 

 

화면이동을 할려면 위에 작성한 NavHost에 route 매개변수가 "table1"일때 실행하는 TableLayoutScreen1() 컴포저블 함수로 이동 할수 있도록 다음과 같이 코드를 추가합니다.

 

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "main") {
        composable("main") { MainMenu(navController) }
        composable("linear") { LinearLayoutScreen() }
        composable("table1"){ TableLayoutScreen1() }
    }
}

 

그리고 TableLayoutScreen1.kt 파일을 프로젝트에 추가하고 다음과 같이  TableLayoutScreen1() 컴포저블 함수를 작성 합니다.

 

TableLayoutScreen1.kt

@Composable
fun TableLayoutScreen1() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
            Button(onClick = { /* 추가 기능 구현 */ }) {
                Text(text = "버튼 A")
            }
            Button(onClick = { /* 추가 기능 구현 */ }) {
                Text(text = "버튼 B")
            }
        }
        Spacer(modifier = Modifier.height(16.dp))
        Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
            Button(onClick = { /* 추가 기능 구현 */ }) {
                Text(text = "버튼 C")
            }
            Button(onClick = { /* 추가 기능 구현 */ }) {
                Text(text = "버튼 D")
            }
        }
    }
}

 

 

 

화면이동을 할려면 위에 작성한 NavHost에 route 매개변수가 "table2"일때 실행하는 TableLayoutScreen2() 컴포저블 함수로 이동 할수 있도록 다음과 같이 코드를 추가합니다.

 

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "main") {
        composable("main") { MainMenu(navController) }
        composable("linear") { LinearLayoutScreen() }
        composable("table1"){ TableLayoutScreen1() }
        composable("table2"){ TableLayoutScreen2() }

    }
}

 

그리고 TableLayoutScreen2.kt 파일을 프로젝트에 추가하고 다음과 같이  TableLayoutScreen2() 컴포저블 함수를 작성 합니다.

 

 

TableLayoutScreen2.kt

@Composable
fun TableLayoutScreen2() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) {
            Button(onClick = { /* 추가 기능 구현 */ }) {
                Text(text = "버튼 1")
            }
            Button(onClick = { /* 추가 기능 구현 */ }) {
                Text(text = "버튼 2")
            }
            Button(onClick = { /* 추가 기능 구현 */ }) {
                Text(text = "버튼 3")
            }
        }
    }
}

 

 

🎨 실행화면 

안드로이드 스튜디오에서 실행하면 다음과 같이 메인 메뉴 화면에서 버튼을 클릭하면 해당 화면으로 화면이 이동하게 되며 back 키를 누르면 이전 화면으로 돌아가게 됩니다.

 

 

 

반응형
LIST