반응형
Composable 함수의 속성
- @Composable 어노테이션을 통해서 함수나 표현식의 타입을 변경
- 해당 타입에 일부 제약 사항이나 특성을 부여
Compose Runtime
- Composable 함수가 사전에 정의된 특성을 준수하도록 가정
- 효과
- 병렬적인 Composition
- 우선순위에 따른 임의의 Composition 정렬
- 효과적인 Recomposition
- 위치 기억법 (positional memoization)
호출 컨텍스트 (Calling context)
- Compose Compiler는 일반적인 컴파일러 실행단계 중에 동작
- Composable 함수의 중간 표현인 Intermediate Representation을 변환하여 추가적인 정보 부여
- 함수의 매개변수 목록의 끝에 새롭게 추가된 Composer
- Composable 함수는 오로지 다른 Composable 함수에서만 호출
- 이 호출 컨텍스트(calling context)는 필수적
- 트리가 오직 Composable 함수로 구성되도록 보장
- Composer가 트리를 따라 하향 전달될 수 있게 보장
Composer
- Composer 매개변수의 인스턴스는 런타임에 주입
- 모든 하위 Composable 호출로 전달
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
//------------------------------변환------------------------------------
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier, $composer: Composer<*>) {
Text(
text = "Hello $name!",
modifier = modifier,
$composer
)
}
멱등성 (Idempotent)
- 연산을 여러 번 적용하더라도 결과가 달라지지 않는 불가변의 성질
- 생성하는 노드 트리에 대해 멱등성을 가져야 함
- 동일한 입력 매개변수를 사용하여 Composable 함수를 다시 실행하더라도 동일한 트리가 생성되어야 함
- recomposition과 같은 작업을 위해 해당 성질이 필요함
- 입력값이 변경된 노드만 recomposition을 수행하고 나머지는 생략
- 해당 과정은 멱등성이 성립할 때만 나머지 생략 가능
통제되지 않은 Side Effect 방지
- Side Effect - 호출된 함수의 제어를 벗어나서 발생할 수 있는 예상치 못한 모든 동작
- Side Effect 예시
- 내부적 - 로컬 데이터 읽기, 네트워크 요청 작업
- 외부적 - 예외를 던질 수 있는 서드파티 API
- Composable 함수가 사이드 이펙트를 실행한다면 매 함수 호출 시마다 새로운 프로그램 상태를 생성하여 멱등성을 위배하게 됨
- Composable 함수를 stateless하게 만들어야 함
Composable 함수 내에서 네트워크 요청 실행
@Composable
fun UserName(networkUseCase: AllEventCase) {
val cases = networkUseCase.getNetworkEvents()
LazyColumn {
items(cases) { case -> Text(text = case.name) }
}
}
- Composable 함수는 Compose Runtime에 의해 짧은 시간 내에 여러 번 다시 실행될 수 있음
- 네트워크 요청이 여러 번 수행되어 사이드 이펙트 발생
Composable 함수 실행에 순서 관계를 부여한 경우
@Composable
fun MainScreen() {
FirstScreen()
SecondScreen()
ThirdScreen()
}
- FirstScreen, SecondScreen, ThirdScreen은 순서에 상관없이, 병렬적으로 실행될 수 있음
Effect Handler
- Composable 함수에서 통제된 환경내에서 effect를 호출할 수 있게 해줌
- Side-effect가 Composable의 lifecycle을 인식하게 하여, lifecycle에 의해 통제 가능하게 함
- Composable 함수 내에서 아무런 제어를 받지 못하고 직접적으로 effect가 호출되는 것을 방지
재시작 가능 (Restartable)
- Composable은 recomposition 때문에 여러 번 호출할 수 있음
- Composable 함수는 관찰하는 State의 변화에 기반하여 반작용적으로 재실행되도록 설계
빠른 실행 (Fast execution)
- Composable 함수
- UI를 구축하거나 반환하지 않음
- 인메모리 구조를 구축 및 업데이트하기 위한 데이터를 방출
- 이러한 과정은 애니메이션의 각 프레임 만큼이나 자주 발생하기도 함
- 런타임시 해당 부분을 충족하기 위해서 비용이 큰 계산 작업8들은 코루틴으로 처리
위치 기억법 (Positional memoization)
- Compose Runtime이 설계에 따라 Composable 함수를 기억할 수 있게 해줌
- Restartable하고 추론된 모든 Composable 함수는 recomposition을 생략할 수 있어야 함
- 함수 메모이제이션(memoization)의 한 형태
- 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 줄이는 캐싱 기법
- 순수 함수 호출을 최적화하고 중복 계산을 줄이며 성능을 향상시키는 방법
- 순수 함수에서만 가능 → 동일한 입력값에서 동일한 결과를 반환한다는 가정 성립
- Compose Runtime은 동일한 함수가 동일한 매개변수 값으로 다른 위치에서 호출될 때
- 동일한 Composable 부모 트리 내에서 고유한 다른 ID를 생성
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
Text(
text = "Hello $name!",
modifier = modifier
)
Text(
text = "Hello $name!",
modifier = modifier
)
}
- 인메모리 트리는 해당 함수들을 세 개의 다른 인스턴스로 저장
- recomposition을 거쳐도 유지
반복문에서 생성된 Composable의 리스트 형태의 경우
@Composable
fun Greeting(names: List<String>, modifier: Modifier = Modifier) {
Column {
for (name in names) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
}
}
- 각각의 talk 요소는 리스트 내에서 다른 항 목으로 치부되고, 결과적으로 트리에서는 서로 다른 노드로 구성
- 리스트 상단이나 중간에 요소를 추가하는 경우 → 모든 Text Composable 함수에서 recomposition
- 해결 방법
- Composable 함수 key 사용
- 명시적으로 선언하여 유지
@Composable
fun Greeting(names: List<Name>, modifier: Modifier = Modifier) {
Column {
for (name in names) {
key(name.key) {
Text(
text = "Hello ${name.str}!",
modifier = modifier
)
}
}
}
}
Suspend 함수와의 유사성
- Kotlin의 suspend 함수는 다른 suspend 함수에서만 호출
- suspend 함수 또한 호출 컨텍스트를 필요
- Continuation 매개변수를 통해 전달
- suspend 함수 끼리만 묶일 수 있도록 보장
- Continuation
- 실행을 중단하고 재개하는 것에 매우 구체적
- 콜백 인터페이스로 모델링
- 실행 지점 간 점프를 수행
- 다양한 중단점을 조율
- Compose @Composable
- 재시작 가능
- 상태을 관찰하여 반응
- 런타임에서 최적화될 수 있는 대규모의 함수 호출 그래프에서 인메모리 표현을 생성
Composable 함수의 색깔(The color of Composable functions)
- 함수 컬러링
- 비동기(async)와 동기(sync)는 잘 결합되지 않음 → 동기에서 비동기 호출 불가
- 이를 결합하기 위해 Promise와 async/await 등 도입하는 방식도 존재
- suspend 함수도 colored된 것으로 간주
- 표준 함수와 suspend 함수를 혼합하여 프로그램을 작성하는 것은 코루틴의 시작점 등이 필요함
- @Composable된 Composable 함수도 통합점이 필요
- 표준 함수가 아래와 같은 특성을 가지게 됨
- restartable
- skippable
- reactive
- inline 연산자는 람다식을 간접 호출이 없는 것처럼 효과적으로 만듬
- e.g. foreach
Composable 함수 타입
- Composable 함수가 Kotlin의 표준 함수와 다른 타입으로 간주되는 이유
- @Composable은 런 타임 시 Composable 함수의 유효성을 검사하고 사용하는 방법을 변경
기본 타입 @Composable (A) -> B
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
composable("${name}님")
}
val composable: @Composable (String) -> Unit = {
Text(it)
}
@Composable Scope.() ‐> A 형태
@Composable
inline fun Box(
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.TopStart,
propagateMinConstraints: Boolean = false,
content: @Composable BoxScope.() -> Unit
) {
val measurePolicy = rememberBoxMeasurePolicy(contentAlignment, propagateMinConstraints)
Layout(
content = { BoxScopeInstance.content() },
measurePolicy = measurePolicy,
modifier = modifier
)
}
- 특정 Composable로만 정보 범위를 지정하는 데 자주 사용
반응형
'Android > Compose' 카테고리의 다른 글
[안드로이드] Inner Shadow, Drop Shadow 처리하는 방법 (1) | 2024.06.10 |
---|---|
[안드로이드] Jetpack Compose UI Test 맛보기 (0) | 2024.05.12 |
[안드로이드] Compose 밑줄 텍스트와 클릭 가능하게 만들기 (0) | 2024.05.04 |
[안드로이드] Composable 함수 알아보기 (0) | 2024.03.31 |
[안드로이드] Compose의 CompositionLocal 알아보기 (0) | 2024.03.21 |