Computer >> 컴퓨터 >  >> 시스템 >> Android

안드로이드 레이아웃 완벽 정리: LinearLayout부터 ConstraintLayout까지 한눈에 이해하기

Linear, Relative, Constraint, Table, Frame 등 안드로이드는 앱 UI를 설계할 때 선택할 수 있는 다양한 레이아웃을 제공합니다. 그렇다면 어떤 레이아웃이 가장 좋을까요? 정답은 상황에 따라 다르다는 것입니다. 각 레이아웃의 특성과 동작 방식을 제대로 이해해야 화면 구조와 성능에 맞는 최적의 선택을 할 수 있습니다.

본격적으로 레이아웃을 살펴보기 전에, 먼저 뷰(View) 객체 계층 구조와 안드로이드의 화면 그리기 과정부터 짚고 넘어가겠습니다.

View와 ViewGroup

ViewGroup은 모든 뷰의 부모 클래스이자 레이아웃의 기반 클래스입니다. 다른 뷰들을 담는 컨테이너 역할을 하는 객체라고 생각하면 됩니다. 예를 들어 LinearLayout은 내부에 뷰뿐만 아니라 다른 레이아웃까지 포함할 수 있으므로 ViewGroup입니다.

반면 View는 UI를 구성하는 가장 기본적인 빌딩 블록입니다. View는 ViewGroup의 자식으로 포함될 수 있는데, 대표적인 예가 바로 TextView입니다.

안드로이드 레이아웃 완벽 정리: LinearLayout부터 ConstraintLayout까지 한눈에 이해하기
ViewGroup과 View의 계층 구조

Measure → Layout → Draw → 반복

안드로이드에서 레이아웃은 XML 파일로 저장됩니다. 그런데 이 XML이 어떻게 화면에 보이는 객체로 변환될까요? 각 XML 파일은 인스턴스화(흔히 '인플레이트(inflate)'라고 표현)되어 하나의 뷰 계층 트리를 형성합니다. 즉, 레이아웃 B가 레이아웃 A 안에 중첩되어 있다면 두 레이아웃은 자식-부모 관계를 맺게 됩니다(A가 B의 부모).

트리가 완성되면 세 단계의 과정이 진행됩니다. 바로 Measure(측정), Layout(배치), Draw(그리기)입니다. 각 단계는 트리를 깊이 우선 탐색(DFS) 방식으로 순회합니다.

Measure (측정)

첫 번째 단계에서는 각 부모 노드가 자식 노드들의 크기 제약 조건을 파악합니다. 부모는 이 제약 조건을 자식에게 아래로 전달하고, 각 자식은 자신이 원하는 크기를 계산하면서 부모에게 받은 제약 조건과 자기 자식들의 제약 조건까지 함께 고려하게 됩니다.

Layout (배치)

두 번째 단계에서는 각 노드가 자식들의 최종 크기와 화면상의 위치를 결정합니다.

Draw (그리기)

마지막 단계에서는 루트 노드부터 시작해 자기 자신을 그린 뒤 자식들에게 그리기를 지시합니다. 이런 방식으로 부모가 먼저 그려지고, 그 위에 자식들이 차례로 그려집니다.

이 과정을 기억한다면, 뷰 계층을 순회하는 데 걸리는 시간을 줄이기 위해 앱의 레이아웃을 최대한 얕게(shallow) 유지하는 것이 좋습니다.
안드로이드 레이아웃 완벽 정리: LinearLayout부터 ConstraintLayout까지 한눈에 이해하기
"assorted-color photo frame lot" by Markus Spiske on Unsplash

주요 레이아웃 상세 분석

LinearLayout (리니어 레이아웃)

자식 뷰들을 세로(vertical) 또는 가로(horizontal) 방향의 한 줄로 배치합니다. 모든 뷰가 하나의 행 또는 하나의 열에 정렬되며, android:orientation 속성으로 방향을 지정할 수 있습니다.

LinearLayout의 흥미로운 기능 중 하나는 layout_weight 속성입니다. 이 속성은 자식 뷰들 사이에 공간을 어떻게 나눌지 LinearLayout에게 알려주는 역할을 하며, 다양한 기기와 화면 방향에서 일관된 레이아웃을 유지하고 싶을 때 특히 유용합니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="World!"
        />

</LinearLayout>
간단한 리니어 레이아웃 예제

