기존 명령형 패러다임을 이용하여 UI를 그릴 때의 한계점
기존 안드로이드는 xml을 이용한 명령형 프로그래밍 패러다임을 따르는 UI Kit을 이용하여 UI를 그리고 있었다. 우리는 안드로이드 위젯을 업데이트 하기 위해서 아래와 같은 findViewById()와 같은 함수를 이용하여 트리를 탐색한 후 setter을 호출하여 위젯을 변경하였다.
findViewById<TextView>(R.id.blog_title_text).text = "Kotlin World"
위 코드가 실행되면 TextView는 text 프로퍼티에 Kotlin World라는 State를 직접 가지고 있게 된다. 이렇게 View가 직접 State를 가지고 있는 것을 Stateful하다고 한다. 하지만 이러한 방식의 뷰 조작은 오류를 발생할 가능성을 높인다. 여러 위치에서 뷰에 들어가야 할 값을 setter을 이용해 업데이트 할 경우, 데이터를 표시하는 부분 중 하나에 setter을 적용하는 것을 놓친다면 오류가 생기기 때문이다. 특히, 뷰가 복잡해질 수록 오류 가능성이 매우 높아진다.
명령형 프로그래밍 패러다임을 따르는 UI Kit는 Stateful 하며, 뷰가 복잡해질 수록 유지보수를 위한 복잡성이 증가된다.
Compose : 선언형 패러다임을 따르는 Stateless하게 위젯를 생성
Jetpack Compose는 위와 같은 문제를 해결하기 위해 등장하였다. Compose는 선언형 프로그래밍 패러다임을 따르는 UI를 그리는 라이브러리이다.
선언형 프로그래밍 패러다임
선언형 프로그래밍은 어떤 방법으로 해야 하는지를 설명하는 것이 아닌, 어떠한 결과가 나와야 하는지를 나타내도록 프로그래밍 하는 것을 뜻한다. 선언형 프로그래밍은 보통 DSL의 형태로 사용되는데, 선언형 프로그래밍 패러다임을 따르는 대표적인 언어로는 SQL와 같은 언어가 있다. SQL에서는 특정 테이블에 데이터를 넣기 위해 INSERT를 쓸 뿐 INSERT가 어떻게 수행되어야 하는지를 정의하지 않는다. 선언을 하면 DB에서 알아서 INSERT를 해준다.
마찬가지로 Compose에서는 위젯를 그리기 위해 위젯이 어떻게 그려져야 하는지 선언만 하면 위젯이 그려진다. 또한 Compose의 선언형 접근 방식에서는 위젯을 객체로 노출하지 않으며 이는 Compose가 기존 xml에서의 접근과 다르게 getter과 setter함수를 직접 호출하지 않는다는 것을 뜻한다. 이로인해 Compose가 그리는 위젯은 Stateless할 수 있다. 아래 예시를 통해 어떻게 Stateless한 위젯이 생성될 수 있는지를 알아보자.
Stateless한 위젯
@Composable
fun BlogTitle(blogName: String) {
Row {
Text(blogName)
}
}
@Composable은 Compose로 그려지는 뷰를 뜻한다. 위의 예시에서 BlogTitle이라는 View는 blogName을 변수로 받고 있으며, blogName은 Text위젯으로 전달된다. 즉, 위 @Composable의 Text는 State를 외부로부터 전달받을 뿐 직접 가지고 있지 않다. 즉, Compose는 직접 State에 대한 데이터를 가지고 있지 않으며, 외부로부터 전달받는다. 이러한 위젯 구성 방식을 우리는 Stateless하다고 한다. 만약 위를 Stateful하게 바꾸려면 다음과 같이 바꾸면 된다.
@Composable
fun BlogTitle() {
Row {
Text("Kotlin World")
}
}
선언형 패러다임으로의 전환의 의의
안드로이드의 MVVM 아키텍처는 ViewModel에서 State를 관리한다. Compose를 사용하면 모든 위젯을 Stateless하게 구성한다음 ViewModel로부터 필요한 State를 전달받을 수 있게 된다. 이를 통해 기존 명령형 프로그래밍 패러다임에서 View가 복잡해질 수록 유지보수를 위한 복잡성이 증가해지는 것을 방지하고, State의 관리 포인트를 일원화 함으로써 코드를 깔끔하고 유지보수하기 쉽게 유지할 수 있게 된다.