반응형 레이아웃으로 UI 이전

Android 앱은 끊임없이 확장되는 기기 폼 팩터 생태계를 지원해야 합니다. 앱의 UI는 다양한 화면 크기, 다른 화면 방향, 기기 상태에 맞게 반응해야 합니다.

반응형 UI는 유연성과 연속성의 원칙을 기반으로 합니다.

유연성은 사용 가능한 공간을 최적으로 사용하고 사용 가능한 공간이 변경될 때 조정되는 레이아웃을 의미합니다. 조정은 여러 형태를 취할 수 있습니다. 즉, 단일 뷰의 크기 늘리기, 더 쉽게 액세스할 수 있도록 뷰의 위치 변경, 추가 뷰 표시/숨기기를 선택하거나, 이러한 형태들을 조합하여 사용할 수 있습니다.

연속성은 하나의 창 크기에서 다른 창 크기로 전환하는 동안 원활한 사용자 환경이 유지되는 것을 의미합니다. 사용자가 참여 중인 모든 환경이 중단 없이 계속되어야 합니다. 크기 변경에는 전체 뷰 계층 구조의 소멸과 재생성이 수반될 수 있으므로 사용자가 현재 위치나 데이터를 잃지 않는 것이 중요합니다.

피해야 할 사항

레이아웃을 결정할 때 실제 하드웨어 값을 사용하지 마세요. 고정된 값을 기반으로 결정하고 싶을 수 있지만 많은 경우 이러한 값은 UI가 사용할 수 있는 공간을 결정하는 데 유용하지 않습니다.

태블릿에서는 앱이 멀티 윈도우 모드에서 실행될 수 있으며, 이는 앱이 다른 앱과 화면을 공유한다는 의미입니다. ChromeOS에서는 앱이 크기 조절이 가능한 창에서 실행될 수도 있습니다. 폴더블 기기 또는 여러 디스플레이가 있는 기기와 같이 실제 화면이 두 개 이상 있을 수도 있습니다. 이러한 경우는 모두 실제 화면 크기와 콘텐츠 표시 방법을 결정하는 것 사이에 아무 관련이 없습니다.

다양한 크기의 앱 창을 보여주는 여러 기기
그림 1. 창 크기는 실제 기기나 디스플레이의 크기와 다를 수 있음

같은 이유로 앱을 특정 방향이나 가로세로 비율로 제한하지 마세요. 기기 자체는 특정 방향이면서 앱은 창 크기만을 기준으로 하여 다른 방향일 수 있습니다. 예를 들어 멀티 윈도우 모드 사용 시 태블릿이 가로 모드에 있지만 앱은 높이가 너비보다 크기 때문에 세로 모드일 수 있습니다.

또한 기기가 휴대전화인지 태블릿인지 확인하려고 하지 마세요. 무엇을 태블릿으로 볼 것인지에 관한 기준은 어느 정도 주관적입니다. 특정 크기나 가로세로 비율을 기준으로 하거나, 크기와 가로세로 비율의 조합을 기준으로 할 수도 있습니다. 새로운 폼 팩터가 등장함에 따라 이러한 가정이 바뀔 수 있고 이 구분은 중요하지 않게 됩니다.

위의 전략을 시도하는 대신 중단점과 창 크기 클래스를 사용하세요.

중단점 및 창 크기 클래스

화면에서 앱에 할당되는 실제 부분이 앱의 창입니다. 앱의 창은 전체 화면이나 화면 일부를 차지할 수 있으므로 앱 레이아웃에 관한 대략적인 결정을 내릴 때 창 크기를 사용합니다.

여러 폼 팩터를 설계할 때 이러한 대략적인 결정이 여러 방향으로 분기되는 기준점 값을 찾습니다. 이를 위해 Material Design 반응형 레이아웃 그리드는 너비와 높이에 중단점을 제공하므로 이 중단점을 사용하여 원시 크기를 창 크기 클래스라고 하는 별도의 표준화된 그룹에 매핑할 수 있습니다. 세로 스크롤이 보편적이기 때문에 대부분의 앱은 기본적으로 너비 크기 클래스에 중점을 둡니다. 따라서 몇 가지 중단점만 처리하여 모든 화면 크기에 맞게 최적화할 수 있습니다. 창 크기 클래스에 관한 자세한 내용은 다양한 화면 크기 지원을 참고하세요.

영구 UI 요소

Material Design 레이아웃 가이드라인에는 앱 바, 탐색, 콘텐츠의 영역이 정의되어 있습니다. 일반적으로 앱 바와 탐색은 뷰 계층 구조의 루트에 있거나 매우 가까운 영구 UI 요소입니다. '영구'라는 말은 뷰가 항상 표시된다는 의미가 아니라 다른 콘텐츠 뷰가 이동하거나 변경될 수 있는 반면 영구 뷰는 그대로 유지된다는 것을 의미합니다. 예를 들어 탐색 요소가 화면 밖에 있는 슬라이딩 창에 있을 수 있지만 이 창은 항상 제자리에 존재합니다.