예를 들어 첫 번째 TextView(Hello)가 항상 화면 너비의 4분의 3을 차지하도록 만들고 싶다면 layout_weight 속성을 활용하면 됩니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_weight="4"        // <-- 레이아웃 전체 weight를 4로 설정
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="3"   // <-- 4중 3의 비율 (3/4)
        android:text="Hello" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="World!"
        android:layout_weight="1"   // <-- 4중 1의 비율 (1/4)
        />

</LinearLayout>
layout_weight 속성 활용 예시

RelativeLayout (릴레티브 레이아웃)

이름 그대로 자식 뷰들을 서로 간의 상대적 위치를 기준으로 배치하는 레이아웃입니다. 중첩된 ViewGroup 없이 평탄한(flat) 뷰 계층을 유지할 수 있다는 장점이 있습니다. 다만 RelativeLayout은 두 번의 Measure 패스를 거쳐야 하므로 성능에 영향을 줄 수 있다는 점은 감안해야 합니다.

RelativeLayout의 유용한 기능 중 하나는 centerInParent 속성을 사용해 자식 뷰를 부모의 정중앙에 배치할 수 있다는 점입니다.

안드로이드 레이아웃 완벽 정리: LinearLayout부터 ConstraintLayout까지 한눈에 이해하기
layout_centerInParent로 TextView를 중앙 정렬한 모습

ConstraintLayout (컨스트레인트 레이아웃)

컨스트레인트(constraint, 제약)란 해당 요소와 연결되거나 정렬되는 관계를 의미합니다. 각 자식 뷰에 대해 주변의 다른 뷰를 기준으로 다양한 제약 조건을 정의하는 방식입니다. 이를 통해 중첩된 ViewGroup 없이도 복잡한 레이아웃을 평탄한 뷰 계층으로 구성할 수 있습니다. RelativeLayout과 마찬가지로 두 번의 Measure 패스가 필요합니다.

참고로 ConstraintLayout은 현재 안드로이드 공식 문서에서 권장하는 기본 레이아웃으로, 유연한 반응형 UI 구현에 널리 사용되고 있습니다.

안드로이드 레이아웃 완벽 정리: LinearLayout부터 ConstraintLayout까지 한눈에 이해하기
TextView에 설정된 제약 조건(constraints)

FrameLayout (프레임 레이아웃)

이 레이아웃은 기본적으로 단일 자식 뷰를 담기 위한 용도로, 다른 뷰들이 겹치지 않도록 설계되었습니다. 레이아웃 자체의 크기는 보이든 보이지 않든 가장 큰 자식 뷰의 크기에 패딩을 더한 값이 됩니다.

FrameLayout 안에 여러 개의 자식 뷰를 넣으면 뷰들이 서로 겹치기 쉽기 때문에 피하는 것이 좋습니다. 여러 자식 뷰를 사용해야 한다면 각 자식에게 layout_gravity 속성을 지정해 위치를 제어할 수 있습니다.

안드로이드 레이아웃 완벽 정리: LinearLayout부터 ConstraintLayout까지 한눈에 이해하기

ListView / GridView (리스트 뷰 / 그리드 뷰)

화면에 여러 개의 항목을 나열해야 할 때 사용합니다(예: 식당 메뉴판). ListView는 사용자가 스크롤할 수 있는 단일 열 목록이며, GridView는 이를 여러 열로 확장한 형태라고 생각하면 됩니다.

이 레이아웃들에서 중요한 점은 뷰가 동적(dynamic)이라는 것입니다. 항목들은 런타임에 생성되며, 이를 채우려면 AdapterView를 사용해야 합니다.

안드로이드 레이아웃 완벽 정리: LinearLayout부터 ConstraintLayout까지 한눈에 이해하기
layout_column과 layout_row로 각 항목의 위치를 지정할 수 있습니다

TableLayout (테이블 레이아웃)

GridView와 매우 유사하게 자식 뷰들을 행(row)과 열(column)로 배열하는 레이아웃입니다. 각 레이아웃은 여러 개의 TableRow 객체를 포함하며, 각 TableRow가 하나의 행을 정의합니다.

안드로이드 레이아웃 완벽 정리: LinearLayout부터 ConstraintLayout까지 한눈에 이해하기
두 개의 TableRow 요소로 구성된 예시

마무리

어떤 레이아웃이 '정답'인지 미리 정해져 있지 않습니다. 다양한 레이아웃을 직접 시도해 보면서 자신의 화면 구조와 성능 요구 사항에 가장 잘 맞는 것을 찾는 것이 중요합니다. 여러분에게 가장 유용했던 레이아웃이 무엇인지, 그 이유는 무엇인지 댓글로 알려주세요!