이 페이지에서는 Jetpack Compose를 구성하는 아키텍처 레이어와 관련 디자인에 큰 영향을 주는 핵심 원칙을 간략하게 설명합니다.
Jetpack Compose는 단일 모놀리식 프로젝트가 아닙니다. 완전한 스택을 만들기 위해 함께 조합된 다수의 모듈로 만들어졌습니다. Jetpack Compose를 구성하는 여러 모듈을 이해하면 다음이 가능합니다.
- 적절한 수준의 추상화를 사용하여 앱 또는 라이브러리 빌드
- 보다 세부적인 제어나 맞춤설정을 위해 낮은 수준으로 '드롭다운할' 수 있는 경우 파악
- 종속 항목 최소화
레이어
Jetpack Compose의 기본 레이어는 다음과 같습니다.
그림 1. Jetpack Compose의 기본 레이어
각 레이어는 하위 수준에 기반하고, 상위 수준의 구성요소를 만들기 위해 기능을 결합합니다. 각 레이어는 하위 레이어의 공개 API를 기반으로 하여 모듈 경계를 확인하고 필요한 경우 레이어를 대체할 수 있게 해줍니다. 이러한 레이어를 아래부터 살펴보겠습니다.
- 런타임
- 이 모듈은
remember,mutableStateOf,@Composable주석,SideEffect같은 Compose 런타임의 기초를 제공합니다. UI가 아닌 Compose의 트리 관리 기능만 필요한 경우 이 레이어에 바로 빌드하는 것이 좋습니다. - UI
- UI 레이어는 여러 개의 모듈(
ui-text,ui-graphics,ui-tooling)로 구성됩니다. 그러한 모듈은LayoutNode,Modifier, 입력 핸들러, 맞춤 레이아웃, 그리기 같은 UI 툴킷의 기본 사항을 구현합니다. UI 툴킷의 기본 개념만 필요한 경우 이 레이어를 기반으로 빌드하는 것이 좋습니다. - 기초
- 이 모듈은 Compose UI에
Row,Column,LazyColumn, 특정 동작 인식 같은 디자인 시스템에 구속되지 않는 구성요소를 제공합니다. 자체 디자인 시스템을 만들 때는 기초 레이어를 기반으로 빌드하는 것이 좋습니다. - Material
- 이 모듈은 Compose UI에 Material Design 시스템의 구현을 제공하고 테마 설정 시스템, 스타일 적용된 구성요소, 물결 표시, 아이콘도 제공합니다. 앱에 머티리얼 디자인을 사용할 때는 이 레이어를 기반으로 빌드합니다.
디자인 원칙
Jetpack Compose의 기본 원칙은 몇 가지 모놀리식 구성요소를 제공하는 것보다 함께 조립(또는 구성)할 수 있는 작고 집중된 기능을 제공하는 것입니다. 이 접근방식에는 여러 가지 장점이 있습니다.
컨트롤
상위 수준의 구성요소는 자동으로 더 많은 작업을 실행하지만 개발자가 직접 제어할 수 있는 정도를 제한합니다. 더 많은 제어가 필요하면 하위 수준의 구성요소를 사용하도록 '드롭다운'하면 됩니다.
예를 들어 구성요소 색상을 애니메이션으로 표시하려면 animateColorAsState API를 사용하면 됩니다.
val color = animateColorAsState(if (condition) Color.Green else Color.Red)
그러나 구성요소를 항상 회색으로 시작해야 하는 경우에는 이 API에서 가능하지 않습니다. 대신 하위 수준의 Animatable API를 사용하도록 드롭다운하면 됩니다.
val color = remember { Animatable(Color.Gray) } LaunchedEffect(condition) { color.animateTo(if (condition) Color.Green else Color.Red) }
상위 수준의 animateColorAsState API 자체는 하위 수준의 Animatable API를 기반으로 빌드됩니다. 하위 수준의 API 사용이 좀 더 복잡하지만 보다 세부적인 제어가 가능합니다. 필요에 가장 적합한 추상화 수준을 선택하세요.
맞춤설정
작은 구성요소에서 상위 수준의 구성요소를 조합하면 필요할 때 훨씬 더 쉽게 구성요소를 맞춤설정할 수 있습니다. 예를 들어 Material 레이어에서 제공하는 Button의 구현을 살펴보겠습니다.
@Composable fun Button( // … content: @Composable RowScope.() -> Unit ) { Surface(/* … */) { CompositionLocalProvider(/* … */) { // set LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { Row( // … content = content ) } } } }
Button은 4가지 구성요소로 조합되었습니다.
배경, 도형, 클릭 처리 등을 제공하는 머티리얼
Surface버튼이 사용되거나 중지될 때 콘텐츠의 알파를 변경하는
CompositionLocalProvider사용할 기본 텍스트 스타일을 설정하는
ProvideTextStyle버튼 콘텐츠의 기본 레이아웃 정책을 제공하는
Row
구조를 좀 더 명확히 하기 위해 일부 매개변수와 주석을 생략했습니다. 하지만 전체 구성요소는 버튼 구현을 위해 이러한 4개 구성요소를 조합하는 역할만 하기 때문에 약 40개의 코드 줄에 불과합니다. Button과 같은 구성요소는 노출되는 매개변수에 관해 편향적이고, 구성요소의 사용을 더 어렵게 만들 수 있는 매개변수의 증가와 관련해 사용 설정된 일반 맞춤설정의 균형을 잡습니다. 예를 들어 Material 구성요소는 Material Design 시스템에 지정된 맞춤설정을 제공하기 때문에 Material Design 원칙을 쉽게 따를 수 있습니다.
하지만 구성요소의 매개변수 이외의 요소를 맞춤설정하려면 수준을 '드롭다운'하고 구성요소를 포크하면 됩니다. 예를 들어, 머티리얼 디자인에는 버튼 배경이 단색이어야 한다고 지정되어 있습니다. 그라데이션 배경이 필요한 경우 이는 Button 매개변수에서 지원되지 않습니다. 이 경우 Material Button 구현을 참조로 사용하고 자체 구성요소를 빌드할 수 있습니다.
@Composable fun GradientButton( // … background: List<Color>, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background( Brush.horizontalGradient(background) ) ) { CompositionLocalProvider(/* … */) { // set material LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { content() } } } }
위 구현에서는 Material의 현재 콘텐츠 알파 개념과 현재 텍스트 스타일 개념 같은 Material 레이어의 구성요소를 계속 사용하고 있습니다. 하지만 머티리얼 Surface를 Row로 대체하고 스타일을 지정하여 원하는 모양을 만듭니다.
맞춤 디자인 시스템을 빌드하는 등 Material 개념을 전혀 사용하지 않으려는 경우 기반 레이어 구성요소만 순수하게 사용하도록 드롭다운할 수 있습니다.
@Composable fun BespokeButton( // … backgroundColor: Color, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background(backgroundColor) ) { // No Material components used content() } }
Jetpack Compose는 최상위 수준의 구성요소에는 가장 단순한 이름을 예약합니다. 예를 들어 androidx.compose.material.Text는 androidx.compose.foundation.text.BasicText를 기반으로 합니다.
이렇게 하면 상위 수준을 대체하려고 할 때 자체 구현에 가장 쉽게 찾을 수 있는 이름을 제공할 수 있습니다.
정확한 추상화 선택
재사용 가능한 계층화된 구성요소를 빌드한다는 Compose 철학의 의미는 항상 하위 수준의 구성요소만 추구해서는 안 된다는 뜻을 담고 있습니다. 대다수 상위 수준 구성요소가 더 많은 기능을 제공할 뿐만 아니라 접근성 지원과 같은 권장사항을 구현하는 경우도 많습니다.
예를 들어 맞춤 구성요소에 동작 지원을 추가하려면 Modifier.pointerInput을 사용하여 처음부터 이를 빌드해도 됩니다. 하지만 이 구성요소를 기반으로 빌드된 다른 상위 수준 구성요소가 있고 이러한 구성요소가 좀 더 나은 시작점을 제공할 수 있습니다. Modifier.draggable, Modifier.scrollable, Modifier.swipeable 등이 그 예입니다.
일반적으로 최상위 수준 구성요소를 기반으로 하는 것이 좋습니다. 이러한 구성요소가 권장사항의 이점을 누리는 데 필요한 기능을 제공합니다.
자세히 알아보기
맞춤 디자인 시스템을 빌드하는 예는 Jetsnack 샘플을 참고하세요.
추천 서비스
- 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
- Jetpack Compose용 Kotlin
- 목록 및 그리드
- Compose의 부작용