<영상 내용 요약 >
1. Jetpack Compose 는 최신 Native UI 도구 키트입니다.
2. (글 작성일 기준) Compose 안정화버전은 1.3.0 까지 출시되었습니다.
3. 아직 실무에 Compose 를 도입하지 않았다면, JetPack Compose 를 준비하기에는 정말 좋은 시기 입니다.
4. Compose 는 직관적이고, 개발 가속화를 위하여 설계되었습니다. (훨씬 적은 코드로 UI 를 개발할 수 있습니다.)
5. 기기성능은 향상되고, 앱에 대한 기대가 커진만큼 UI는 훨씬 동적이고, 표현이 풍부해졌습니다.
6. Views 로도 충분히 만들어낼 수 있지만, 최신 아키텍처를 기반으로 하고 있는 Kotlin 을 활용하는 현대적 도구키트를 원하는 의견으로 인해 Jetpack Compose 가 등장했습니다.
7. Jetpack Compose 는 선언적 독립형 도구 키트입니다.
8. 선언적(Declarative) : 요즘 앱은 데이터가 동적이고, 실시간으로 업데이트 됩니다. 기존에는 xml 에 UI 를 선언하고, 데이터가 바뀌면 UI 도 업데이트 해주어야 합니다. 변형도 필요합니다. 이 과정을 위하여 View를 조회하고, 속성을 설정해야 합니다. 애플리케이션 상태가 바뀔때마다 (데이터베이스, 네트워크 호출 로드, 사용자 상호작용 등) 새로운 정보를 이용하여 UI 를 업데이트하여 데이터를 동기화해야합니다. View 마다 상태가 다르고, 각각 업데이트 해야하므로 그 과정이 복잡하고, 모델과 UI 를 동기화 하는 과정에서 버그가 엄청나게 발생할 수 있습니다. 이는 온전히 개발자가 책임지고 모든걸 업데이트 해야합니다. 앱과 UI 가 복잡해진 만큼, 당연히 오류가 생기기가 더 쉬워졌습니다.
Compose 는 xml 방식과 다른 방식을 사용합니다. Compose 는 "상태를 UI 로 변환합니다." UI 는 변경이 불가능하고, 한번 생성되면 업데이트가 불가능합니다. 앱상태가 바뀌면, 새로운 상태를 새로운 표현으로 변환합니다. 상태에 따른 데이터 동기화 문제가 완전히 해결됩니다. UI 전체를 다시 생성하기 때문입니다. 물론, Compose 는 매우 지능적이고 효율적이어서 변경되지 않은 요소에 대한 작업은 건너뜁니다. 그러나 개념적으로 특정 상태에 따라 UI 를 새로 생성하는 것과 같습니다.
코드는 그저 특정 상태에 따른 UI 형태를 설명할뿐, 생성 방법을 지정하지 않습니다.
Q. 어떻게 상태를 UI 로 변환할까?
A. Compose 에서 UI 구성요소는 구성가능한 주석이 달린 함수일 뿐입니다. 그래서 UI 구성 요소를 빠르고 쉽게 생성 할 수 있습니다.
따라서, 재사용가능한 요소로 구성된 라이브러리로 UI 를 나누는 것이 좋습니다. (중복코드 방지)
@Composable
fun MessageList(messages: List<String) {
Column {
messages.forEach { message ->
Text(text = message)
}
}
}
위 함수는 값을 반환하는 것이 아닌, UI 를 전달합니다. (Compose 라이브러리의 Column 과 Text Composable 사용합니다.)
Text 를 수직으로 배열하고, 간단한 Text Lable 을 표시합니다.
만약, 특정 조건에 따라서 요소를 표시하려면, 즉 message 가 없을때 Lable 을 표현하고 싶다면, if 문을 추가하면 됩니다.
@Composable
fun MessageList(messages: List<String) {
Column {
if (messages.size == 0) {
Text(text = "No messages")
} else {
messages.forEach { message ->
Text(text = message)
}
}
}
}
Composable 은 매개변수를 받을 수 있고, 사실 받아야 합니다.
데이터를 UI 로 변환한다는 것이 이런 뜻입니다. Composable 은 데이터를 함수 매개변수로 받아서 UI 를 전달합니다.
이렇게되면, UI 가 동기화 상태에서 벗어나지 않습니다. (메시지가 없다는 Text 를 삭제하지 않는 등의 실수를 하지 않을 수 있습니다.)
-> 메시지데이터가 있다면, 메시지가 없는 UI 를 제거하고, 메시지가 있는 UI 를 보여주어야하는데, 둘다 보여주는 실수를 하지 않을 수 있다. 는 것입니다.
상태가 바뀌었을때, 이 함수를 실행한다면 새로운 UI 가 생성됩니다. 이러한 행위를 리컴포징(recomposing)이라고 합니다.
메시지 목록은 어떻게 바뀌는가?
-> 콜 스택을 처리하는 동안 ViewModel 이 메시지의 LiveData 를 노출합니다. 이 데이터를 관찰할 수 있고, message 필드를 읽는 Composable 은 새로운 데이터가 입력될때마다 리컴포저블(recomposable) 됩니다
직접 감시 객체를 설정할 필요가 없습니다. Compose 컴파일러는 어느 Composable 이 상태를 읽는지 추적하고, 상태가 바뀌면 자동으로 다시 실행합니다. 여기서 Compose 가 지능적이기 때문에 입력이 변경된 Composable 만 다시 실행하고, 나머지는 건너뜁니다.
@Composable
fun ConversationScreen() {
val viewModel: ConversationViewModel = viewModel()
val messages by viewModel.messages.observeAsState()
MessageList(messages)
}
@Composable
fun MessageList(messages: List<String) {
...
}
위 코드를 보면 이전과 달린 viewModel.messages.observe { ... } 는 필요없고, Compose 컴파일러가 observeAsState() 로 된 messages 를 추적하기 때문에, 해당 상태가 바뀌면 자동으로 ConversationScreen() 를 다시 실행하고, 이 과정에서 입력이 변경된 MessagesList(message: List<String) 도 다시 실행됩니다. 만약, 변경된 데이터가 없는 Composable 함수는 건너뛰는 것으로 이해하면 될 것 같습니다.
각 Composable 은 변경할 수 없습니다. Composable 을 참조하거나, 나중에 쿼리하거나, 내용을 업데이트 할 수 없습니다.
정보입력을 원할땐, 모두 매개변수로 Composable 에 전달해야 합니다.
하지만, 모든 Composable 이 고정되어 있는 것은 아닙니다. 아래 코드가 그 예시입니다.
@Composable
fun MessageList(messages: List<String>) {
Column {
CheckBox(
checked = false
)
...
}
}
위 코드에서 CheckBox 를 클릭해도 View 와 달리 시각적인 변화가 없습니다.
상태를 나타내는 checked 가 상수로 전달되었기 때문입니다.
여기서 checked 의 값을 결정하는 로컬변수를 넣어보면 시각적인 변화를 줄 수 있습니다.
@Composable
fun MessageList(messages: List<String>) {
Column {
var selectAll: Boolean = ...
CheckBox(
checked = selectAll,
onCheckChange = { checked ->
selectAll = checked
}
)
...
}
}
CheckBox 에는 onCheckChange 이벤트를 제공합니다. 이 콜백을 통하여 로컬 상태를 업데이트 할 수 있습니다.
이렇게 하면 Compose 컴파일러가 상태를 읽을 때, checkBox 를 다시 받습니다.
처음에는, 코드를 작성해야 선택이 된다는 것이 직관적이지 못하다고 생각할 수 있지만 이것이 선언적UI 의 핵심 개념입니다.
위 코드를 "단일 진실 공급원" 으로 로직을 올릴 수도 있습니다.
@Composable
fun MessageList(
messages: List<String>,
selectAll: Boolean,
onSelectAll: (Boolean) -> Unit
) {
Column {
CheckBox(
checked = selectAll,
onCheckChange = onSelectAll
)
...
}
}
선언적 UI 의 핵심은, 특정 상태에서 UI의 형태를 완전히 설명하고, 상태가 바뀌면 프레임워크에서 UI 업데이트를 처리합니다.
Compose 는 여러가지 애플리케이션 아키텍처와 호환되지만, 단반향 데이터 플로우를 아키텍처와 잘 맞습니다.
-> ViewModel 이 화면 상태의 단일 스트림을 노출하면, Compose UI 에서 관찰하고, 각 구성요소의 매개변수로 전달합니다.
각 구성요소는 필요한 상태만 수신하므로, 데이터를 바꿀때만 업데이트하면 됩니다.
LiveData<ScreenState> 처럼 ViewState 객체의 단일 스트림을 생성하면, 상태변경을 한곳에서 처리하는 데 도움이 됩니다.
전체적 화면 상태를 추론하고 오류를 낮추기 쉽습니다.
이렇게되면, 입력에 따라 완전히 제어되기 때문에 간단하게 Composable 을 테스트 할 수 있습니다.
9. Jetpack Compose 는 머티리얼 디자인 구성요소와 테마시스템을 구현합니다.
애플리케이션을 어셈블하는데 필요한 구성 요소도 제공합니다. Buttons, Cards, FABs, AppBars 등이 이에 해당합니다.
모든 구성요소는 기본으로 머티리얼 스타일링을 따르고, 머티리얼 테마를 구현하기 때문에 모든 구성요소를 브랜드에 맞게 체계적으로 맞춤 설정할 수 있습니다. 원하는 색, 도형, 서체 스타일 등을 지정할 수 있습니다.
10. Compose 는 간단하지만, 강력한 새로운 레이아웃 시스템을 제공합니다. Row, Column 은 기존의 Linear Layout 과 비슷하다고 보면 되고, Box 는 Frame Layout 과 비슷하다고 보면 됩니다.
그러나 View 시스템과 다르게, Compose 레이아웃 모델은 여러 척도를 전달할 수 없어서 중첩된 레이아웃에 적당합니다.
11. 새로운 Compose DSL 을 적용한 ConstraintLayout 을 사용하면, 더욱 복잡한 레이아웃을 표현할 수 있지만,
맞춤형 레이아웃도 훨씬 간단하게 구현됩니다.
12. 가장 기대가 큰 개선 사항은 새로운 애니메이션 시스템 입니다.
훨씬 간단하게 사용할 수 있고, 보다 효과적이고 간단하게 UI 에 모션을 적용할 수 있습니다.
13. Compose에서는 테스트와 접근성이 일급객체 입니다. UI 에 병렬트리를 제공하는 시맨틱 시스템을 기반으로 합니다.
14. Compose 는 테스트 기능을 극대화하는 전용 테스트 아티팩트를 제공하고, 독립적으로 Composable 을 테스트하는 간편한 API를 제공합니다. 테스트에서 Clock 이 노출되고, 그후 UI 가 업데이트되고, 이를 제어하기 위한 API 가 제공됩니다.
< 실제 완성된 UI 를 어떻게 작성했는지 보고 싶다면, 영상 11분 부터 보시면 됩니다. >
15. Compose 는 기존 View 시스템과 호환됩니다. 필요에 따라 Compose 를 점진적으로 도입할 수 있습니다.
View 시스템 뿐만 아니라, Navigation, ViewModel, LIveData, Rx, Flow, Paging, Hilt 등 다른 주요 라이브러리와의 통합을 제공합니다.
AppCompat XML 테마를 Compose 로 변환할때는 adapter 를 제공합니다.
'BackUp (관리중지) > Android 이론' 카테고리의 다른 글
이제는, SharedPreferences 👉 DataStore (0) | 2021.05.16 |
---|---|
Android Service (0) | 2021.05.14 |
Android Coroutine [코루틴] (0) | 2021.05.04 |
[Android Jetpack] LiveData란, (0) | 2021.04.21 |
Fragment 와 Fragment LifeCycle 분석 (0) | 2021.04.19 |