영구 요소는 반응형일 수 있으며 일반적으로 창의 전체 너비나 전체 높이를 차지하므로 크기 클래스를 사용하여 요소를 배치할 위치를 결정하는 것이 좋습니다. 이렇게 하면 콘텐츠에 사용할 수 있는 공간이 표시됩니다. 다음 스니펫에서 활동은 소형 화면에 하단 앱 바를, 대형 화면에는 상단 앱 바를 사용합니다. 정규화된 레이아웃은 앞에서 설명한 대로 너비 중단점을 사용합니다.

<!-- res/layout/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- content view(s) -->

    <com.google.android.material.bottomappbar.BottomAppBar
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />
</androidx.constraintlayout.widget.ConstraintLayout>


<!-- res/layout-w600dp/main_activity.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        ... />

    <!-- content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

콘텐츠

영구 UI 요소를 배치한 후에 남는 공간을 콘텐츠에 사용합니다. 예를 들어 앱의 탐색 그래프와 함께 NavHostFragment를 사용하는 방법이 있습니다. 추가 고려사항은 반응형 UI 탐색을 참고하세요.

모든 데이터를 다양한 크기로 사용할 수 있는지 확인

오늘날 대부분의 앱 프레임워크는 UI에 기여하는 Android 구성요소(활동, 프래그먼트, 뷰)와 분리된 데이터 모델을 사용합니다. Jetpack을 사용하면 이 역할이 일반적으로 ViewModel에서 실행되며 구성 변경 후에도 유지된다는 추가 이점이 있습니다. 자세한 내용은 ViewModel 개요를 참고하세요.

다양한 크기에 맞게 조정되는 레이아웃을 구현할 때 현재 크기에 따라 다른 데이터 모델을 사용하고 싶을 수 있습니다. 그러나 이는 단방향 데이터 흐름의 원칙에 어긋납니다. 데이터는 뷰 방향으로 아래로 흘러야 하고 사용자 상호작용과 같은 이벤트는 위로 흘러야 합니다. 데이터 모델이 UI 레이어의 구성에 종속되는 방식으로 반대 방향의 종속 항목을 만들면 흐름이 매우 복잡해집니다. 앱의 크기가 변경되면 한 데이터 모델에서 다른 데이터 모델로의 변환을 고려해야 합니다.

대신 데이터 모델이 최대 크기의 클래스를 수용하도록 한 다음 현재 크기 클래스에 맞게 UI에서 콘텐츠를 선택적으로 표시하거나 숨기거나 재배치할 수 있습니다. 다음은 크기 클래스 간 전환 시 레이아웃의 동작 방식을 결정할 때 사용할 수 있는 몇 가지 전략입니다.

콘텐츠 확장

표준 레이아웃: 피드

확장된 공간은 콘텐츠를 확대하고 더 쉽게 액세스할 수 있도록 콘텐츠 형식을 다시 지정하는 기회가 될 수 있습니다.

항목 모음을 확대합니다. 대부분의 앱은 스크롤 컨테이너에 RecyclerView 또는 ScrollView 같은 항목 모음을 표시합니다. 컨테이너가 자동으로 커지도록 설정하면 더 많은 콘텐츠가 표시될 수 있습니다. 하지만 컨테이너 내의 콘텐츠가 과도하게 늘어나거나 왜곡되지 않도록 주의하세요. 예를 들어 RecyclerView를 사용하는 경우 너비가 크다면 GridLayoutManager, StaggeredGridLayoutManager, FlexboxLayout 같은 다른 레이아웃 관리자를 사용하는 것이 좋습니다.

기기가 접힌 상태와 펼친 상태일 때 여러 레이아웃 관리자가 너비 크기 클래스에 따라 다르게 앱을 배치하는 방식
그림 2. 다양한 창 크기 클래스에 관한 여러 레이아웃 관리자

또한 개별 항목은 다양한 크기나 모양을 활용하여 더 많은 콘텐츠를 표시하고 항목 경계를 더 쉽게 구분할 수 있습니다.

히어로 요소를 강조합니다. 레이아웃에 이미지나 동영상 같은 특정 초점이 있는 경우 앱 창이 커질 때 사용자의 관심을 유지하기 위해 레이아웃을 확장합니다. 다른 지원 요소를 히어로 뷰 주변이나 아래에 재배치할 수 있습니다.

