๐ŸŒŸ Jetpack Compose์˜ CompositionLocal ์ •๋ฆฌ

์ด์ง„์˜ยท2025๋…„ 9์›” 10์ผ
post-thumbnail

๐Ÿ‘‹ ๋“ค์–ด๊ฐ€๋ฉฐ

Jetpack Compose๋ฅผ ์“ฐ๋‹ค ๋ณด๋ฉด
โœจ Composable ํŠธ๋ฆฌ ๊นŠ์ˆ™์ด ์žˆ๋Š” ๊ณณ๊นŒ์ง€ ๊ณตํ†ต ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์–ด์š”.

์˜ˆ๋ฅผ ๋“ค์–ด, ์•ฑ ์ „์ฒด์—์„œ ์‚ฌ์šฉํ• 

  • ๐ŸŽจ Theme ์ƒ‰์ƒ
  • ๐Ÿ‘ค User ์ •๋ณด
  • ๐ŸŒ Locale ์„ค์ •

๊ฐ™์€ ๊ฒƒ๋“ค์ด์ฃ .

์ด๋Ÿด ๋•Œ ๋ฐ”๋กœ ๋“ฑ์žฅํ•˜๋Š” ๊ฒŒ ๋ฐ”๋กœ CompositionLocal์ž…๋‹ˆ๋‹ค.
์˜ค๋Š˜์€ compositionLocalOf์™€ CompositionLocalProvider๋ฅผ ์ค‘์‹ฌ์œผ๋กœ
๊ทธ ๊ฐœ๋…๊ณผ ์‚ฌ์šฉ๋ฒ•์„ ์ •๋ฆฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿš€


๐ŸŒŸ CompositionLocal์€ ๋ฌด์—‡์ผ๊นŒ?

CompositionLocal์€ Compose์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜์กด์„ฑ ์ „๋‹ฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด์—์š”.
์‰ฝ๊ฒŒ ๋งํ•ด, Context๋‚˜ ์ „์—ญ ์ƒํƒœ๋ฅผ Composable ํŠธ๋ฆฌ ์ „์ฒด์— ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

  • ๊ธฐ์กด Android View ์‹œ์Šคํ…œ์—์„œ Context๋ฅผ ์–ด๋””์„œ๋“  ๊บผ๋‚ด ์“ฐ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ
  • Compose์—์„œ๋Š” CompositionLocal์„ ํ†ตํ•ด ํŠธ๋ฆฌ ์ƒ์œ„์—์„œ ์„ ์–ธ โ†’ ํ•˜์œ„์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ‘‰ ์ค‘์š”ํ•œ ์ ์€, Composable ๊ฐ„ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๋ฉด์„œ๋„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฑฐ์˜ˆ์š”.


๐ŸŽฏ compositionLocalOf

compositionLocalOf๋Š” CompositionLocal์„ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
์ฆ‰, ์ „์—ญ์œผ๋กœ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋Š” ํ‚ค๋ฅผ ๋งŒ๋“ ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ์š”.

// User ์ด๋ฆ„์„ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ CompositionLocal ์ •์˜
val LocalUserName = compositionLocalOf<String> { error("No User Found") }

์—ฌ๊ธฐ์„œ error("No User Found")๋Š”
๋งŒ์•ฝ Provider์—์„œ ๊ฐ’์„ ์ฃผ์ง€ ์•Š์•˜๋Š”๋ฐ ์ ‘๊ทผํ•˜๋ฉด ๋ฐœ์ƒํ•  ๊ธฐ๋ณธ ๋™์ž‘์„ ์ •์˜ํ•œ ๊ฑฐ์˜ˆ์š”.


โšก staticCompositionLocalOf

compositionLocalOf์™€ ๋น„์Šทํ•˜๊ฒŒ, ์ปดํŒŒ์ผ ํƒ€์ž„์—์„œ ๊ธฐ๋ณธ๊ฐ’์„ ๋ณด์žฅํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด
staticCompositionLocalOf๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

data class BackgroundTheme(val color: Color)

val LocalBackgroundTheme = staticCompositionLocalOf {
    BackgroundTheme(color = Color.White)
}
  • staticCompositionLocalOf๋Š” ๊ธฐ๋ณธ๊ฐ’์ด ํ•ญ์ƒ ์œ ํšจํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • Theme, Typography, Color ๊ฐ™์€ ์•ฑ ์ „์—ญ Theme ์‹œ์Šคํ…œ์— ์ž์ฃผ ํ™œ์šฉ๋˜๋ฉฐ,
    BackgroundTheme์ฒ˜๋Ÿผ ๊ธฐ๋ณธ ์ƒ‰์ƒ์„ ๋ฐ˜๋“œ์‹œ ์ง€์ •ํ•ด ๋‘์–ด์•ผ ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋Ÿฐํƒ€์ž„ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์ค„์–ด๋“ค์–ด ์„ฑ๋Šฅ์ƒ ์ด์ ์ด ์žˆ์–ด ๊ถŒ์žฅ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

