Jetpack Compose의 CompositionLocal
Jetpack Compose의 CompositionLocal
CompositionLocal은 Jetpack Compose에서 컴포지션(Composition) 트리를 따라 데이터를 암시적으로 전달하는 메커니즘입니다. 매번 컴포저블 함수의 매개변수로 데이터를 일일이 전달하지 않아도, 테마, 설정 정보, 플랫폼 의존성 등 여러 컴포저블에서 필요로 하지만 명시적으로 받을 필요가 없는 값에 대해 범위가 지정된(scoped) 접근 방식을 제공합니다. 이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.
CompositionLocal이 컴포지션 트리를 통해 값을 전파하는 방식compositionLocalOf와staticCompositionLocalOf의 차이점CompositionLocal값을 올바르게 제공하고 소비하는 방법CompositionLocal을 사용하기에 적합한 상황과, 직접 매개변수를 전달하는 것이 더 나은 경우
CompositionLocal 정의하기
CompositionLocal은 compositionLocalOf 또는 staticCompositionLocalOf를 사용하여 생성할 수 있습니다. 두 함수 모두 컴포지션 트리에서 별도의 제공자(provider)가 없을 때 사용되는 기본값 팩토리(default value factory)를 필수로 요구합니다.
val LocalUserName = compositionLocalOf { "Guest" }
네이밍 컨벤션(naming convention)으로 Local 접두사를 사용하는데, 이는 해당 값이 매개변수로 직접 전달되는 것이 아니라 컴포지션 트리를 통해 제공된다는 것을 나타냅니다. 기본값은 컴포저블이 CompositionLocal을 읽었지만, 상위 컴포저블에서 CompositionLocalProvider를 통해 값을 제공하지 않은 경우에 반환됩니다.
실무에서
Local접두사는 Compose 팀의 공식 네이밍 컨벤션이므로, 커스텀CompositionLocal을 정의할 때에도 반드시 이 규칙을 따르시는 것이 좋습니다.
값 제공하기
CompositionLocalProvider를 사용하면 특정 범위(scope) 내에서 값을 제공할 수 있습니다. 해당 범위 안에 있는 모든 하위 컴포저블은 제공된 값을 읽을 수 있습니다.
@Composable
fun GreetingScreen() {
CompositionLocalProvider(
LocalUserName provides "skydoves"
) {
UserGreeting()
}
}
@Composable
fun UserGreeting() {
val userName = LocalUserName.current
Text("Hello, $userName!")
}
UserGreeting은 별도의 매개변수를 받지 않고도 LocalUserName.current를 통해 값을 읽습니다. 만약 GreetingScreen에서 값을 제공하지 않았다면, 정의 시 설정한 기본값인 "Guest"가 사용됩니다.
CompositionLocalProvider를 통해 제공된 값은 후행 람다(trailing lambda)로 정의된 컴포저블 하위 트리에만 범위가 한정됩니다. 해당 범위 바깥에서는 이전에 제공된 값이나 기본값이 적용되며, 이러한 범위 지정 메커니즘 덕분에 트리의 서로 다른 부분에서 동일한 CompositionLocal에 대해 서로 다른 값을 참조할 수 있습니다.
면접에서
CompositionLocal의 범위 개념에 대해 질문을 받으시면, "제공된 값은CompositionLocalProvider의 후행 람다 안에서만 유효하며, 트리의 서로 다른 위치에서 중첩 제공을 통해 값을 오버라이드할 수 있습니다"라고 설명하시면 좋습니다.