이러한 레이아웃을 생성하는 데는 여러 방법이 있지만, ConstraintLayout을 사용하면 비율에 따르거나 가로세로 비율을 적용하는 등의 다양한 방법으로 하위 뷰의 크기를 제한하고 뷰 자체나 다른 하위 뷰에 상대적으로 하위 요소를 배치할 수 있기 때문에 특히 적합합니다. ConstraintLayout으로 반응형 UI 빌드에서 이러한 모든 기능에 관해 자세히 알아보세요.

축소할 수 있는 콘텐츠를 기본적으로 표시합니다. 탭, 스크롤, 동작과 같은 추가적인 사용자 상호작용을 통해서만 액세스할 수 있는 콘텐츠를 가용 공간이 있을 때 표시합니다. 예를 들어 공간이 비교적 작을 때 탭 방식 인터페이스에 표시되는 콘텐츠는 사용 가능한 공간이 더 많을 때 대신 열이나 목록으로 재배치될 수 있습니다.

여백을 확장합니다. 공간이 너무 커서 모든 콘텐츠를 활용해도 보기 좋게 배치되지 않으면 콘텐츠가 중앙에 유지되고 개별 뷰가 자연스러운 크기가 되며 뷰 사이의 자연스러운 간격이 생기도록 레이아웃의 여백을 확장합니다.

또는 전체 화면 구성요소를 플로팅 대화상자 UI로 변환할 수 있습니다. 이 방법은 이메일 작성이나 캘린더 일정 만들기와 같은 즉각적인 사용자 작업을 처리하기 위해 독점적인 포커스를 요구하는 구성요소의 경우 특히 적합합니다.

대화상자 전체 화면을 보여주는 표준 휴대전화와 동일한 대화상자를 플로팅 창으로 보여주는 폴더블 휴대전화
그림 3. 전체 화면 대화상자가 중간 및 확장 후 너비의 표준 대화상자로 변환됨

콘텐츠 추가

표준 레이아웃: 지원 창, 목록 세부정보 뷰

지원 창을 사용합니다. 지원 창에는 문서의 댓글이나 재생목록의 항목과 같이 기본 콘텐츠와 관련된 추가 콘텐츠나 상황별 작업이 표시됩니다. 일반적으로 이 창은 확장 후 높이에 화면의 하단 3분의 1을, 확장 후 너비에 후행 3분의 1을 사용합니다.

창을 표시할 공간이 충분하지 않은 경우 이 콘텐츠를 배치할 위치는 중요한 고려사항입니다. 다음과 같은 대안을 살펴볼 수 있습니다.

  • DrawerLayout을 사용하는 후행 가장자리의 측면 창
  • BottomSheetBehavior를 사용하는 하단 창
  • 메뉴 아이콘을 탭하여 액세스할 수 있는 메뉴 또는 팝업 창
그림 4. 지원 창에 추가 콘텐츠를 표현하는 다른 방법

창이 두 개인 레이아웃을 만듭니다. 큰 화면에는 일반적으로 작은 화면에 별도로 표시되는 기능의 조합이 표시될 수 있습니다. 많은 앱에서 일반적인 상호작용 패턴은 연락처나 검색결과와 같은 항목의 목록을 표시하고 특정 항목이 선택되면 해당 항목의 세부정보로 전환하는 것입니다. 큰 화면에 알맞게 목록을 확대하는 대신 목록 세부정보 뷰를 사용하여 창 두 개로 된 레이아웃에 두 기능을 나란히 표시할 수 있습니다. 지원 창과 달리 목록 세부정보 뷰의 세부정보 창은 작은 화면에 독립적으로 표시될 수 있는 독립형 요소입니다.

목록 세부정보 뷰를 구현하려면 SlidingPaneLayout 전용 위젯을 사용하세요. 이 위젯은 두 창에 지정된 layout_width 값을 기반으로 두 개의 창을 함께 표시할 공간이 충분한지 자동으로 계산하며, layout_weight를 사용하여 남은 공간을 분배할 수 있습니다. 공간이 충분하지 않으면 각 창은 레이아웃의 전체 너비를 사용하고 세부정보 창은 목록 창 위에서 슬라이드되거나 화면을 벗어납니다.

SlidingPaneLayout이 넓은 디스플레이가 있는 기기에서 목록 세부정보 레이아웃의 두 창을 모두 표시함
그림 5. SlidingPaneLayout이 확장 후 너비의 창 두 개와 좁은 너비의 창 하나를 표시함

두 개의 창 레이아웃 만들기에는 SlidingPaneLayout 사용에 관한 자세한 내용이 포함되어 있습니다. 또한 이 패턴은 탐색 그래프를 구조화하는 방식에 영향을 미칠 수 있습니다(반응형 UI 탐색 참고).

추가 리소스