xml기반의 ViewSystem에서 kotlin기반의 Compose로 전환하며 패러다임이 바뀌었는데, 이는 '명령형'에서 '선언형'으로의 변화를 의미한다. 이로인해 개발자는 UI를 업데이트할 때, 명령적인 메서드 호출(eg., TextView.setText
등)을 전혀 신경쓰지 않아도 되며, 오로지 Composable함수만 호출 및 그 파라미터에 상태값을 선택적으로 주입함으로써 UI 바인딩이 더 쉬워진다. 이로인해 라인수도 크게 줄어든다. 단적인 예로, ViewSystem의 RecyclerView를 사용할 때와 비교했을 때, Compose는 Lazy*
메서드를 사용하여 라인수를 크게 줄일 수 있다.
[Compose의 이점]
- 라인 수의 감소.
- 코드의 재사용성 향상.
명령형 프로그래밍 방식이란, 위에서 말했듯, 특정 작업을 수행할때, 문자 그대로 명령을 내려 작업을 진행시키는 것을 의미한다. 이때의 명령은 함수 호출을 통한 동작 지시를 의미한다. 예를 들어, 우리가 TextView
에 데이터를 쓴다고 가정해보자. 이땐 다음과 같은 방식이 들어간다.
<TextView
android:id="+@id/tvTextView"
android:text="helloWorld"
...
/>
위 TextView는 Android에서 제공해주는 UI 갱신 API인데, 아래와 같이 class의 형태를 띄고있으며, 그 내부에서 UI갱신을 위한 메서드 또한 다수 제공한다.
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
{ ... }
즉, 우리가 ViewSystem에서 TextView
의 문자를 변경해주는 등의 UI 업데이트 작업은 android:text="helloWorld
와 같이, 클래스 내부 메서드를 호출해주게 된다.
public final void setText(CharSequence text) {
setText(text, mBufferType);
}
참고 : [Android공식 홈페이지의 선언형 패러다임 부분]
[정리]
ViewSystem은 xml기반, 명령형 방식, 즉 메서드 호출 방식으로 UI를 업데이트하며, 이를 위해 개발자는TextView.setText
등의 메서드 호출을 해줘야한다.
선언형 방식의 COmpose는 UI 업데이트를 어떻게 하는지 알아보자.
@Composable
fun loadTextView() {
Text("HelloWorld")
}
매우 간결하다. TextView
에 대응되는 Text
에 문자값 설정을 위해 위 샘플 코드와 같이 '상태값을 주입'해주기만하면 된다. 이는 기존 TextView
와 비교해봤을 때, 라인수가 크게 줄었다는 것을 알 수 있다. 기존 ViewSystem의 TextView
는 UI 갱신을 위해 findViewById<TextView>()
나 DataBinding
등의 방식으로 UI참조 후, 명령형 방식으로 갱신해주었다. 하지만 선언형 기반 Compose는 위와같이 단순 '상태값 주입'만으로 끝이다.
추가적으로, ViewSystem의 RecyclerView
를 대체한 LazyColumn()
도 라인수를 매우 크게 줄인 1가지 경우이다.
우린 Compose이전, RecyclerView
를 어떻게 사용했는지 복기해보자. 사용을 위한 첫 단계로, RecyclerView.Adapter
를 정의한다. 그리고 각 아이템에 맞는 xml
또한 정의해주어야 한다. 어댑터 정의가 끝났으면, Activity
내, Adapter를 Activity에 바인딩되는 코드 또한 작성해줘야만 한다.
// RecyclerView.Adapter 정의
class MainAdapter: RecyclerView.Adapter<MainAdapter.MainViewHolder>() {
override fun onCreateViewHolder() { ... }
override fun onBindeViewHolder() { ... }
override fun getItemCount() { ... }
inner class MainViewHolder() { ... }
}
// RecyclerView.Adapter를 Activity에 연결
binding {
adapter = MainAdapter()
layoutManager = ...
setHasFixedSize(true)
}
이러한 일련의 여럿 작업의 결과, 1개RecyclerView
의 라인수는 200줄은 쉽게 넘어간다. 하지만 Compose에선 어떨까?
@Composable
fun MainListView() {
LazyColumn() {
items(items = item) { item ->
ItemView(item = item)
}
}
}
}
@Composable
fun ItemView() {
Text("HelloWorld")
}
이로인해 알 수 있는 점은 Compose는 '선언형 UI'방식을 채택하기에, 단순 상태값(안드로이드 앱 아키텍처에선 UiState)변경만으로 이를 Composeble함수 파라미터에 주입하고 이로 인해 UI가 상태값에 '반응'하여 변경된다. 또한 당연하게도, '명령형 UI'방식의 단점인 'UI 갱신을 위한 메서드 호출'작업이 없어, 라인 수가 크게 줄어든 것을 확인할 수 있으며, 개발자가 이를 신경쓰지 않아도 되어 개발 유지보수성 또한 올라간다.
참고 : [안드로이드 공식 홈페이지]
Compose를 처음 접하는 사람은 Compose UI 컴포넌트인 TextView("HelloWorld")
가 객체라고 생각할 수 있다. 그도 그럴 것이, 맨 앞글자가 '대문자'이기 때문이다. 하지만 이는 Composable메서드를 선언하기 위한 컨벤일 뿐이다. 사실상 이는 함수이다.
package androidx.compose.material3
@Composable
fun Text(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) { ... }
위 TextView메서드를 클릭해서 타고 들어간 TextView의 모습이다. 보면 놀랍게도 함수라는 것을 알 수 있으며, 내부 파라미터인 color
, fontSize
, text
등을 받고있는걸 볼 수 있다. 이로 인해 Text
로 인해 표현되는 UI를 업데이트할 수 있는 것이다.
참고 : [Android 공식 홈페이지]
명령형 UI와 선언형 UI를 정리하면 아래와 같다.
TextView.setText
) 즉, 말 그대로 명령적 방식으로 UI를 업데이트한다.