앱 UI를 구성하는 TextView, Button 등 컴포넌트 사이사이에는 의도적으로 여백을 넣어요. 붙어 있으면 답답한 느낌을 주고 유저에게 강조하고 싶은 UI가 한눈에 들어오지 않기 때문이겠죠? (개발자의 추측 ㅎ)
XML 기반의 Android UI에서는 margin 혹은 padding으로 여백을 구현했었습니다. 그렇다면 새로운 UI Kit인 Compose에서는 어떻게 구현할까요? 바로 Modifier.padding이랑 Space Composable을 사용하면 됩니다 :)
Padding, Spacer
@Composable
fun MyComponent(
displayText: String
) {
Text(
text = displayText,
modifier = Modifier.padding(bottom = 16.dp)
)
}
padding은 Composable의 여백을 결정해 줍니다. 상하좌우 각각 여백을 따로 지정할 수도 있고, vertical, horizontal으로 한 방향에 대해서 동일한 여백을 지정할 수도 있어요 :)
xml에서 padding은 많이 사용했던 개념이기 때문에 Compose 입문자에게도 크게 어려운 기능은 아닐 거라고 믿어요!
@Composable
fun MyComponent(
displayText: String
) {
Column {
Text(displayText)
Spacer(modifier = Modifier.height(16.dp))
}
}
Space Composable은 여백을 위해 만들어졌어요. 내부 코드를 보면 정말 정해진 사이즈만큼 공간을 차지하는 것 이외에 하는 작업이 없는 것을 확인할 수 있습니다.
@Composable
@NonRestartableComposable
fun Spacer(modifier: Modifier) {
Layout({}, measurePolicy = SpacerMeasurePolicy, modifier = modifier)
}
private object SpacerMeasurePolicy : MeasurePolicy {
override fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult {
return with(constraints) {
val width = if (hasFixedWidth) maxWidth else 0
val height = if (hasFixedHeight) maxHeight else 0
layout(width, height) {}
}
}
}
modifier의 width, height 또는 size 속성에서 설정한 사이즈만큼의 빈 공간을 만들 뿐이네요.
결론적으로, 사용자 입장에서 봤을 때 두 가지 방법은 모두 동일한 결과를 보여줍니다. 그런데 Compose는 왜 2가지 방법을 제공해 줄까요? 그 이유는 이제 설명할 미묘한 차이점이 있기 때문입니다.
왼쪽은 padding을 사용하여 여백을 구현했기 때문에 티스토리 A 컴포넌트 자체의 크기가 커졌다는 것이 미묘한 차이점입니다. 이 차이점으로 인해 어떤 문제점이 발생할 수 있을까요??
컴포넌트 액션 혹은 애니메이션의 영향이 가요.
ripple 애니메이션을 가지고 있는 컴포넌트에 Top 여백이 적용된 모습이에요. 클릭할 때마다 padding으로 늘어난 여백까지 ripple 효과가 표시되는 것을 확인할 수 있어요. 또한 추가된 공간을 클릭해도 마찬가지로 ripple 효과가 보입니다. 이것은 의도하지 않은 ux이기 때문에 수정이 필요합니다.
Q. Modifier의 호출 순서를 달리하면 안 되는 건가요?
맞습니다. Modifier의 호출 순서를 달리하면 결과적으로 위 문제를 해결할 수도 있습니다. 하지만 컴포넌트를 사용할 때마다 해당 부분을 신경 쓰며 개발하기는 쉽지 않습니다.
물론, 내부에서 설정하는 padding은 문제없는 방법입니다!
Spacer Hoisting
어디서 들어본 말이죠. Compose의 상태 관리 기법인 State Hoisting이랑 비슷한 단어입니다. State Hoisting은 Comspoable의 상태를 호출자로 이동시켜 해당 Composable을 무상태로 만드는 패턴입니다.
Spacer Hoisting는 동일한 원칙을 가져가면서 다른 개념을 적용시킨 패턴입니다. 바로 Composable 간의 여백을 Composable 내부에서 관리하는 것이 아닌 호출자, 즉 부모에게 위임하는 패턴입니다.
@Composable
fun RobinhoodScreen() {
LazyColumn {
NavBar(...)
Spacer(modifier = Modifier.height(16.dp))
UpsellCard(...)
Spacer(...)
SectionHeader(...)
Spacer(...)
OfferCard(...)
Spacer(...)
EarningsSection(...)
Spacer(...)
ActionRow(...)
Spacer(...)
SectionHeader(...)
Spacer(...)
TickerRow(...)
}
}
Spacer는 여백을 구현하기 위해 만들어진 Composable 입니다. 그 목적이 명확하기 때문에 구성 요소가 캡슐화된 상태를 유지하고 사용 중인 컨텍스트의 영향을 받지 않도록 도와줍니다.
정리하면
padding이랑 Spacer 사이에는 재사용 가능성에 영향을 미치는 몇 가지 미묘한 차이에 대해 알아보았습니다. 이러한 차이는 화면의 전체 디자인에 따라 달라지기 때문에 상황에 맞는 알맞은 방법을 찾아 사용하시면 될 것 같습니다 :)
참고
'Android > Compose' 카테고리의 다른 글
[Android] Compose BackdropScaffold 적용해보기 (0) | 2023.01.19 |
---|---|
[Android] Compose PreviewParameterProvider (2) | 2022.09.22 |
[Android] Compose - Accompanist Inset (0) | 2022.09.14 |
[Android] Bottom Navigation Bar UI with Compose - (1) (0) | 2022.06.06 |
[Android] Jetpack Compose란? (0) | 2022.05.22 |