๐Ÿ‘‰ ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” staticCompositionLocalOf ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•˜๊ณ ,
ํŠน๋ณ„ํžˆ ๋Ÿฐํƒ€์ž„์—๋งŒ ๊ธฐ๋ณธ๊ฐ’์„ ๋‹ค๋ค„์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ํ•œํ•ด compositionLocalOf๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.


๐ŸŽฏ CompositionLocalProvider

๊ทธ๋ ‡๋‹ค๋ฉด ๊ฐ’์„ ์–ด๋–ป๊ฒŒ ๋„ฃ์„๊นŒ์š”?
๋ฐ”๋กœ CompositionLocalProvider๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

@Composable
fun UserScreen() {
    CompositionLocalProvider(LocalUserName provides "Jerry") {
        Greeting()
    }
}

@Composable
fun Greeting() {
    // LocalUserName ๊ฐ’์— ์ ‘๊ทผ
    val user = LocalUserName.current
    Text(text = "Hello, $user!")
}
  • CompositionLocalProvider๋Š” LocalUserName์— "Jerry"๋ผ๋Š” ๊ฐ’์„ ๊ณต๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.
  • Greeting()์—์„œ๋Š” LocalUserName.current๋ฅผ ํ˜ธ์ถœํ•ด ๊ทธ ๊ฐ’์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

์ฆ‰, Provider โ†’ Consumer ๊ตฌ์กฐ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ํ๋ฅด๋Š” ๊ฑฐ์˜ˆ์š”.


โœจ ์ฃผ์š” ์ด์ 

  1. ๐Ÿ—‚๏ธ Prop Drilling ์ œ๊ฑฐ
    • ๋งค๋ฒˆ Composable ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ธฐ์ง€ ์•Š๊ณ ๋„ ๋ฐ์ดํ„ฐ ๊ณต์œ  ๊ฐ€๋Šฅ
  2. ๐Ÿ”Œ ๋™์  ๊ต์ฒด
    • ํŠน์ • ์Šค์ฝ”ํ”„ ์•ˆ์—์„œ๋งŒ ๋‹ค๋ฅธ ๊ฐ’์„ ์ฃผ์ž… ๊ฐ€๋Šฅ (์˜ˆ: ๋‹คํฌ๋ชจ๋“œ, ๋‹ค๊ตญ์–ด ์ง€์›)
  3. ๐Ÿงฉ ์œ ์—ฐํ•œ ๊ตฌ์กฐ
    • Context, Theme, Local Settings ๋“ฑ์„ ์‰ฝ๊ฒŒ ์ „์—ญ์ ์œผ๋กœ ๊ด€๋ฆฌ

๐Ÿ› ๏ธ ๊ฐ„๋‹จ ์˜ˆ์‹œ: ๋‹คํฌ ๋ชจ๋“œ ํ† ๊ธ€

val LocalDarkMode = compositionLocalOf { false }

@Composable
fun App() {
    var dark by remember { mutableStateOf(false) }

    Column {
        Button(onClick = { dark = !dark }) {
            Text("Toggle Dark Mode")
        }

        CompositionLocalProvider(LocalDarkMode provides dark) {
            ThemedText()
        }
    }
}

@Composable
fun ThemedText() {
    val darkMode = LocalDarkMode.current
    Text(
        text = if (darkMode) "๐ŸŒ™ Dark Mode" else "โ˜€๏ธ Light Mode",
        color = if (darkMode) Color.White else Color.Black
    )
}
  • dark ๊ฐ’์— ๋”ฐ๋ผ Provider๊ฐ€ ๋ฐ”๋€Œ๊ณ ,
  • ThemedText()๋Š” ์ž๋™์œผ๋กœ ๋‹คํฌ/๋ผ์ดํŠธ ๋ชจ๋“œ์— ๋งž๋Š” UI๋ฅผ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค.
  • Provider๋ฅผ ๊ต์ฒดํ•˜๋ฉด ํ•ด๋‹น ์Šค์ฝ”ํ”„ ์•ˆ์˜ ๋ชจ๋“  Composable์ด ์ž๋™์œผ๋กœ ์žฌ๊ตฌ์„ฑ๋˜๋Š” ๊ฒƒ์ด Compose์˜ ํฐ ์žฅ์ ์ž…๋‹ˆ๋‹ค.

