시배's Android

Compose Docs | Jetpack Compose architectural layering 본문

Android/Compose Docs

Compose Docs | Jetpack Compose architectural layering

si8ae 2023. 8. 10. 23:17
 

Jetpack Compose 아키텍처 레이어링  |  Android Developers

Jetpack Compose 아키텍처 레이어링 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 페이지에서는 Jetpack Compose를 구성하는 아키텍처 레이어와 관련 디자인에

developer.android.com

Layers

각 레이어는 낮은 수준에서부터 쌓여져 높은 수준의 구성 요소를 만들기 위한 기능을 조합합니다. 각 레이어는 하위 레이어의 공개 API를 기반으로 구축되며 모듈 간 경계를 확인하고 필요한 경우 어떤 레이어든 교체할 수 있도록 합니다. 이제 아래에서 위로 이러한 레이어들을 살펴보겠습니다.

  • Runtime (런타임) : 이 모듈은 remember, mutableStateOf, @Composable 어노테이션, SideEffect와 같은 Compose 런타임의 기본 기능을 제공합니다. 이 모듈은 Compose의 트리 관리 기능만 필요하고 UI 기능은 필요하지 않은 경우 직접 이 레이어 위에 구축할 수 있습니다.
  • UI : UI 레이어는 여러 모듈(ui-text, ui-graphics, ui-tooling 등)로 구성됩니다. 이러한 모듈은 레이아웃 노드(LayoutNode), Modifier, 입력 처리기, 사용자 정의 레이아웃 및 그리기와 같은 UI 도구킷의 기본 기능을 구현합니다. 이 레이어 위에 구축할 수 있는 시나리오는 기본적인 UI 도구킷의 개념만 필요한 경우입니다.
  • Foundation : Foundation 모듈은 디자인 시스템에 중립적인 Compose UI의 구성 요소들을 제공합니다. 예를 들어 Row 및 Column, LazyColumn, 특정 동작의 인식 등이 있습니다. 이 레이어 위에 구축하는 시나리오는 자체 디자인 시스템을 만들기 위한 경우입니다.
  • Material : Material 모듈은 Compose UI Material Design 시스템 구현을 제공하여 테마 시스템, 스타일이 적용된 컴포넌트, 리플 효과, 아이콘 등을 제공합니다. 앱에서 Material Design 사용할 레이어 위에 구축합니다.

Design Principles

Jetpack Compose 지침 원칙 하나는 가지 거대한(monolithic) 컴포넌트가 아닌 작고 중점을 기능 조각을 제공하여 이를 함께 조합(또는 구성) 있도록 하는 것입니다. 접근 방식은 여러 가지 이점을 가지고 있습니다.

Control

더 높은 수준의 컴포넌트는 대신 더 많은 작업을 수행하지만 직접적인 제어량을 제한할 수 있습니다. 더 많은 제어가 필요한 경우 하위 수준 컴포넌트를 사용해 "하향식"으로 이동할 수 있습니다.

예를 들어, 컴포넌트의 색상을 애니메이션화하려면 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를 사용하면 더 복잡하지만 더 많은 제어가 가능합니다. 필요에 가장 적합한 추상화 수준을 선택하세요.

Customization

높은 수준의 구성 요소를 작은 구성 요소로 조합하는 것은 필요할 경우 컴포넌트를 쉽게 사용자 정의할 있게 해줍니다. 예를 들어, Material 레이어에서 제공하는 Button 구현을 살펴보겠습니다:

@Composable
fun Button(
    // …
    content: @Composable RowScope.() -> Unit
) {
    Surface(/* … */) {
        CompositionLocalProvider(/* … */) { // set LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                Row(
                    // …
                    content = content
                )
            }
        }
    }
}

Button은 다음 4개의 구성 요소를 조합하여 만듭니다:

  • 배경, 모양, 클릭 처리 등을 제공하는 material Surface
  • 버튼이 활성화되거나 비활성화될 때 콘텐츠의 알파 값을 변경하는 CompositionLocalProvider
  • 기본 텍스트 스타일을 설정하는 ProvideTextStyle
  • 버튼 콘텐츠의 기본 레이아웃 정책을 제공하는 Row

위 구조를 명확하게 하기 위해 일부 매개변수와 주석은 생략되었지만, 이 전체 컴포넌트는 이 4개의 구성 요소를 단순히 조합하여 버튼을 구현하는 데 사용되므로 전체 코드는 약 40줄 정도입니다. Button과 같은 컴포넌트는 사용자 정의를 위해 노출하는 매개변수를 선택하면서 매개변수의 폭발적인 증가를 방지하고 컴포넌트를 더 사용하기 어렵게 만드는 문제를 균형잡는 의견이 포함되어 있습니다. 예를 들어 Material 컴포넌트는 Material Design 시스템에서 지정된 사용자 정의를 제공하여 Material Design 원칙을 따르는 것을 쉽게 만들 수 있습니다.

그러나 컴포넌트의 매개변수를 넘어 사용자 정의를 수행하려면 낮은 수준으로 "하향식"으로 이동하여 컴포넌트를 포크(fork)해야 있습니다. 예를 들어, 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의 개념을 계속 사용합니다. 그러나 material Surface를 Row로 대체하고 원하는 모양을 얻기 위해 스타일을 적용합니다.

만약 자체 고유한 디자인 시스템을 구축하려는 경우와 같이 전혀 material 개념을 사용하지 않으려면, foundation 레이어 구성 요소만 사용하도록 "하향식"으로 내려갈 있습니다.

@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) 중에서 더 나은 출발점을 제공하는 경우도 있을 수 있습니다.

일반적으로 필요한 기능을 제공하는 가장 높은 수준의 컴포넌트 위에 구축하는 것이, 그들이 포함하는 모범 사례의 혜택을 얻기 위한 선호되는 방법입니다.

'Android > Compose Docs' 카테고리의 다른 글

Compose Docs | Semantics in Compose  (0) 2023.08.13
Compose Docs | Performance  (0) 2023.08.13
Compose Docs | Jetpack Compose Phases  (0) 2023.08.10
Compose docs | Side-effects in Compose  (0) 2023.08.08
Compose Docs | Modifiers  (0) 2023.08.07