|

Compose adaptive layout은 어떻게 시작할까: 폰, 태블릿, 폴더블 화면을 한 구조로 다루기

Compose adaptive layout은 어떻게 시작할까: 폰, 태블릿, 폴더블 화면을 한 구조로 다루기
Compose adaptive layout을 window size class, navigation rail, list-detail 구조로 시작하는 방법을 정리합니다. 폰, 태블릿, 폴더블 화면을 한 코드 흐름에서 나누는 기준을 설명합니다.

Compose adaptive layout은 폰, 태블릿, 폴더블마다 앱을 따로 만드는 일이 아닙니다. 같은 기능을 유지하되 화면 공간이 달라질 때 navigation, 목록, 상세 영역의 배치를 조정하는 방식입니다.

처음 시작할 때 핵심은 화면 크기 조건을 if문으로 흩뿌리지 않고, window size class 기준으로 UI 구조를 나누는 것입니다. 그래야 화면이 늘어날수록 코드도 같이 흐트러지는 일을 줄일 수 있습니다.

Compose adaptive layout 시작 기준 카드
Compose adaptive layout은 window size class로 화면 구조를 나누고 navigation과 content 배치를 바꾼다.

Compose adaptive layout은 왜 필요할까

폰 화면에서는 하나의 목록을 꽉 채워 보여주는 구조가 자연스럽습니다. 하지만 태블릿이나 폴더블 펼침 화면에서는 같은 UI가 너무 넓게 늘어나거나, 사용자가 목록과 상세를 오가느라 불필요하게 뒤로 가기를 반복할 수 있습니다.

adaptive layout은 화면이 넓어졌을 때 남는 공간을 의미 있게 쓰는 설계입니다. 단순히 width를 matchParent로 두는 것이 아니라, 정보 구조 자체를 화면 크기에 맞게 재배치합니다.

window size class로 첫 기준을 잡는다

window size class는 현재 창 크기를 compact, medium, expanded 같은 범주로 나눠 생각하게 해줍니다. 정확한 픽셀 하나에 맞춰 분기하기보다, UI 구조를 바꿀 만한 큰 기준으로 쓰는 편이 좋습니다.

@Composable
fun AppRoot(windowSizeClass: WindowSizeClass) {
    val isExpanded = windowSizeClass.widthSizeClass ==
        WindowWidthSizeClass.Expanded

    if (isExpanded) {
        ExpandedHomeScreen()
    } else {
        CompactHomeScreen()
    }
}

navigation은 화면 크기에 따라 달라질 수 있다

compact 화면에서는 bottom navigation이 익숙합니다. 하지만 medium이나 expanded 화면에서는 navigation rail이나 drawer가 더 자연스러울 수 있습니다. Android Developers는 adaptive navigation을 위해 NavigationSuiteScaffold 같은 도구를 제공합니다.

  • compact: bottom navigation이 손가락 접근성에 유리하다
  • medium: navigation rail로 세로 공간을 아낄 수 있다
  • expanded: drawer 또는 list-detail 구조를 함께 고려한다

list-detail 구조를 먼저 검토한다

메일, 설정, 상품 목록, 문서 목록처럼 목록과 상세가 함께 있는 앱은 큰 화면에서 list-detail 구조가 효과적입니다. 폰에서는 목록에서 상세로 이동하고, 큰 화면에서는 왼쪽 목록과 오른쪽 상세를 동시에 보여줄 수 있습니다.

compact
List Screen -> Detail Screen

expanded
List Pane | Detail Pane

상태는 화면 구조보다 위에 둔다

화면 크기에 따라 composable 구조가 바뀌어도, 선택된 item이나 필터 상태는 유지되어야 합니다. 그래서 상태는 compact/expanded 화면 내부에 따로 두기보다 ViewModel이나 공통 state holder에 두는 편이 안전합니다.

폴더블에서 추가로 볼 것

폴더블은 단순히 큰 화면만 의미하지 않습니다. 접힌 상태, 펼친 상태, hinge가 만드는 분할감, 앱 창 크기 변경을 함께 봐야 합니다. 그래서 고정된 기기 이름보다 현재 window 정보와 실제 레이아웃 공간을 기준으로 판단해야 합니다.

