안드로이드 시스템·Compose 용어 사전
ComposeLocalContext · staticCompositionLocalOf · compositionLocalOf

CompositionLocal

컴포지션 트리를 따라 값을 암묵적으로 전달하는 통로. 남용하면 의존이 감춰진다.

컴포지션 트리를 따라 값을 암묵적으로 전달하는 통로.

val LocalSpacing = staticCompositionLocalOf { Spacing() }

CompositionLocalProvider(LocalSpacing provides Spacing(large = 24.dp)) {
    MyScreen()          // 트리 어디서든 LocalSpacing.current 로 읽는다
}

@Composable fun DeepChild() {
    Box(Modifier.padding(LocalSpacing.current.large))
}

이미 쓰고 있는 것들

  • LocalContext — 안드로이드 Context
  • LocalConfiguration — 화면 크기 · 방향 · 다크모드
  • LocalDensity — dp ↔ px 변환
  • LocalLifecycleOwner — 생명주기
  • LocalFocusManager — 포커스 제어
  • MaterialTheme — 내부적으로 CompositionLocal 로 구현돼 있다

두 가지 생성 함수

  • compositionLocalOf — 값이 자주 바뀐다

    • 읽는 곳만 무효화한다 (추적 비용이 있다)
  • staticCompositionLocalOf — 값이 거의 안 바뀐다

    • 바뀌면 Provider 아래 전체를 다시 그린다
    • 대신 읽기가 싸다
  • 테마 · 간격처럼 사실상 고정인 값 → static

언제 쓰지 않나

❌ ViewModel 이나 Repository 를 CompositionLocal 로 내려보낸다

  • 의존성이 시그니처에 안 보인다
  • 미리보기와 테스트에서 Provider 를 빠뜨려 크래시한다
  • 어떤 값을 받는지 호출부가 모른다

✅ 파라미터로 명시적으로 넘긴다

기준: "트리 대부분이 쓰고, 중간 계층이 신경 쓸 필요가 없는 횡단 관심사인가?"

  • 테마 · 간격 · 밀도 · 접근성 설정 → 예
  • 화면 데이터 · 콜백 → 아니오

기본값이 없으면 크래시한다

val LocalUser = staticCompositionLocalOf<User> {
    error("LocalUser 가 제공되지 않았습니다")
}

Provider 를 감싸지 않고 읽으면 이 예외가 난다 @Preview 에서 특히 자주 터진다 — 미리보기용 Provider 를 함께 만들어 둔다

View interop

// Compose 안에 View 를 넣는다
AndroidView(
    factory = { ctx -> MapView(ctx) },
    update = { it.setCenter(lat, lng) }        // 리컴포지션마다 불린다
)

// View 안에 Compose 를 넣는다
ComposeView(context).apply {
    setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)   // ★ 누수 방지
    setContent { MyComposable() }
}

면접 함정

  • "CompositionLocal은 전역 변수와 같다" → 트리 범위다. 같은 Local이 위치에 따라 다른 값일 수 있다.
  • AndroidViewfactory에서 상태를 읽는다factory는 한 번만 불린다. 갱신은 update에서 한다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — Compose 심화 — CompositionLocal·제스처·interop·접근성