명령형 UI와 선언형 UI의 차이

SSY·2023년 2월 25일
2

Compose

목록 보기
1/10

1. 시작하며

안드로이드에서 선풍적인 변화가 있다. 그리고 그것을 다른 회사에서도 점점 도입해가는 추세인 기술이 있다. 그것은 바로, 'Android Compose'이다. Compose는 선언형 UI방식으로 코드의 라인 수를 '미친듯이'줄였을 뿐만 아니라, 코드의 재사용성을 높였다. 또한 UI렌더링 방식에서 성능적인 이점 또한 대단하다. 이번 포스팅에선 이 점을 다루려 한다.

[Compose의 이점]

  • 라인 수를 미친듯이 줄인다.
  • 코드의 재사용성도 높인다.
  • UI렌더링 방식에서 성능적인 이점이 명확하다.

2. 명령형UI와 선언형 UI

2.1. 명령형UI

명령형 프로그래밍 방식이란, 어떤 작업을 수행할때 말 그대로 명령을 내리는걸 의미한다. 이때의 명령은 '함수의 호출'을 통한 명령을 의미한다.

예를 들어, 우리가 TextView에 데이터를 쓴다고 가정해보자. 이땐 다음과 같은 방식이 들어간다.

<TextView
	android:id="+@id/tvTextView"
	android:text="helloWorld"
	...
/>

그리고 이러한 TextView는 Android에서 제공해주는 API객체이다. 아래와 같은 형태를 띄고 있다.

@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { ... }

즉, 우리가 xml내에서 위와 같은 'android:text="helloWorld"'와 같이 선언해 주는 것은 위 TextView class 내에서 아래의 메서드를 호출해주는 것과 같다.

그리고 위의 text상태 값을 보유하고 있기 위해 메모리 내에는 'TextView객체'가 존재한다.

@android.view.RemotableViewMethod
    public final void setText(CharSequence text) {
        setText(text, mBufferType);
    }

그리고 이러한 메서드는 xml내에서 뿐만 아니라 Activity내의 소스코드에서도 접근할 수 있다. 아래와 같이 말이다.

val textView = findViewById<TextView>(R.id.tvTitle)
textView.text = "HelloWorld"

그리고 이는 [Android공식 홈페이지의 선언형 패러다임 부분]에도 나와 있다.

여기서 우리는 다음과 같은 결론을 내릴 수 있다.

[결론]

  • xml의 TextView에 UI를 렌더링하는 방식은 명령형 방식이다.
    • xml내 : android:text=" ... "방식으로 명령을 내린다.
    • Activity내 : setText방식으로 명령을 내린다.
  • 그리고 이러한 상태값을 가지고 있는 TextView객체가 존재한다.

이러한 방식이 명령형UI방식이다. 이제 선언형UI방식에 대해 알아보자.

2.2. 선언형UI

획기적인 열풍을 불러오고 있는 Compose. 도대체 어떤 점이 다르기에 이렇게 각광을 받고 있는 걸까? 우선 우리가 TextView를 선언할때, Compose상에선 어떻게 하는지를 보자.

@Composable
fun loadTextView() {
	Text("HelloWorld")
}

놀랍지만 이게 단순히 끝이다. 우린 TextView를 View에 띄워주기 위해선 이 작업만 하면 된다. 딱 봐도 알것이다. 코드 라인이 미친듯이 줄었다는 것을 말이다. 단 3줄이면 끝이 난다.

그리고 필자가 이건 '정말 미친듯이 혁명적이다!'라고 생각한 부분이 있다. 그 부분을 소개하려 한다.

우린 Compose이전에 RecyclerView를 사용할때 어떻게 사용했는가? RecyclerView.Adapter를 정의해서 사용해주었다. 아~주 길게길게 말이다. 이는 아래와 같은 형태를 띈다.

class MainAdapter: RecyclerView.Adapter<MainAdapter.MainViewHolder>() {
    override fun onCreateViewHolder() { ... }
    override fun onBindeViewHolder() { ... }
    override fun getItemCount() { ... }
    inner class MainViewHolder() { ... }
}

위의 형태를 모두 정의해야만 한다. 필자가 그동한 개발한 경험을 토대로 봤을 때, 200줄은 우.습.게 넘어가곤 한다. 그리고 이러한 Adapter객체는 Activity에서도 일일히 호출해줘야 한다.

binding {
    adapter = MainAdapter()
    layoutManager = ...
    setHasFixedSize(true)
}

뭐.. 대충 위와 같은 형태를 띈다. 근데 Compose에선?

@Composable
fun MainListView() {
	LazyColumn() {
    	    items(items = item) { item ->
        	    ItemView(item = item)
        	}
    	}
	}
}

@Composable
fun ItemView() {
    Text("HelloWorld")
}

이게 끝이다. 추가적으로 필자는 이걸 보고 처음에 믿지 못했다. 아니... 몇백줄이나 되던 RecyclerView를 이렇게 10줄로 줄여버린다고? 하지만 이는 [안드로이드 공식 홈페이지]에도 나와 있는 부분이다.

과장이 아니고 이정도로 놀랐다.

이제 다시 TextView로 돌아와서 설명을 이어가볼까 한다.

Compose를 통해 UI를 호출해주는 TextView("HelloWorld")이 부분. 객체라고 생각되지 않는가? 충분히 그렇게 생각할 수 있다. 그 이유는 바로, 맨 앞글자가 '대문자'이기 때문이다. 하지만 이는 Composable메서드를 선언하기 위한 Compose의 컨벤일 뿐이다. 사실상 이는 메서드이다. TextView를 타고 들어가볼까?

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의 모습이다. 보면 놀랍게도 fun이라는 것을 알 수 있다.

즉, Compose란 UI를 렌더링할 때, '객체의 상태'를 xml에 바인딩하는 방식이 아니다. 단순히 구글 API로 제공되는 Composable메서드를 호출하는 방식으로 UI를 렌더링하는 것이다.

그리고 이는 [Android 공식 홈페이지]에서도 다음과 같이 말해주고 있다.

위에 대해 간단 설명을 해보자면, 명령형UI에서는 위젯( = TextView의 객체와 대응)의 setter메서드를 사용한다고 나와 있다. 이는 필자가 이전에 말한 'setText'메서드와도 일맥상통한다. 같은 맥락으로, 선언형UI에서는 구성 가능한 함수(=Composable함수)를 '단순히 호출'하는 형태로 UI를 구성한다는 것이다. 이는 '@Composable TextView()'를 호출하는 것과 일맥 상통한다.

결론

  • 명령형UI 방식은 여러개의 객체를 생성(ex. TextView...)함으로써 UI렌더링을 시도한다.
  • 선언형UI 방식은 Composable한 함수를 호출함으로써 UI렌더링을 시도한다.
  • 선언형UI 방식은 명령형UI방식에 비해 라인 수를 미친듯이 줄일 수 있다.
profile
불가능보다 가능함에 몰입할 수 있는 개발자가 되기 위해 노력합니다.

0개의 댓글