Scaffold는 Material Design 사양에 따라 기본 화면 구조를 구현하는 Jetpack Compose의 composable 함수입니다. Scaffold는 TopAppBar, BottomBar, FloatingActionButton, Drawer 및 Snackbar를 위한 슬롯을 제공합니다. Google Android Documentation, 2025에 따르면 Scaffold는 Material Design 시스템 구성 요소의 여백을 관리하기 때문에 Compose 애플리케이션의 대부분 화면에서 필수 요소입니다.
핵심 사항
Scaffold는 Material Design 사양에 따라 화면 프레임워크를 구현하는 composable 함수입니다. Scaffold는 전통적인 의미의 컨테이너가 아니라 화면에서 여러 표준 Material 구성 요소의 배치를 조정하는 레이아웃 관리자입니다.
Scaffold는 TopAppBar, BottomBar 및 FloatingActionButton의 여백을 관리하며 시스템 요소(상태 표시줄 및 탐색 표시줄)도 관리합니다. 이를 통해 콘텐츠가 시스템 요소와 겹치지 않고 Material Design 지침을 준수할 수 있습니다.
Material Design Guidelines, 2025에 따르면 Scaffold는 모든 앱 화면의 루트 요소여야 합니다. 이는 View 시스템의 레거시 CoordinatorLayout을 대체하고 화면 레이아웃을 구축하기 위한 더 간단하고 선언적인 API를 제공합니다.
Scaffold는 modifier, scaffoldState, topBar, bottomBar, floatingActionButton, drawerContent, snackbarHost 및 content 매개변수를 허용합니다. content 매개변수는 PaddingValues(기본 콘텐츠가 고려해야 할 여백)를 받습니다.
@Composable
fun BasicScaffold() {
Scaffold(
topBar = {
TopAppBar(
title = { Text("홈") }
)
},
floatingActionButton = {
FloatingActionButton(onClick = {}) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
}
) { innerPadding ->
Column(
modifier = Modifier.padding(innerPadding)
) {
Text("메인 콘텐츠")
}
}
}
topBar는 앱 상단 바(일반적으로 TopAppBar 또는 LargeTopAppBar)를 배치하는 슬롯입니다. Scaffold는 콘텐츠가 topBar와 겹치지 않도록 innerPadding에 자동으로 여백을 추가합니다. TopAppBar에는 제목, 탐색 아이콘 및 작업이 포함될 수 있습니다.
bottomBar는 하단 탐색 표시줄을 위한 슬롯입니다. BottomNavigation, BottomAppBar 또는 NavigationBar를 지원합니다. Scaffold는 콘텐츠의 innerPadding을 계산할 때 bottomBar의 높이를 고려합니다. Material Design Guidelines, 2025에 따르면 bottomBar는 3~5개의 탐색 지점이 있는 화면에서 사용해야 합니다.
floatingActionButton은 플로팅 액션 버튼을 위한 슬롯입니다. Scaffold는 Material Design 사양에 따라 FAB를 자동으로 배치합니다(올바른 여백으로 오른쪽 하단 모서리). floatingActionButtonPosition을 사용하면 FAB를 화면 하단 가장자리 중앙으로 이동할 수 있습니다.
이 예제에서 Scaffold는 모든 주요 슬롯을 사용합니다: 제목이 있는 TopAppBar, 세 개의 탐색 항목이 있는 BottomBar 및 주요 작업을 위한 FloatingActionButton.
@Composable
fun FullScaffold() {
Scaffold(
topBar = {
TopAppBar(
title = { Text("앱") },
navigationIcon = {
IconButton(onClick = {}) {
Icon(Icons.Default.Menu, "Menu")
}
}
)
},
bottomBar = {
NavigationBar {
NavigationBarItem(
icon = { Icon(Icons.Default.Home, "홈") },
label = { Text("Home") },
selected = true,
onClick = {}
)
NavigationBarItem(
icon = { Icon(Icons.Default.Search, "검색") },
label = { Text("Search") },
selected = false,
onClick = {}
)
}
},
floatingActionButton = {
FloatingActionButton(onClick = {}) {
Icon(Icons.Default.Add, "Add")
}
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
Text("화면 콘텐츠")
}
}
}
ScaffoldState는 DrawerState와 SnackbarHostState를 결합한 Scaffold의 상태 객체입니다. DrawerState는 사이드 메뉴 열기 및 닫기를 관리하고 SnackbarHostState는 Snackbar 표시 및 숨기기를 관리합니다. 상태는 rememberScaffoldState()를 통해 생성되며 scaffoldState 매개변수에 전달됩니다.
DrawerState는 프로그래밍 방식 Drawer 제어를 위한 open 및 close 메서드를 제공합니다. Drawer 상태는 열림, 닫힘 또는 부분적으로 열림일 수 있습니다. drawerGesturesEnabled를 사용하면 Drawer를 여는 스와이프 제스처를 비활성화하고 버튼을 통한 프로그래밍 방식 열기만 남길 수 있습니다.
SnackbarHostState는 Snackbar(화면 하단의 임시 알림)를 관리합니다. showSnackbar 메서드는 메시지와 선택적 작업이 있는 Snackbar를 표시합니다. Material Design Guidelines, 2025에 따르면 Snackbar는 중요하지 않은 간단한 메시지에 사용해야 합니다.
@Composable
fun ScaffoldWithDrawer() {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
title = { Text("메뉴") },
navigationIcon = {
IconButton(onClick = {
scope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(Icons.Default.Menu, "Menu")
}
}
)
},
drawerContent = {
Column(modifier = Modifier.padding(16.dp)) {
Text("항목 1")
Text("항목 2")
}
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
Text("콘텐츠")
}
}
}
Scaffold를 사용한 완전한 화면 예제에는 TopAppBar, BottomNavigation, FloatingActionButton 및 FAB 누를 때 알림을 표시하는 Snackbar가 포함됩니다. ScaffoldState는 상태 조정을 위해 모든 구성 요소에 전달됩니다.
@Composable
fun MainScreen() {
val state = rememberScaffoldState()
val scope = rememberCoroutineScope()
Scaffold(
scaffoldState = state,
topBar = {
TopAppBar(
title = { Text("IT Sectr") },
actions = {
IconButton(onClick = {}) {
Icon(Icons.Default.Notifications, "Notifications")
}
}
)
},
bottomBar = {
NavigationBar {
NavigationBarItem(icon = { Icon(Icons.Default.Home, "홈") },
label = { Text("Home") }, selected = true, onClick = {})
NavigationBarItem(icon = { Icon(Icons.Default.Settings, "설정") },
label = { Text("Settings") }, selected = false, onClick = {})
}
},
floatingActionButton = {
FloatingActionButton(onClick = {
scope.launch {
state.snackbarHostState.showSnackbar("완료")
}
}) {
Icon(Icons.Default.Add, "Add")
}
}
) { padding ->
Box(
modifier = Modifier.padding(padding).fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text("환영합니다")
}
}
}
innerPadding은 Scaffold가 content 블록에 전달하는 PaddingValues입니다. 여기에는 콘텐츠가 statusBar, navigationBar, topBar 및 bottomBar와 겹치는 것을 방지하는 데 필요한 여백이 포함됩니다. 다른 기기에서 올바르게 표시하려면 innerPadding 사용이 필수입니다.
Scaffold는 시스템 여백과 설치된 슬롯의 높이를 기반으로 innerPadding을 자동으로 계산합니다. 카메라 아일랜드나 화면 컷아웃이 있는 기기에서 Scaffold는 상태 표시줄의 여백을 조정합니다. 물리적 버튼이 있는 구형 기기에서는 하단 여백이 없을 수 있습니다.
Android Developers, 2024에 따르면 innerPadding은 Modifier.padding(innerPadding)을 통해 콘텐츠의 루트 컨테이너에 적용해야 합니다. 콘텐츠가 스크롤 가능한 컨테이너를 사용하는 경우 키보드 및 시스템 제스처의 올바른 작동을 위해 innerPadding이 Modifier.consumeWindowInsets을 통해 적용됩니다.
자주 묻는 질문
Scaffold는 컨테이너가 아니라 TopAppBar, BottomBar, FAB, Drawer 및 Snackbar를 위한 슬롯을 제어하고 시스템 구성 요소의 여백을 자동으로 처리하는 레이아웃 관리자입니다. Column과 Box는 Material Design을 고려하지 않고 요소를 배치만 합니다.
Scaffold는 Material Design 구성 요소를 사용하는 화면에 권장됩니다. TopAppBar, BottomBar 또는 FAB가 없는 단순한 화면에서는 Scaffold가 필수가 아니지만 innerPadding을 통해 올바른 시스템 여백을 제공하므로 슬롯이 없어도 유용합니다.
Scaffold의 scaffoldState 매개변수에 ScaffoldState를 전달한 다음 코루틴 내에서 state.snackbarHostState.showSnackbar(“메시지”)를 호출합니다. Scaffold가 자동으로 화면 하단에 Snackbar를 표시합니다.
네, Scaffold는 scaffoldState가 전달되지 않으면 기본적으로 내부 상태를 생성합니다. 그러나 Drawer 및 Snackbar를 프로그래밍 방식으로 제어하려면 rememberScaffoldState()를 통해 ScaffoldState를 생성하고 전달해야 합니다.
Scaffold의 floatingActionButtonPosition 매개변수를 사용합니다. 기본적으로 FAB는 오른쪽 하단(FabPosition.End)에 위치합니다. FabPosition.Center로 설정하면 FAB를 화면 하단 가장자리 중앙에 배치할 수 있습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.