Jetpack Compose를 공부하면서 "선언형 UI"라는 단어를 자주 접하게 되었습니다.
Compose의 가장 큰 장점으로 꼽히는 선언형 UI가 기존의 명령형 UI와 어떤 차이가 있는지,
그리고 왜 선언형 UI가 더 효율적인지에 대한 궁금증이 생겼습니다.
이 글에서는 명령형 UI와 선언형 UI의 차이점,
그리고 Jetpack Compose가 제공하는 선언형 UI의 장점에 대해 정리해보겠습니다.
1. 명령형 UI와 선언형 UI란?
UI를 구성하는 방법에는 크게 명령형 UI(Imperative UI)와 선언형 UI(Declarative UI) 두 가지 접근법이 있습니다.
이 두 방식은 상태 관리와 UI 업데이트 방법에서 근본적인 차이를 보입니다.
- 명령형 UI → 어떻게 그릴지를 개발자가 직접 명령
- 선언형 UI → 무엇을 그릴지 선언하고, UI 갱신은 프레임워크가 처리
Jetpack Compose는 선언형 UI 패러다임을 따르는 대표적인 프레임워크입니다.
2. 명령형 UI (Imperative UI)
명령형 UI란?
명령형 UI는 UI를 직접 구성하고, 변경사항이 생길 때마다 수동으로 업데이트하는 방식입니다.
안드로이드의 기존 XML View 시스템이 대표적인 명령형 UI 패러다임을 따릅니다.
명령형 UI 특징
- 절차적 접근: UI를 순차적으로 생성하고, 상태가 바뀌면 직접 UI를 수정해야 합니다.
- 상태와 UI 분리: 데이터와 UI가 별도로 관리되며, 상태 변경 시 수동으로 UI를 갱신해야 합니다.
- 보일러플레이트 코드: findViewById, setText(), setOnClickListener() 등 반복 코드가 많습니다.
명령형 UI 예시 (Android View System)
val textView = findViewById<TextView>(R.id.textView)
textView.text = "초기 텍스트"
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
textView.text = "버튼 클릭됨"
}
명령형 UI의 단점
- 보일러플레이트 코드 증가
- findViewById 등 반복적인 코드가 많아져서 가독성이 떨어집니다.
- UI와 로직의 결합
- 상태가 변경될 때마다 UI를 수동으로 갱신해야 하므로 코드가 복잡해집니다.
- 상태 동기화 어려움
- 상태와 UI가 별도로 관리되기 때문에, 변경 사항이 누락될 가능성이 있습니다.
- 성능 저하 가능성
- 복잡한 뷰 계층을 가진 경우, 렌더링 성능이 저하될 수 있습니다.
3. 선언형 UI (Declarative UI)
선언형 UI란?
선언형 UI는 무엇을 그릴지 선언하고, 상태에 따라 UI가 자동으로 업데이트되는 방식입니다.
Jetpack Compose, React, SwiftUI 등이 선언형 UI 프레임워크에 해당합니다.
선언형 UI 특징
- 결과 중심: “이런 UI가 필요하다”고만 선언하면, 나머지는 프레임워크가 처리합니다.
- 상태 기반 렌더링: 상태(State)가 변경되면, UI가 자동으로 갱신됩니다.
- 간결한 코드: 보일러플레이트 코드가 줄어들어, 가독성이 향상됩니다.
선언형 UI 예시 (Jetpack Compose)
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
상태 기반 UI 예시
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column {
Text(text = "Count: $count")
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
count 값이 변경되면, Text 뷰가 자동으로 업데이트됩니다.
개발자는 상태 관리에만 집중하면 되고, UI 갱신 로직을 따로 작성할 필요가 없습니다.
4. 명령형 UI vs 선언형 UI 비교
항목 | 명령형 UI | 선언형 UI |
초점 | 어떻게 그릴지 명령 | 무엇을 그릴지 선언 |
상태 관리 | 수동으로 UI를 갱신 (findViewById, setText) | 상태 변경에 따라 자동으로 UI 갱신 |
코드량 | 보일러플레이트 코드가 많음 | 간결하고 가독성이 높음 |
성능 최적화 | 복잡한 뷰 계층에서 성능 저하 가능 | 필요한 부분만 리컴포지션하여 성능 최적화 |
유지보수성 | 상태와 UI의 동기화가 어려워 유지보수가 복잡함 | 상태 기반으로 자동 렌더링, 유지보수가 쉬움 |
대표 프레임워크 | 안드로이드 View System (XML) 등 | Jetpack Compose, React, SwiftUI 등 |
5. Jetpack Compose의 선언형 UI 방식의 장점
Jetpack Compose는 기존의 안드로이드 XML View 시스템의 단점을 개선하기 위해 만들어졌습니다.
Compose가 해결한 문제점
- 보일러플레이트 코드 제거
- findViewById가 사라지고, Kotlin 코드로 직접 UI를 구성할 수 있습니다.
- XML과 Kotlin 간의 연결이 필요 없습니다.
- UI와 로직 분리
- @Composable 함수를 사용하여, UI와 비즈니스 로직을 명확히 분리할 수 있습니다.
- 가독성이 좋아지고, 유지보수도 쉬워집니다.
- 상태 기반 UI 렌더링
- remember, mutableStateOf 등을 활용해 상태를 관리할 수 있습니다.
- 상태가 변경되면, 해당 UI가 자동으로 리컴포지션됩니다.
- 성능 최적화
- 부분적인 리컴포지션을 통해, 변경된 부분만 다시 그리므로 성능이 향상됩니다.
- 복잡한 뷰 계층이 사라져 렌더링 효율이 높아집니다.
- 간결하고 직관적인 코드
- Kotlin 언어만으로 UI를 구성할 수 있어, 일관성이 향상됩니다.
- 실시간 미리보기(Preview) 기능으로 개발 속도가 빨라집니다.
6. Jetpack Compose 예제 – 선언형 UI의 장점
To-Do 리스트 예시
@Composable
fun TodoList() {
var todos by remember { mutableStateOf(listOf("Study", "Workout")) }
Column {
todos.forEach { todo ->
Text(text = todo)
}
Button(onClick = {
todos = todos + "New Task"
}) {
Text("Add Todo")
}
}
}
설명
- todos 리스트가 상태로 관리됩니다.
- 버튼을 클릭하면 새로운 할 일이 추가되고, UI는 자동으로 업데이트됩니다.
- UI와 상태가 완벽히 연결되어 있어, 따로 UI 갱신 코드를 작성할 필요가 없습니다.
7. 결론 – 선언형 UI의 장점
명령형 UI는 어떻게 그릴지를 개발자가 직접 명령해야 했고,
상태 변경 시 UI를 수동으로 갱신해야 했습니다.
반면, 선언형 UI는 무엇을 그릴지를 선언하면,
상태 변경에 따라 UI가 자동으로 갱신됩니다.
Jetpack Compose는 이러한 선언형 UI 방식을 도입하여,
- 코드가 간결해지고
- 상태 관리가 쉬워지며
- UI 업데이트가 직관적으로 이루어질 수 있도록 개선하였습니다.
'Android > Compose' 카테고리의 다른 글
[Android/Compose] collectAsState()를 활용한 상태 관리 (0) | 2025.03.20 |
---|---|
[Android/Compose] LazyColumn vs RecyclerView 비교 : RecyclerView 없이 리스트 만들기 (0) | 2025.02.19 |
[Android/Compose] Recomposition: 상태 변화에 따른 UI 갱신 (0) | 2025.02.06 |
[Android/Compose] Modifier를 파라미터로 전달하는 이유 (0) | 2025.02.05 |
[Android/Compose] Spacer를 활용한 UI 요소 중앙 및 하단 배치 (0) | 2025.02.04 |