
์ฑ์ ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๋์ ์ ๊น์ ๊ณต๋ฐฑ์ด ์๊น๋๋ค.
๋ง์ฝ ์ด๋ ํ๋ฉด์ด ํ
๋น์ด ์๋ค๋ฉด ์ฌ์ฉ์๋ ๋ถ์ํด์ง๋๋ค.
"์ฑ์ด ๋ฉ์ถ ๊ฑด๊ฐ?" ๋ผ๋ ์๋ฌธ์ ๊ฐ๊ฒ ๋์ฃ .
๊ทธ๋์ ๋ณดํต์ ProgressBar ๊ฐ์ ์คํผ๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ํ์ง๋ง ์คํผ๋๋ง ๋๊ณ ์์ผ๋ฉด ์ฌ์ฉ์๋ ๋จ์ํ "๋ก๋ฉ ์ค์ด๋ค" ๋ผ๋ ์ฌ์ค๋ง ์ ๋ฟ,
๐ ๋ฌด์์ ๊ธฐ๋ค๋ฆฌ๊ณ ์๋์ง๋ ์ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ ๋ฑ์ฅํ๋ ๊ฒ Skeleton Loading(์ค์ผ๋ ํค ๋ก๋ฉ)์ ๋๋ค.
๐ก Skeleton Loading์ด๋?
์ค์ ์ฝํ ์ธ ์ ๋ ์ด์์์ ํ์ ๋ธ๋ก ๋ฑ์ผ๋ก ๋ฏธ๋ฆฌ ๊ทธ๋ ค๋๊ณ ,
๊ทธ ์์ ๋น์ด ํ๋ฌ๊ฐ๋ ๋ฏํ ์ ๋๋ฉ์ด์ ์ ๋ฃ์ด
์ฌ์ฉ์๊ฐ "๊ณง ์ด ์๋ฆฌ์ ์ฝํ ์ธ ๊ฐ ๋ํ๋๊ฒ ๊ตฌ๋" ๋ผ๊ณ ๋๋ผ๊ฒ ํ๋ ๊ธฐ๋ฒ์ ๋๋ค.
์ฆ, UX์ ์ผ๋ก ์ฌ์ฉ์์๊ฒ ๋ฐ์ดํฐ์ ๋งฅ๋ฝ๊ณผ ๊ธฐ๋๊ฐ์ ์ฃผ๋ ๋ฐฉ์์ ๋๋ค.
์ ๋ ์ด๋ฒ์ Modifier ํ์ฅ์ ํ์ฉํด์
์์ฃผ ๊ฐ๋จํ shimmer ์ ๋๋ฉ์ด์
์ ์ ์ฉํ ์ ์๋ SkeletonBox๋ฅผ ๋ง๋ค์์ต๋๋ค.
@Composable
fun SkeletonBox(
modifier: Modifier = Modifier,
content: @Composable () -> Unit = {}
) {
Box(
modifier = modifier.shimmerBackground()
) {
content()
}
}
์ฌ๊ธฐ์๋ Box์ Modifier.shimmerBackground()๋ฅผ ๋ถ์ฌ์
๋ด๋ถ ์ฝํ
์ธ ๊ฐ ์์ด๋ ๋ฐ์ง์ด๋ ๋ฐฐ๊ฒฝ์ ๋ณด์ฌ์ค ์ ์๊ฒ ํฉ๋๋ค.
ํต์ฌ์ Modifier.shimmerBackground() ์
๋๋ค ๐
@SuppressLint("SuspiciousModifierThen")
fun Modifier.shimmerBackground(): Modifier = composed {
// 1) ๋ฌดํ ์ ๋๋ฉ์ด์
์ปจํ
์ด๋ ์์ฑ
val transition = rememberInfiniteTransition(label = "shimmerBackground")
// 2) 0f โ 1000f ๊น์ง ์ ํ์ผ๋ก ์ด๋ํ๋ ๊ฐ์ ๋ฌดํ ๋ฐ๋ณต
val translateAnimation by transition.animateFloat(
initialValue = 0f,
targetValue = 1000f,
animationSpec = infiniteRepeatable(
animation = tween(
durationMillis = 1200,
easing = LinearEasing
),
repeatMode = RepeatMode.Restart
),
label = "shimmerTranslate",
)
// 3) ๊ทธ๋ฆฌ๊ธฐ ๋จ๊ณ: ์บ์ ๊ฐ๋ฅํ ๋ฆฌ์์ค๋ ์บ์ํ๊ณ , ๋งค ํ๋ ์ ๊ทธ๋ผ๋ฐ์ด์
์ ๊ทธ๋ ค์ค
return@composed this.then(
drawWithCache {
// 3-1) ๊ณ ์ ๋ ์ปฌ๋ฌ ๋ฆฌ์คํธ๋ ์บ์
val shimmerColors = listOf(
Color.LightGray.copy(alpha = 0.9f),
Color.LightGray.copy(alpha = 0.5f),
Color.LightGray.copy(alpha = 0.9f)
)
// 3-2) ์ค์ ๊ทธ๋ฆฌ๊ธฐ: ์ฝํ
์ธ "๋ค"์ ์ฌ๊ฐํ์ ๊ทธ๋ผ๋ฐ์ด์
๋ธ๋ฌ์๋ก ์ฑ์
onDrawBehind {
// 3-2-1) ๋๊ฐ์ (์ข์โ์ฐํ)์ผ๋ก ํ๋ฅด๋ ์ ํ ๊ทธ๋ผ๋ฐ์ด์
val brush = Brush.linearGradient(
colors = shimmerColors,
start = Offset.Zero,
end = Offset(x = translateAnimation, y = translateAnimation)
)
// 3-2-2) ํ์ฌ ๋ ์ด์์ ์์ญ ์ ์ฒด๋ฅผ ์ฑ์
drawRect(
brush = brush,
size = this.size
)
}
}
)
}
composed { ... } ๋ฅผ ์ฐ๋ ์ด์ remember/์ ๋๋ฉ์ด์
์ํ๋ฅผ ์์ ํ๊ฒ ์ฐ๊ธฐ ์ํ ์ง์
์ ์
๋๋ค.Modifier ์ฒด์ธ์์๋ ์ปดํฌ์ง์
์ค์ฝํ๊ฐ ์์ด remember ๊ณ์ด API๋ฅผ ์ง์ ์ฌ์ฉํ ์ ์๋๋ฐ, composed { ... } ๋ธ๋ก ์์์๋ ์ปดํฌ์ง์
์๋ช
์ฃผ๊ธฐ๋ฅผ ๊ฐ๊ฒ ๋์ด ์ํ๋ฅผ ๊ธฐ์ตํ๊ณ ์ฌ๊ตฌ์ฑ(recomposition)์ ๋ฐ์ํ ์ ์์ต๋๋ค.rememberInfiniteTransition, animate* ๊ฐ์ Compose Runtime ์์กด API๋ฅผ Modifier ๊ตฌํ ์์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.rememberInfiniteTransition + animateFloatrememberInfiniteTransition()์ ๋ฌดํ ๋ฐ๋ณต ์ ๋๋ฉ์ด์
์ปจํ
์ด๋์
๋๋ค. ์ด ์ปจํ
์ด๋์ ์ฌ๋ฌ ํธ๋(animateFloat, animateColor ๋ฑ)์ ๋ฑ๋กํ๋ฉด ๊ฐ๊ฐ์ด ๋
๋ฆฝ์ ์ผ๋ก ๋ฐ๋ณต๋ฉ๋๋ค.animateFloat(0f โ 1000f)๋ก ๊ฐ์ ์ ํ(LinearEasing)์ผ๋ก ์ฆ๊ฐ์ํค๊ณ , RepeatMode.Restart๋ก ๋์์ ๋ค์ ์ฒ์์ผ๋ก ์ ํํด ์ฌ์ํฉ๋๋ค.translateAnimation)์ ๊ทธ๋ผ๋ฐ์ด์
์ ์ข
์ ์ขํ๋ก ์ฌ์ฉํด, ์๊ฐ์ ์ผ๋ก ๋น์ด ํ๋ฌ๊ฐ๋ ๋๋์ ๋ง๋ญ๋๋ค.drawWithCache + onDrawBehinddrawWithCache๋ ๊ทธ๋ฆฌ๊ธฐ ๋ฆฌ์์ค์ ์บ์ ์ง์ ์ ์ ๊ณตํฉ๋๋ค. ์ปฌ๋ฌ ๋ฆฌ์คํธ๋ ๋ธ๋ฌ์ ๊ณ์ฐ ๋ฑ ๋งค ํ๋ ์ ๋์ผํ ๊ฐ์ ์บ์ํ๊ณ , ์
๋ ฅ์ด ๋ฐ๋ ๋๋ง ์ฌ๊ณ์ฐํด ํผํฌ๋จผ์ค ๋น์ฉ์ ์ค์
๋๋ค.onDrawBehind๋ ์ฝํ
์ธ ๋ค์ชฝ ๋ ์ด์ด์ ๊ทธ๋ฆฝ๋๋ค. ์ฆ, ์ค์ผ๋ ํค์ ๋ฐฐ๊ฒฝ์ฒ๋ผ ํ๋ฅด๊ฒ ํ ์ ์์ด ๋ ์ด์์/ํฐ์น ์์ญ์ ํด์น์ง ์์ต๋๋ค.onDrawWithContent์์ drawContent() ์ดํ์ ์ค๋ฒ๋ ์ด๋ฅผ ๊ทธ๋ฆฌ๋ฉด ๋ฉ๋๋ค.)Brush.linearGradient์ ์ขํstart: Offset, end: Offset ๋ ์ ์ผ๋ก ๋ฐฉํฅ๊ณผ ๊ธธ์ด๊ฐ ์ ํด์ง๋๋ค.start = Offset.Zero, end = Offset(x = translate, y = translate)์ด๋ฉด ์ข์๋จ โ ์ฐํ๋จ(๋๊ฐ์ ) ์ผ๋ก ํ๋ฅด์ฃ .alpha 0.9 โ 0.5 โ 0.9)๋ก ๋๋ฉด ๊ฐ์ด๋ฐ๊ฐ ํ์ด๋ผ์ดํธ์ฒ๋ผ ๋ณด์ด๋ฉฐ, ์ด๋ํ๋ ์ข
์ ์ขํ์ ๊ฒฐํฉ๋์ด ์ค๋จธ(Shimmer) ํจ๊ณผ๊ฐ ๋ฉ๋๋ค.composed ์์์ ๋ฌดํ ์ ๋๋ฉ์ด์
์ปจํ
์ด๋๋ฅผ remember ํ๋ค. animateFloat๋ก 0โ๋ชฉํ๊ฐ๊น์ง ๋ฐ๋ณต ์ฆ๊ฐํ๋ ์งํ ๊ฐ์ ๋ง๋ ๋ค. drawWithCache๋ก ๋ถ๋ณ ๋ฆฌ์์ค๋ฅผ ์บ์ํ๊ณ , ๋งค ํ๋ ์ onDrawBehind์์ ๊ทธ๋ผ๋ฐ์ด์
์ ๊ทธ๋ฆฐ๋ค. onDrawBehind๋ก ๋ฐฐ๊ฒฝ ๋ ์ด์ด์์ ๋์ โ ๋ ์ด์์/ํฐ์น์ ๊ฐ์ญ ์์. drawWithCache๋ก ๋ถํ์ ํ ๋น/๊ณ์ฐ ์ต์ํ, ์ ๋๋ฉ์ด์
์ Compose ๋ฐํ์์ด ๊ด๋ฆฌ.
์ ์ฌ์ง๊ณผ ๊ฐ์ Skeleton Loading ์ ์ฉ์ด ๊ฐ๋ฅํฉ๋๋ค.
Modifier ํ์ฅ๋ง์ผ๋ก ์์ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค. ๐ฌ ์ฌ๋ฌ๋ถ์ ๋ก๋ฉ UI๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ์๋์?
๐ก Skeleton Loading์ ๋จ์ํ ๋ก๋ฉ ํ์ ์ด์์ ๊ฐ์น๋ฅผ ์ ๊ณตํฉ๋๋ค.
โก Jetpack Compose์ Modifier ํ์ฅ์ ํ์ฉํ๋ฉด ๊ฐ๋จํ๋ฉด์๋ โจ ์ผ๊ด๋ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ง๋ค ์ ์์ต๋๋ค.