Compose 와 XML 의 차이 - YangJJune/U-Compass GitHub Wiki
XML(eXtensible Markup Language)은 데이터의 구조를 정의하는 마크업 언어로 2008년 안드로이드 출시와 함께 Android UI 레이아웃을 정의하는 데 사용되어왔습니다. UI 관련한 코드들은 XML 상에서 작성하고, 기능이나 데이터를 관리하는 Java/Kotlin 파일들과 명확하게 분리되어 있습니다.
아래와 같이 안드로이드 UI 요소를 XML 로 선언합니다. 각각 레이아웃의 크기, 위치, 색상, 스타일 등 다양한 속성들을 XML 코드상에서 정의합니다.
<Button
android:id=”@+id/myButton”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text="Click Me" />
위와 같이 선언된 XML 코드들은 빌드 시 Runtime 에 각각의 요소들이 View 객체로 인스턴스화됩니다.(inflate)
fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_layout)
}
아래 사진처럼 ViewGroup
안에 View
가 존재하는 계층구조로 XML layout 이 구성됩니다.
XML 기반 뷰 시스템에서 layout 이 화면에 그려지기까지의 과정은 다음과 같습니다.
- 레이아웃 정의 : res/layout 폴더에 XML 기반 레이아웃을 정의합니다.
- 인플레이션 & View 계층 구조 생성 : Runtime 에서 안드로이드 시스템에 의해 레이아웃이 View 객체들로 inflate 됩니다.
- 아래 3가지 단계에 의해서 크기가 측정되고, 위치를 정하고, 실제로 화면에 그려지게 됩니다.
- 각 뷰의 크기 측정 :
measure()
- 각 뷰의 위치 결정 :
layout()
- 각 뷰를 실제로 화면에 그림 :
draw()
- 각 뷰의 크기 측정 :
XML 기반 뷰 시스템은 안드로이드 UI 를 구성하는 방식으로 오랫동안 자리매김해오면서 많고 성숙한 생태계를 구성하고 있고, 그만큼 다양한 테마와 스타일, 그리고 많은 라이브러리들이 존재하고 있습니다.
하지만 XML 기반 뷰 시스템은 시간이 지날수록 한계가 점차 드러났습니다. Android 앱이 갈수록 복잡해짐에 따라서 그만큼 작성해야 할 XML 코드도 점점 방대해지고 복잡해져갔습니다. 유지보수의 어려움을 초래하는 경우가 많아졌고, 유지되어오던 UI 인터랙션과 애니메이션에 관한 문제점도 대두되어왔습니다.
Jetpack Compose 는 최근 Android 앱 개발의 패러다임을 변화시키고 있는 혁신적인 도구입니다.
2019년 5월 Google I/O 에서 처음 발표되었으며, Compose 프레임워크는 네이티브 Android UI 를 구축하기 위한 가장 최신의 방법론으로 자리 잡았습니다.
"동일한 버튼 클래스의 경우 [코드] 의 규모가 10배 더 작았습니다.” (Twitter)
“RecyclerView로 빌드한 모든 화면에서 상당한 감소 효과가 나타났으며, 대부분의 화면이 여기에 해당합니다.” (Monzo)
“앱에서 목록이나 애니메이션을 만드는 데 필요한 코드 줄이 이렇게 적다는 사실이 매우 기뻤습니다. 기능마다 작성하는 코드 줄 수가 줄어든 덕분에 고객에게 가치를 제공하는 데 더욱 집중할 수 있게 되었습니다.” (Cuvva)
“Kotlin과 XML 사이를 전환하는 것이 아니라 모든 것이 같은 언어로 종종 같은 파일에서 작성되면 코드를 추적하기가 훨씬 쉬워집니다.” (Monzo)
“Compose의 레이아웃 시스템은 개념적으로 더 단순하기 때문에 추론하기도 쉽습니다. 복잡한 구성요소의 코드도 쉽게 읽을 수 있습니다.” (Square)
Jetpack Compose 는 Kotlin 을 기반으로 하며, XML 없이 오직 Kotlin 코드만으로 UI 를 구성할 수 있습니다. XML 기반 뷰 시스템을 사용하는 것에 비해 현저히 더 적은 코드로 UI 를 구성할 수 있고, 더 많고 다양한 작업을 할 수 있습니다. XML 없이 Kotlin 코드만 사용하기 때문에 원하는 코드를 추적하거나 수정하기에도 용이합니다. Kotlin 을 사용하기 때문에 다양한 Kotlin 의 기능들을 응용해서 원하는 UI 를 구현할 수 있습니다.
아래 코드와 같이 @Composable
어노테이션과 함께 함수의 형태로 UI 를 구성할 수 있습니다.
@Composable
fun MyButton() {
Button(onClick = { onClick() }) {
Text(text = "Click me")
}
}
위와 같이 선언된 @Composable
함수는 Activity 의 onCreate()
함수에서 setContent { }
의 바디에 컴포넌트를 삽입합니다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
edgeToEdge()
setContent {
Scaffold { innerPadding ->
Greeting(innerPadding)
}
}
}
}
Compose 기반 뷰 시스템에서는 다음과 같은 순서로 구성되고 배치되며 그려집니다.
- Composition : UI 정의 및 초기 구성
- Layout : 크기 측정 및 배치
- Drawing : 화면에 실제 UI 를 렌더링
- 선언적이며 반응적인 접근 방식
Compose 는 UI 의 업데이트 방식에서 “How” 보다 “What” 에 집중합니다.
명령형 UI, 선언형 UI ?
명령형 UI (Imperative UI) 란 어떻게(How) UI 를 구성하고 업데이트할 지 개발자가 명시적으로 지시하는 방식입니다.
binding.myButton.setOnClickListener {
binding.textView.text = "Clicked!"
}
UI 가 새로 구성되고 업데이트되는 방식을 개발자가 직접 타이핑해서 명령합니다.
선언형 UI (Declarative UI) 란 무엇(What) 보여줄지에 대한 것을 정의하는 방식입니다.
var clicked by remember { mutableStateOf(false) }
Button(onClick = { clicked = true }) {
Text(if (clicked) "클릭됨!" else "안녕하세요")
}
개발자는 최종 UI 의 상태만 선언하고 관리하면 되고, 실제 UI 업데이트는 프레임워크(Compose) 가 알아서 처리해줍니다.
- 코드 간결성과 모듈성
Jetpack Compose 는 코드의 간결성을 강조합니다. 기존의 XML 기반 UI 에서는 보일러 플레이트 코드를 포함해 많은 코드가 요구되었지만, Compose 에서는 필요한 UI 요소를 간단하게 작성할 수 있습니다. 또한 함수 형태로 구성되어 있어서 화면 어디에서나 함수를 호출하는 형태로 UI 컴포넌트를 모듈화해서 재사용할 수 있습니다.
- 성능과 효율성
Jetpack Compose 는 XML 방식의 View 계층구조에 비해서 상대적으로 flat 하고 가벼운 Composable 트리구조를 사용합니다. 이로 인해 렌더링 과정에서의 오버헤드를 줄이고 Compose 자체적으로 최적화된 렌더링 엔진을 직접 활용해서 효율적으로 화면을 구성할 수 있습니다.
또한 State 를 추적해 데이터가 변경된 부분만 UI 를 업데이트하는 Recomposition 기능을 사용해서 좋은 퍼포먼스를 가집니다.
-** 애니메이션과 유연성**
Composable 과 Modifier 를 사용해서 원하는 맞춤형 UI 디자인을 쉽게 구현할 수 있습니다.
또한 Jetpack Compose는 기본적으로 다양한 애니메이션 API 를 내장하고 있어서 개발자가 더 매력적고 역동적인 UI를 만들 수 있도록 도와줍니다. 간단한 코드로 다양하고 복잡한 애니메이션을 구현할 수 있으며, 이를 통해서 사용자 경험(User Experience)을 더욱 풍부하게 만들 수 있습니다.
- 미래 지향적 개발
Jetpack Compose 는 앞으로 Android 개발에 있어 필수적인 UI 구현 도구로 자리 잡을 것입니다. Google 은 Compose 시스템을 공식적인 UI 개발 도구로 지정했으며, Jetpack Compose 에 대한 지속적인 업데이트와 개선을 약속했습니다. 아직은 XML 기반 뷰 시스템에 비해서 적은 수의 라이브러리와 좁은 생태계를 가질 수도 있지만, 장기적으로 봤을 때는 CMP(Compose MultiPlatform) 같은 Compose 의 생태계는 가파르게 성장할 것으로 보입니다.
- 확립되고 친숙함
XML은 10년 이상 Android UI 개발의 표준으로 자리 잡아왔었고 많은 Android 개발자들이 XML 기반 뷰 시스템을 사용하여 화면 UI 를 구현했었습니다.
오랫동안 이어져 왔고 많이 사용되어 왔기 때문에 이와 관련된 방대한 양의 리소스와 라이브러리가 존재하고 다양한 참고자료들이 존재합니다.
- 풍부한 생태계
XML은 수년에 걸쳐 View, Theme, Style을 포함한 광범위한 도구 및 라이브러리 생태계를 확보했습니다. 이러한 생태계는 개발자들이 다양한 UI 요소를 쉽게 활용할 수 있도록 도와줍니다.
-** 관심사 분리**
XML을 사용하면 UI와 로직을 명확하게 분리할 수 있습니다. XML 파일에서 레이아웃 디자인에 집중하고, Kotlin이나 Java에서 로직을 처리할 수 있기 때문에, 코드의 가독성과 유지보수성이 향상된다고 할 수 있습니다.
- 이전(옛) 버전과의 호환성
XML은 옛 버전의 Android OS와의 호환성을 유지할 수 있는 장점이 있습니다. 이는 기존의 앱을 업데이트하거나 유지보수할 때 큰 도움이 됩니다. 새로운 기능을 추가하면서도 기존의 사용자 경험을 해치지 않도록 할 수 있습니다.
-** 보일러플레이트 코드**
XML을 사용하다 보면 보일러플레이트 코드가 많이 발생하게 됩니다. 이는 코드의 양이 많아지고, 가독성이 떨어지게 만드는 원인이 됩니다. 특히, 복잡한 UI를 구현할 때는 이러한 보일러플레이트 코드가 더욱 두드러지게 나타납니다.
- UI 유지보수가 어려움
Android 앱이 복잡해짐에 따라 XML 코드도 같이 방대하고 광범위해져서 유지보수 비용이 점차 증가하는 모습을 보이고 있습니다. UI 요소가 많아질수록 XML 파일의 크기도 커지고, 이로 인해 유지보수가 어려워지는 문제가 발생합니다.
-** UI 유연성이 제한됨**
XML은 UI의 유연성을 제한하는 경향이 있습니다. 복잡한 UI를 구현할 때, XML의 구조적 특성 때문에 원하는 대로 UI를 조정하기 어려운 경우가 많습니다. 이는 개발자들이 UI를 자유롭게 디자인하는 데 제약이 될 수 있습니다.
-** 성능 오버헤드**
XML 레이아웃을 인플레이션하면 런타임에 View 객체로 변환해야 하기 때문에 성능 비용이 발생합니다. 복잡한 UI를 가진 앱에서는 이러한 성능 병목 현상이 발생할 수 있습니다. 따라서 성능을 최적화하기 위해서는 XML 사용에 신중해야 합니다.
- Kotlin-First의 선언적 접근 방식과 반응형 프로그래밍
Compose는 Kotlin 언어와 밀접하게 통합되어 있으며, 이를 통해 개발자들은 더 직관적이고 간결한 코드를 작성할 수 있습니다. Kotlin의 다양한 기능들을 활용할 수 있어, 코드의 가독성과 유지보수성이 높습니다.
또한 반응형 프로그래밍을 지원하여, UI가 상태 변화에 즉각적으로 반응하도록 설계되었습니다. 선언형 방식으로 Compose 프레임워크 자체에서 State 변화에 따라서 자동으로 UI 를 업데이트해줘서 더욱 편하게 코드를 작성할 수 있습니다.
- 보일러플레이트 코드 감소
Compose는 보일러플레이트 코드를 대폭 줄여주는 데 큰 역할을 합니다. 전통적인 Android 개발에서는 XML 레이아웃 파일과 Java/Kotlin 코드 간의 상호작용을 위해 많은 양의 코드가 필요했지만, Compose를 사용하면 Kotlin 코드 하나만으로 코드를 UI 와 기능, 데이터 작업을 진행할 수 있습니다. 결과적으로 더욱 간단한 코드로 UI를 구현할 수 있게 됩니다.
- 모듈과 재사용성
Compose에서는 UI 구성 요소를 재사용하기 쉽게 설계할 수 있습니다. Composable 함수를 사용하여 다양한 UI 구성 요소를 모듈화함으로써, 코드의 재사용성을 높이고 생산성을 향상시킬 수 있습니다.
- 원활한 애니메이션 지원
Compose는 애니메이션을 구현하기 위한 내장 애니메이션 API 를 포함에 다양한 기능을 제공하여, 자연스럽고 매끄러운 사용자 인터페이스를 만들어낼 수 있습니다. 이러한 애니메이션 기능은 개발자에게 시간과 노력을 절약해줍니다.
- 러닝 커브
Compose는 선언적 UI 방식을 사용하는 만큼, 기존의 XML과 명령형 UI를 사용해온 개발자들에게는 러닝 커브가 존재할 수 있습니다. 처음 접하는 개발자들은 Compose의 개념을 이해하는 데 시간이 걸릴 수 있습니다.
추가적으로 Compose를 효과적으로 사용하기 위해서는 Kotlin에 대한 깊은 이해도가 필요합니다. 또한, 상태 관리를 잘 다루지 못하면 애플리케이션의 동작에 큰 영향을 미칠 수 있습니다.
- Compose Preview의 안정성
Compose Preview는 Android Studio의 XML 디자인 도구에 비해 안정성이 떨어질 수 있습니다. Preview 기능이 Android Studio 상 XML editor 에 비해 성능과 안정성이 부족한 편입니다.
- XML 생태계의 부족
Android 앱이 복잡해짐에 따라 XML 코드도 같이 방대하고 광범위해져서 유지보수 비용이 점차 증가하는 모습을 보이고 있습니다. Compose를 사용하는 경우에도 기존 XML 생태계와의 통합이 어려운 점이 존재합니다.
Button by XML
<Button
android:id=”@+id/myButton”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text="Click Me" />
binding.myButton.setOnClickListener {
onClick()
}
Button by Compose
@Composable
fun MyButton() {
Button(onClick = { onClick() }) {
TexT(text = "Click me")
}
}
RecyclerView by XML
<!-- book_item.xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/bookTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"/>
<TextView
android:id="@+id/bookAuthorTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"/>
</LinearLayout>
class BookAdapter(private val bookList: List<Book>) : RecyclerView.Adapter<BookAdapter.BookViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookViewHolder {
val binding = BookItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return BookViewHolder(binding)
}
override fun onBindViewHolder(holder: BookViewHolder, position: Int) {
holder.bind(bookList[position])
}
override fun getItemCount(): Int = bookList.size
class BookViewHolder(val binding: BookItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(book: Book) {
binding.bookTitleTextView.text = book.title
binding.bookAuthorTextView.text = book.author
}
}
}
LazyColumn by Compose
@Composable
fun BookList(books: List<Book>) {
LazyColumn {
items(books) { book ->
BookItem(book)
}
}
}
@Composable
fun BookItem(book: Book) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(text = book.title, fontWeight = FontWeight.Bold, fontSize = 16.sp)
Text(text = book.author, fontSize = 14.sp)
}
}
Google 이 공식적으로 Compose 를 지원함에 따라, XML 기반 코드들은 더 이상 많은 발전과 업데이트는 기대하기 힘들어 보입니다. 따라서 현대적이고 효율적인 UI 를 원하는, 미래를 보는 기업 혹은 프로젝트(XML기반)에서 Jetpack Compose 로 마이그레이션하는 과정이 불가피할 것으로 생각됩니다.
Compose의 선언적 접근 방식, 간결한 코드, 뛰어난 성능과 유연성은 앞으로 Android 개발의 핵심 기술로 자리 잡을 것입니다. 지금부터라도 Compose를 익히고 적용해보는 것이 장기적으로 큰 도움이 될 것입니다.
출처 : https://velog.io/@ikseong00/Compose-%EC%99%80-XML-%EC%9D%98-%EC%B0%A8%EC%9D%B4