왜 Inset 처리가 필요한가?
스마트폰 화면 상단과 하단에는 시스템 UI(상태바·네비게이션바)가 있습니다.
앱 화면이 이 영역까지 확장되면(Edge-to-Edge) 콘텐츠가 시스템 UI와 겹칠 수 있어, 영역을 고려한 레이아웃 조정이 필요합니다.
영역을 고려하지 않고 코드를 작성하게 되면
상단 Toolbar가 상태바 아래로 파묻히거나 하단 버튼이 네비게이션바와 겹쳐서 클릭하기 어려운 경우가 생길 수 있겠죠.
따라서 Inset 처리가 필요합니다!


기존에는 어떻게 했을까요?(Accompanist Insets)
예전엔 Accompanist Insets라는 라이브러리를 써서
- 상태바(Status Bar) 높이만큼
- 네비게이션 바(Navigation Bar) 높이만큼 패딩 주기
를 쉽게 했습니다.
Modifier.statusBarsPadding()
Modifier.navigationBarsPadding()
그런데 Accompanist Insets는 deprecated 됐고, Jetpack Compose와 AndroidX가 WindowInsets API를 직접 지원하게 됐어요.
공식 API로 처리하는 방법
Compose 1.2+부터는 WindowInsets 처리를 위한 표준 Modifier들이 있습니다.
enableEdgeToEdge()
Scaffold(
modifier = modifier
.fillMaxSize()
.windowInsetsPadding(WindowInsets.statusBars),
...
bottomBar = {
Modifier.navigationBarsPadding()
}
)
- enableEdgeToEdge() -> 화면을 시스템 바(상태바·네비게이션바)까지 확장해서 그리게 함
- windowInsetsPadding(WindowInsets.statusBars) -> 상태바 높이만큼 패딩
- navigationBarsPadding() -> 네비게이션바 높이만큼 패딩
이건 Compose API이고, Accompanist가 아니라 이미 공식 API이긴합니다.
이 방식처럼 Accompanist 없이도 Compose 내부에서 안전하게 Insets를 처리할 수 있죠.
하지만 Compose 내부에서 뿐만 아니라 바깥에서도 제어가 필요하다면 어떨까요?
대부분의 경우 Compose Modifier로 충분하지만,
- 스플래시 화면: 로고를 화면 정중앙에 배치하고 싶은데 상태바/네비게이션바 때문에 위치가 어긋남
- Compose + XML 혼합 화면: XML 뷰에도 Insets 처리가 필요
- 전체 화면 동영상: 하단 컨트롤 버튼이 네비게이션바와 겹치는 문제 해결
와 같은 경우에는 바깥에서 제어를 해줘야겠죠?
따라서 여기서 한 단계 더 나아가, Compose 바깥에서도 inset 조정하는 법에 대해 알아볼게요.
setOnApplyWindowInsetsListener 활용하기
ViewCompat.setOnApplyWindowInsetsListener는 시스템 UI 영역 변화를 감지하고, 그 값에 맞춰 레이아웃을 동적으로 조정할 수 있습니다.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { _, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
// systemBars.top → 상태바 높이
// systemBars.bottom → 내비게이션바 높이
// 예: 이 값을 Compose ViewModel에 전달해 UI 상태로 관리 가능
insets
}
setContent {
MyApp()
}
}
이렇게 하면 상태바/내비게이션바 높이 정보를 Compose 외부에서 확보하고,
Compose UI나 다른 View 레이아웃에 반영할 수 있습니다.
Compose와 Acticity Insets 연동하기
Activity에서 측정한 Insets 값을 Compose로 넘기려면 ViewModel이나 CompositionLocal을 사용할 수 있습니다.
class InsetsViewModel : ViewModel() {
private val _insets = MutableStateFlow(Insets())
val insets: StateFlow<Insets> = _insets
fun update(insets: Insets) {
_insets.value = insets
}
}
data class Insets(val top: Int = 0, val bottom: Int = 0)
val vm: InsetsViewModel by viewModels()
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { _, insets ->
val sb = insets.getInsets(WindowInsetsCompat.Type.systemBars())
vm.update(Insets(sb.top, sb.bottom))
insets
}
val insets by vm.insets.collectAsState()
Spacer(modifier = Modifier.height(insets.top.dp)) // 상태바 높이만큼 Spacer
이런 식으로요.
결론적으로,
setOnApplyWindowInsetsListener를 쓰면 Activity나 View 레벨에서 시스템 UI 영역 정보(상태바·네비게이션바 크기)를 받아서, 그걸 원하는 방식으로 가공해서 적용할 수 있습니다.
즉, 각 화면·각 뷰마다:
- 둘 다 적용 → 상태바 + 네비게이션바 높이만큼 패딩
- 하나만 적용 → 예: 상단만 패딩, 하단은 그대로
- 둘 다 빼기 → Edge-to-Edge로 꽉 채움
- 값 변형 → 패딩 대신 margin이나 offset에 적용, 혹은 절반만 적용
다 가능합니다.
이제 Accompanist 없이도 공식 API + Acticity 제어만으로 edge-to-edge UI를 안전하고 유연하게 구성할 수 있겠죠? 모두 한번 활용해보시기를 추천드립니다.
'🤖안드로이드🤖' 카테고리의 다른 글
| [안드로이드] 멀티모듈, 왜 그리고 어떻게? (2) | 2025.08.25 |
|---|---|
| [안드로이드] 프로젝트 구조 뜯어보기 - Android SDK에 대하여 (5) | 2025.08.19 |
| [안드로이드] 카카오 로그인 세팅, 구현법 (7) | 2025.08.01 |
| [안드로이드] CMP/KMP 톺아보기🔎 (1) | 2025.06.07 |
| [안드로이드] ktlint와 GitHub Actions로 Android 코드 스타일 자동화하기 (0) | 2025.05.23 |