개요
컴포즈를 쓰면서, 상태 호이스팅이라는 개념을 사용하면서, 하위 컴포저블에서 사용하는 데이터를 상위 컴포저블에서 매개변수로 내려주는게 은근 불편하다(?)고 느끼고 있는 중이다. 그러던 도중 CompositionLocal에 대해 알게되었고, 학습한 내용을 정리해보려고 한다.
1. CompositionLocal 등장
CompositionLocal에 대해 적기 전 알고 넘어야하는 것이 있다! 바로 컴포저블 함수는 위의 그림처럼 트리 구조로 구성된다는 점이다. 이런 구조로 되어 있어 우리는 상태 호이스팅을 위해 하위 컴포저블에서 쓰이는 UI 상태를 상위 컴포저블에서 매개변수로 내려주는 방식을 사용한다.
하지만! 트리의 깊이가 매우 깊어진다면? 그 데이터가 필요한 컴포저블까지 매개변수를 계속해서 내려주는 방식은 너무 비효율적이라고 생각한다.
이러한 이유 때문에 CompositionLocal이 등장했다고 생각한다.
CompoistionLocal
CompositionLocal은 위의 그림에서 보는 것처럼, 상위 컴포저블이 특정 데이터를 제공할 때 그 하위에 있는 모든 컴포저블이 해당 데이터에 접근이 가능하도록 하는 것이다.
예를 들자면, 테마 색상을 상위 컴포저블에서 CompositionLocal을 통해 제공하면, 하위의 있는 컴포저블은 별도의 매개변수를 통한 데이터 전달 없이도 지정된 색상을 사용할 수 있다.
2. CompositionLocal의 사용법
CompositionLocal의 사용법은 크게 두 단계만 기억하면 된다.
1) CompositionLocal 제공 -> 상위 컴포저블
2) CompositionLocal 소비 -> 하위 컴포저블
상위 컴포저블에서는 CompositionLocal을 제공하면 되고, 하위 컴포저블에서는 CompositionLocal을 사용하기만 하면 된다. 제공하는 법, 소비하는 법에 대해 알아보기 전에 CompositionLocal은 2개로 정의할 수 있다.
먼저, staticCompositionLocalOf를 통해 정의할 수 있다.
val LocalStaticValue = staticCompositionLocalOf {"기본값"}
얘의 경우 제공되는 값이 컴포지션 동안 변하지 않는다. 즉, 값이 고정되어 있어서 리컴포지션 될때 불필요하게 업데이트를 방지할 수 있다.
테마 색상, 폰트 스타일 등과 같이 변경되지 않는 값에 사용한다!
다음으로, compositionLocalOf를 통해 정의할 수 있다.
val LocalDynamicValue = compositionLocalOf { "변할 수 있는 값" }
얘는 그럼 반대로, 값이 변할 수 있어 얘를 소비하는 컴포저블에서는 UI가 재구성된다.
사용자의 입력에 따라 변하는 데이터, 애니메이션 상태 등 변경될 수 있는 값에 사용된다!
위에 나온 정의는 커스텀으로 사용할 때 사용하는 것으로,
LocalColors, LocalContentAlpha, LocalIndication 등 기본적으로 안드로이드에서 제공되는 CompositionLocal 또한 있다.
CompositionLocal 제공
상위 컴포저블에서 CompositionLocal을 제공하기 위해 CompositionLocalProvider 라는 것을 사용해야 한다.
코드를 통해 보자
@Composable
fun CompositionLocalExample() {
Column {
Text("상위 컴포저블")
CompositionLocalProvider(LocalColor provides Color.Red) {
Text(
modifier = Modifier.background(LocalColor.current),
text = "컴포저블"
)
CompositionLocalProvider(LocalColor provides Color.Blue) {
DescendantExample()
}
}
}
}
@Composable
fun DescendantExample() {
Text(
modifier = Modifier.background(LocalColor.current),
text = "하위컴포넌트"
)
}
CompositionLocalProvider로 감싸진 컴포저블이 각각 정의된 대로 배경색이 변경된 것을 알 수 있다.
이때 provides라는 중위 함수를 통해 왼쪽에는 안드로이드에 있는, 또는 사용자 정의에 CompositionLocal 객체를 넣으면 되고, 오른쪽에는 데이터를 넣어준다.
CompositionLocal 소비
제공받은 CompositionLocal에 대해 위의 코드와 같이 current를 통해 데이터를 받을 수 있다!
생각보다 간단하네?
참고
아래 코드와 같이 하나의 CompositionLocalProvider 내에서 여러 CompositionLocal을 제공할 수 있다고 한다.
@Composable
fun AppTheme(
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalColor provides Color.Red,
LocalContentColor provides Color.Black,
) {
content()
}
}
CompositionLocal의 사용은 상위 컴포저블에서 데이터를 넘겨주고 받기 때문에 정확하게 어떤 데이터를 어디서 주는지 헷갈리게 될 경우가 있다. 그렇기에 신중하게 사용하며, 때에 따라서는 명시적으로 매개 변수를 넘겨주는게 편할 수가 있다.
정리하자면, CompositionLocal을 통해 깊이가 있는 구조에서 데이터를 효율적으로 전달할 수 있지만, 적절한 상황에서 사용하는 것이 좋다!
'Android > Android Compose' 카테고리의 다른 글
[Android] Compose - (6) 부수효과 첫 번째 (0) | 2024.11.12 |
---|---|
[Android] Compose - (5) Navigation (1) | 2024.10.23 |
[Android] Compose - (4) 컴포저블 수명 주기 (2) | 2024.10.22 |
[Android] Compose - (3) 상태를 호이스팅할 대상 위치 (1) | 2024.08.19 |
[Android] Compose UI - (3) 페이저(Pager) (3) | 2024.08.15 |