[Android] - Jetpack Compose
이번글은 Jetpack Compose에 관한 글입니다.
현재 안드로이드 기술에서는 UI를 구현하기 위한 기술로 Xml과 Jetpack Compose 두 가지가 있습니다.
Xml은 전통적인 방식으로 예전부터 사용했던 방식이고, Jetpack Compose는 떠오르는 신기술입니다.
그렇다면 왜 Xml이 아닌 새로운 방식의 기술이 등장했고, 도입하려고 하는 것인지, 그리고 제가 느낀 점에 대해서 적었습니다.
Jetpack Compose란?
Jetpack Compose는 네이티브 UI를 빌드하기 위한 Android의 최신 툴킷
공식문서에서 Compose를 채택해야 하는 이유는 다음과 같이 정의합니다.
간단한 코드
이전까지의 xml 방식에서는 Kotlin과 Xml을 번갈아가면서 작업을 했습니다.
하지만 Compose를 도입하면 Kotlin으로만 개발을 할 수 있고, xml과 kotlin으로 분리되었던 코드들을 하나의 Kotlin 파일에서 작성하기에, 관리하기가 용이해졌습니다.
view에서 id값이 변하면, kotlin 파일도 수정해야하는 이러한 번거로움이 없어졌습니다.
직관적인 API
Compose는 선언적 API를 사용합니다.
선언적 프로그래밍을 설명하기 위해서 항상 등장하는 것이 명령형 프로그래밍입니다.
두 용어를 한 문장으로 요약하면
명령형 프로그래밍은 무엇을 어떻게 할 것인가를 강조하고, 선언형 프로그래밍은 무엇을 할 것인가를 강조합니다.
예를 들어 김치볶음밥을 만든다고 가정할 때,
- 명령형 방식 : 냉장고에서 김치를 꺼내서, 먹기 좋게 잘라, 프라이팬에 기름을 두르고, 파를 넣어서 파기름을 만들고, 파가 노릇노릇해진다면, 김치와 돼지고기를 같이 넣어서 약한 불에 볶아, 김치와 고기가 익었다면, 불을 끄고, 먹을 만큼의 밥을 넣고, 밥과 양념을 비빈다음, 중불로 밥을 볶아!
- 선언형 방식 : 파 , 김치, 돼지고기, 밥, 식용유를 사용하여 볶아 양념이 골고루 섞일 때까지 중불에서 요리해!
어떻게 보면 선언형 방식은 명령형 방식에 비해 불친절하다고 느껴질 수 도 있습니다.
저렇게 말하면 어떻게 요리를 해?라고 생각할 수 있는데 선언형 프로그래밍의 가정은 명령형으로 자세히 언급된 부분들이 이미 구현되어 있어야 합니다.
요리를 하는 과정이라면, 언급된 레시피들을 선언형 방식에서는 레시피라는 추상화된 형태로 구현되어 있다는 뜻입니다.
코드 적으로 예를 들자면,
val list = listOf(1,2,3,4,5,6,7,8,9,10)
val result = mutableListOf<Int>()
for (element in list) {
if(element >= 3 && element%2==0){
result.add(element)
}
}
위 방식은 명령형 프로그래밍입니다.
과정을 뜯어보면,
- list에는 1~10까지의 숫자가 담겨있다.
- result는 숫자를 저장할 임시 리스트이다.
- list의 모든 원소를 탐색해라.
- list의 원소가 3이상이고, 짝수인 경우 result에 추가해라.
이렇게 단계를 하나하나 뜯어보면, list안의 원소들 중에서 3 이상의 짝수를 필터링하고 있습니다.
명령형 방식은 어떻게를 강조한다고 했습니다.
이제 이해가 가실까요? 3이상의 짝수를 어떻게 필터링하는지를 코드로 풀어내고 있습니다.
val list = listOf(1,2,3,4,5,6,7,8,9,10)
val result = list.filter{ it>=3 && it%2 ==0}
위 방식은 선언형 방식입니다.
솔직히 filter가 어떠한 방식으로 진행되는지는 알 수 없지만, 3 이상의 짝수를 필터링하는 것이 result라는 것은 명확하게 알 수 있습니다.
우리는 잘 구현된 방식들을 간단하게 표현해서 의도를 표현하는 것이고, 이것이 선언형 프로그래밍의 장점입니다.
UI를 구현하는 명령형 방식과 선언형 방식은 아래와 같습니다.
명령형 UI
val linearLayout = LinearLayout()
linearLayout.orientation = VERTICAL
val textView1 = TextView().apply {
text = "jaehan"
}
linearLayout.add(textView1)
명령형 방식으로 레이아웃을 리니어로 설정하고, 방향을 설정하고, 텍스트뷰를 생성하고 레이아웃에 더해줍니다.
선언형 UI
<LinearLayout
android:orientation="vertical">
<TextView
android:text="jaehan"/>
</LinearLayout>
기존의 우리가 사용하는 xml 방식도 선언형 프로그래밍의 대표적인 방법입니다.
@Composable
fun mainScreen() {
Column {
Text("jaehan")
}
}
Compose 방식의 특징인데 Composable이 나머지 세세한 부분을 처리해 주고, 사용자는 UI를 선언만 해주면 됩니다.
빠른 개발 과정
Compose는 기존의 모든 코드와 호환됩니다.
Compose에서 Views를, Views에서 Compose 코드를 호출할 수 있습니다.
실제로 기존의 view로 선언된 방식을 Compose로 전환하는 과정도 공식문서에서 안내하고 있습니다.
https://developer.android.com/jetpack/compose/interop/compose-in-existing-ui?hl=ko
기존 UI와 Compose 통합 | Jetpack Compose | Android Developers
기존 UI와 Compose 통합 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 뷰 기반의 UI를 사용하는 앱의 경우 전체 UI를 한 번에 재작성하지 않는 것이 좋습니다.
developer.android.com
그리고 특히 강점으로 와닿았던 장점이 미리 보기 기능을 지원하는 것이었습니다.
@Preview
@Composable
private fun PlantNamePreview() {
MaterialTheme {
FragmentSignup(SignUpViewModel(SignUpRepository(), SignUpValidator()))
}
}
Preview 어노테이션을 통해서
FragmentSignUp의 UI를 미리 볼 수 있습니다.
미리 보기는 XML에서도 미리 볼 수 있었잖아? 왜 이거 가지고 호들갑이야?라고 생각하실 수도 있는데,
무려 상호작용까지도 지원해 줍니다..!
기존의 xml 방식에서는 실행을 하고, 해당 뷰로 가서 상호작용을 테스트해야 하는데, 정말 편리한 기능인 거 같습니다.
저는 하나의 화면을 xml에서 compose로 전환하는 간단한 작업을 했지만, 모든 뷰들 컴포스로 구현한다고 가정할 경우
개발 과정이 빨라질 거 같습니다.
느낀 점
분명히 Jetpack Compose의 장점이 많은 것은 사실이고, 실제로 적용해 봤을 때 기존 xml 방식과는 다르게, 테스트하기에 정말 용이하다고 느꼈습니다. 그리고 선언적으로 생성한 Button, TextField와 같이 xml보다 가독성이 뛰어나고, 재사용성 부분에서도 뛰어나다고 느꼈습니다. 하지만 Jetpack Compose의 생명주기는 Activity, Fragment의 생명주기와 많이 달랐고, Jetpack Compose가 익숙하지 않은 저는 아직까지 xml과 DataBinding을 사용해서 MVVM 패턴을 구현하는 것이 조금은 더 쉬웠습니다. 그리고 복잡한 화면같은 경우는 아직까지 XML로 구현하는 것이 편하다고 느꼈습니다. 그래서 아직까지 현업에서 XML을 선호하고, 점점 Compose로 바꿔가고 있다는 것을 몸소 느꼈습니다. 하지만 리사이클러뷰와 같은 어뎁터, ViewHolder를 통해서 구현하던 XML 방식을 Compose로 바꾸면 정말 간단한 코드로 구현할 수 있고, 간단한 화면일 경우는 Compose가 XML보다 훨씬 유리하다고 판단했습니다. 조금씩 Compose에 익숙해지기 위해서 간단한 화면은 Compose로 구현하고, 복잡한 화면은 Xml로 구현하는 방향성으로 공부할 예정입니다!
다음글은 Jetpack Compose의 사용법에 대해서 포스팅하겠습니다!