article thumbnail image
Published 2022. 7. 14. 12:14

선언형 프로그래밍 패러다임

지금까지 UI를 얻데이트 하는 가장 일반적인 방법은 findViewById()와 같은 함수를 사용하여 트리를 탐색하고 button.setText(String), container.addChild(View) 또는 img.setImageBitmap(Bitmap)과 같은 메서드를 호출하여 노드를 변경하는 것이다. 이러한 메서드는 위젯의 내부 상태를 변경한다.

뷰를 수동으로 조작하면 오류가 발생할 가능성이 커진다. 데이터를 여러 위치에서 렌더링한더면 데이터를 표시하는 뷰 중 하나를 업데이트 하는 것은 잊기 쉽다. 일반적으로 업데이트가 필요한 뷰의 수가 많을수록 소프트웨어 유지관리 복잡성이 증가한다.

선언형 UI 모델은 처음부터 화면 전체를 개념적으로 재생성한 후 필요한 변경사항만 적용하는 방식으로 작동한다. 이러한 접근 방식은 스테이트풀(Stateful) 뷰 계층 구조를 수동으로 업데이트할 때의 복잡성을 방지할 수 있다. Compose는 선언형 UI 프레임워크다.

화면 전체를 재생성하는 데 있어 한 가지 문제는 시간, 컴퓨팅 성능 및 배터리 사용량 측면에서 잠재석으로 비용이 많이 든다는 것이다. 이 비용을 위해 Compose는 특점 시점에 UI의 어떤 부분을 다시 그려야 하는지 지능적으로 선택한다.

 

간단한 구성 가능한 함수

Compose를 사용하면 데이터를 받아서 UI 요소를 내보내는 Composable 함수 집합을 정의하여 사용자 인터페이스를 빌드할 수 있다.

@Composable
fun Greeting(name: String) {
	Text("Hello $name")
}
  • 함수는 @Composable 주석으로 주석이 지정된다. 모든 컴포저블 할수에는 이 주석이 있어야 한다. 이 주석은 이 함수가 데이터를 UI로 변환하기 위한 함수라는 것을 컴파일러에게 알린다.
  • 함수는 데이터를 받는다. 컴포저블 함수는 매개변수를 받을 수 있으며 이 매개변수를 통해 앱 로직이 UI를 형성할 수 있다. 이 예에서 위젯은 사용자의 이름을 부르면서 환영할 수 있도록 String을 받는다.
  • 함수는 UI에 텍스트를 표시한다. 이를 위해 실제로 텍스트 UI 요소를 생성하는 Text() 컴포저블 함수를 호출한다. 컴포저블 함수는 다른 컴포저블 함수를 호출하여 UI 계층 구조를 내보낸다.
  • 함수는 아무것도 반환하지 않는다. UI를 내보내는 Compose 함수는 UI 위젯을 구성하는 대신 원하는 화면 상태를 설명하므로 아무것도 반환할 필요가 없다.
  • 이 함수는 빠르고 멱등원(여러번 실행해도 동일한 결과)이며 부작용이 없다.→ 함수는 속성 또는 전역 변수 수정과 같은 부작용 없이 UI를 형성한다.
  • → 함수는 동일한 인수로 여러번 호출될 때 동일한 방식으로 작동하며 전역 변수 또한 random() 호출과 같은 다른 값을 사용하지 않는다.

 

선언형 패러다임 전환

많은 명령형 객체 지향 UI 도구 키트를 사용하여 위젯의 트리를 인스턴스화함으로써 UI를 초기화한다. 각 위젯은 자체의 내부 상태를 유지하고 앱 로직이 위젯과 상호작용할 수 있도록 getter 및 setter 메서드를 노출한다.

 

Compose의 선언형 접근 방식에서 위젯은 비교적 Stateless 상태이며 setter 또는 getter 함수를 노출하지 않는다. 사실상 위젯은 객체로 노출하지 않는다. 동일한 컴포저블 함수를 다른 인수로 호출하여 UI를 업데이트 한다. 이렇게 하면 ViewModel과 같은 아키텍처 패턴에 상태를 쉽게 제공할 수 있다. 그런 다음, 컴포저블은 식별 간으한 데이터가 업데이트 될 때마다 현재 애플리케이션 상태를 UI로 변환한다