๐Ÿšฉ ์ฃผ์˜ํ•  ์ 

  • CompositionLocal์€ ํ•„์š” ์ด์ƒ ๋‚จ์šฉํ•˜๋ฉด ์ „์—ญ ๋ณ€์ˆ˜์™€ ๋‹ค๋ฅผ ๋ฐ” ์—†์Œ
  • ์ง„์งœ ๊ณตํ†ต์œผ๋กœ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ์—๋งŒ ์“ฐ๋Š” ๊ฒƒ์ด ๋ฒ ์ŠคํŠธ
  • ์ƒํƒœ(state) ์ €์žฅ๋ณด๋‹ค๋Š” ์ฝ๊ธฐ ์ „์šฉ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์— ์ ํ•ฉ
  • ๊ฐ’์ด ์–ด๋””์„œ ์ฃผ์ž…(Provider) ๋˜์—ˆ๋Š”์ง€ ์ถ”์ ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Œ
    • ํŠนํžˆ ํŠธ๋ฆฌ ๊นŠ์ˆ™ํ•œ Composable์—์„œ ์—ฌ๋Ÿฌ Provider๊ฐ€ ์ค‘์ฒฉ๋˜๋ฉด,
      ํ˜„์žฌ ์–ด๋–ค ๊ฐ’์ด ์‚ฌ์šฉ ์ค‘์ธ์ง€ ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ์–ด ๋””๋ฒ„๊น…์ด ํž˜๋“ค์–ด์ง

โœ… Best Practice

  • โœ… Theme, Locale, Config ๋“ฑ ์ „์—ญ์ ์œผ๋กœ ํ•ญ์ƒ ํ•„์š”ํ•œ ๊ฐ’ ์ „๋‹ฌ์— ์‚ฌ์šฉ
  • โœ… Prop Drilling ๋ฐฉ์ง€ (๊นŠ์€ ํŠธ๋ฆฌ ๊ตฌ์กฐ์—์„œ๋„ ๊ฐ„ํŽธํžˆ ๊ณต์œ  ๊ฐ€๋Šฅ)
  • โŒ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด๋‚˜ ์ƒํƒœ ์ €์žฅ์—๋Š” ์‚ฌ์šฉํ•˜์ง€ ๋ง ๊ฒƒ (์ „์—ญ ๋ณ€์ˆ˜ ๋‚จ์šฉ ์œ„ํ—˜)
  • โŒ ๊ฐ’์ด ์ž์ฃผ ๋ฐ”๋€Œ๋Š” ๋ฐ์ดํ„ฐ๋ณด๋‹ค๋Š” ์ฝ๊ธฐ ์ „์šฉ ์ „์—ญ ์ปจํ…์ŠคํŠธ์— ์ ํ•ฉ

๐Ÿงพ ๋งˆ๋ฌด๋ฆฌ

์˜ค๋Š˜์€ Compose์˜

  • compositionLocalOf ๐Ÿ‘‰ CompositionLocal ์ƒ์„ฑ
  • staticCompositionLocalOf ๐Ÿ‘‰ ์„ฑ๋Šฅ/์•ˆ์ •์„ฑ ํ–ฅ์ƒ๋œ CompositionLocal ์ƒ์„ฑ
  • CompositionLocalProvider ๐Ÿ‘‰ CompositionLocal ๊ฐ’ ์ฃผ์ž…

์„ ์‚ดํŽด๋ดค์Šต๋‹ˆ๋‹ค.

์ด ์กฐํ•ฉ๋งŒ ์ž˜ ์จ๋„

  • ๐ŸŽจ Theme ๊ด€๋ฆฌ
  • ๐ŸŒ Locale / ์–ธ์–ด ์„ค์ •
  • ๐Ÿ‘ค User ์„ธ์…˜ ์ •๋ณด ์ „๋‹ฌ

๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๊น”๋”ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”.


๐Ÿš€ ๊ฒฐ๋ก :
ํ•„์š”ํ•œ Context๋ฅผ ์ „์—ญ์ฒ˜๋Ÿผ ์“ฐ๋˜, Compose ์ฒ ํ•™์— ๋งž๊ฒŒ ์•ˆ์ „ํ•˜๊ณ  ์œ ์—ฐํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜์ž!

profile
Android Developer

0๊ฐœ์˜ ๋Œ“๊ธ€