실무 시작 순서

  1. 앱에서 목록-상세, 탐색, 편집처럼 넓은 화면에서 달라질 흐름을 찾는다
  2. window size class를 기준으로 compact/medium/expanded 구조를 나눈다
  3. navigation bar, rail, drawer 중 화면 크기에 맞는 패턴을 고른다
  4. 큰 화면에서는 list-detail 또는 supporting pane을 검토한다
  5. 상태 보존과 back stack 흐름을 폰/태블릿에서 모두 확인한다

정리

Compose adaptive layout은 화면 크기별 대응을 나중에 덧붙이는 작업이 아니라, 앱 구조를 더 넓은 화면까지 확장 가능하게 만드는 설계입니다.

공식 기준은 Android Developers adaptive layouts 문서를 확인하는 것이 좋습니다. navigation 흐름은 Compose Navigation nested graph 글과 함께 보면 연결됩니다.


보강: 화면 크기별로 무엇을 바꿀지 먼저 정한다

adaptive layout을 시작할 때 가장 흔한 실수는 width가 넓어졌다는 이유만으로 모든 composable에 분기문을 넣는 것입니다. 그렇게 하면 화면 하나를 고칠 때 compact, medium, expanded 조건을 모두 따라가야 합니다.

먼저 바꿀 대상을 세 단계로 나누면 훨씬 안전합니다. 첫째는 navigation 위치, 둘째는 content pane 개수, 셋째는 세부 컴포넌트 밀도입니다. 이 세 가지를 한꺼번에 바꾸려고 하면 구조가 빨리 복잡해집니다.

  • navigation: bottom bar, navigation rail, drawer 중 무엇을 쓸지
  • content pane: 목록만 보여줄지, 목록과 상세를 함께 보여줄지
  • component density: 카드 폭, grid column 수, padding, typography를 어떻게 조정할지

compact, medium, expanded를 기능 단위로 읽기

compact는 폰 하나의 세로 화면을 기준으로 생각하면 됩니다. 한 번에 하나의 주요 작업에 집중하게 하고, 상세 화면은 navigation으로 들어가는 구조가 자연스럽습니다.

medium은 태블릿 세로 모드나 폴더블의 중간 상태를 떠올릴 수 있습니다. 이때는 navigation rail을 검토하되, 모든 화면을 2-pane으로 만들 필요는 없습니다. 목록이 길고 상세가 중요한 화면부터 우선 적용합니다.

expanded는 화면이 충분히 넓어 목록과 상세, 또는 본문과 보조 정보를 함께 보여줄 수 있는 상태입니다. 이때 핵심은 빈 공간을 채우는 것이 아니라 사용자의 왕복 이동을 줄이는 것입니다.

Compose 코드에서 분기 위치를 줄이는 방법

분기문은 화면 최상위 조립 지점에 모으는 편이 좋습니다. 세부 row나 card 안에서 window size class를 계속 읽으면 작은 UI 변경도 전체 정책 변경처럼 퍼집니다.

@Composable
fun InboxRoute(
    windowSizeClass: WindowSizeClass,
    state: InboxUiState,
    onMessageClick: (MessageId) -> Unit
) {
    when (windowSizeClass.widthSizeClass) {
        WindowWidthSizeClass.Compact -> {
            InboxCompactScreen(state, onMessageClick)
        }
        else -> {
            InboxListDetailScreen(state, onMessageClick)
        }
    }
}

이 구조에서는 compact와 expanded가 다른 화면 조립 방식을 갖지만, state와 event는 같은 모델을 공유합니다. 그래서 화면 크기가 바뀌어도 업무 흐름은 유지됩니다.

테스트할 때 꼭 보는 장면

  1. 목록에서 item을 선택한 뒤 화면을 넓혔을 때 상세가 유지되는지
  2. 큰 화면에서 선택된 item이 없는 초기 상태가 어색하지 않은지
  3. 뒤로 가기 동작이 compact와 expanded에서 모두 자연스러운지
  4. 폴더블 접힘/펼침 또는 창 크기 변경 후 scroll 위치가 과하게 튀지 않는지
  5. navigation rail과 bottom navigation이 같은 destination 상태를 공유하는지

자주 생기는 설계 실수

  • 모든 화면을 억지로 2-pane으로 만든다
  • window size class를 하위 composable 곳곳에서 직접 읽는다
  • 폰에서는 필요한 뒤로 가기 흐름을 큰 화면에서 그대로 유지해 화면이 답답해진다
  • 상태를 화면별 composable 내부에 두어 크기 변경 때 선택 상태가 사라진다
  • 태블릿 화면에서 카드 폭만 늘리고 정보 구조는 바꾸지 않는다

함께보면 좋은 글