앱로직은 최상위의 컴포저블 함수에 데이터를 제공한다. 그러면 함수는 데이터를 사용하여 다른 컴포저블을 호출함으로써 UI를 형성하고 적절한 데이터를 해당 컴포저블 및 계층 구조 아래로 전달한다.

 

 

 

사용자가 UI와 상호작용할 때 UI는 onClick과 같은 이벤트를 발생시킨다. 이러한 이벤트를 앱 로직에 전달하여 앱의 상태를 변경해야 한다. 상태가 변경되면 구성 가능한 함수는 새 데이터와 함께 다시 호출된다. 이렇게 하면 UI 요소가 다시 그려진다. 이 프로세스를 재구성이라고 한다.

 

사용자가 UI 요소와 상호작용하며 이에 따라 이벤트가 트리거된다. 앱 로직이 이벤트에 응답한다. 그러면 컴포저블 함수가 필요한 경우 새 매개변수를 사용하여 다시 호출된다.

 

동적 콘텐츠

@Composable
fun Greeting(names: List<String>) {
	for(name in names) {
		Text("Hello $name")
	}
}

이 함수는 이름 목록을 받아서 각 사용자에 대한 인사말을 생성한다. if문을 상용하여 특정 UI 요소를 표시할지 여부를 결정할 수 있다. 루프를 사용할 수 있다. 도우미 함수를 호출할 수 있다. 기본 언어의 유연성을 완전히 활용할 수 있다.

 

재구성

명령형 UI 모델에서 위젯을 변경하려면 위젯에서 setter를 호출하여 내부 상태를 변경한다. Compose에서는 새 데이터를 사용하여 구성 가능한 함수를 다시 호출한다. 이렇게 하면 함수가 재구성되며 필요한 경우 함수에서 내보낸 위젯이 새 데이터로 다시 그려진다. Compose 프레임워크는 변경된 구성요소만 지능적으로 재구성할 수 있다.

@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
	Button(onClick = onClick) {
		Text("I've been clicked $clicks times")
	}
}

버튼이 클릭될 때마다 호출자는 clicks 값을 업데이트 한다. Compose는 Text 함수를 사용해 람다를 다시 호출하여 새 값을 표시한다. 이 프로세스를 재구성이라고 한다. 값에 종속되지 않는 다른 함수는 재구성되지 않는다.

함수의 재구성을 건너뛸 수 있으므로 컴포저블 함수 실행의 부작용에 의존하지 마라. 그렇게 하면 사용자가 앱에서 이상하고 예측할 수 없는 동작을 경험할 수 있다. 부작용은 앱의 나머지 부분에서 볼 수있는 모든 변경 사항이다.

  • 공유 객체의 프로퍼티 쓰기 기능
  • ViewModel 에서 Observable 업데이트
  • 쉐어드 프리퍼런스 업데이트

컴포저블 함수는 애니메이션이 렌더링될 때와 같이 모든 프레임에서와 같은 빈도로 재실행될 수 있다. 애니메이션 도중 버벅거림을 방지하려면 구성 간 함수가 빨라야 한다. SharedPrefs에서 읽기와 쓰기 비용이 많이 드는 작업을 실행해야 하는 경우 백그라운드 코루틴에서 작업을 실행하고 값 결과를 컴포저블 함수에 매개변수로 전달한다.

@Composable
fun SharedPrefsToggle(
	text: String, value: Boolean, onValueChanged: (Boolean) -> Unit
) {
    Row {
        Text(text)
        Checkbox(checked = value, onCheckedChange = onValueChanged)	
	}
}

예를들어 위 코드는 SharedPreferences의 값을 업데이트하는 컴포저블을 생성하는 코드이다. 이 컴포저블은 쉐어드 프리퍼런스 자체에서 읽거나 쓰지 않아야 한다. 대신에 이 코드는 ViewModel 백그라운드 코루틴으로 읽기 및 쓰기 로직을 이동시켜야 한다.

 

 

 

https://developer.android.com/jetpack/compose/mental-model?hl=ko

'안드로이드 > UI' 카테고리의 다른 글

Compose에서 "donut-hole skipping" 란 무엇인가  (0) 2022.08.17
Compose 생명주기  (0) 2022.07.31
Compose 상태관리  (0) 2022.07.14
복사했습니다!