LinearLayout의 기초를 설명합니다 — Android SDK의 기본 ViewGroup으로, 자식 요소를 세로(LinearLayout.VERTICAL) 또는 가로(LinearLayout.HORIZONTAL)로 한 줄에 정렬합니다. LinearLayout은 레이아웃 복잡성이 5~10개 요소를 초과하지 않을 때 버튼 행, 설정 목록 또는 입력 양식을 구축하는 가장 간단하고 빠른 방법입니다. layout_weight 및 orientation 속성이 공간 분배와 방향을 관리합니다. 자세한 사양은 LinearLayout API Reference를 참조하세요.
핵심 사항
LinearLayout — Android SDK(API Level 1)의 최초 ViewGroup 중 하나로, 자식 요소를 단일 행 또는 열에 순차적으로 배치하도록 설계되었습니다. 각 새 요소는 이전 요소 다음에 배치되며, 방향은 android:orientation 속성으로 설정됩니다. LinearLayout은 가장 간단하고 예측 가능한 Android 컨테이너로, 버튼 행, 아이콘과 텍스트가 있는 설정 목록, 가로 메뉴와 같은 선형 인터페이스에 이상적인 선택입니다.
역사적으로 LinearLayout은 초보 Android 개발자가 가장 먼저 접하는 ViewGroup입니다. 그 장점은 투명성입니다. 자식 요소는 숨겨진 배치 규칙 없이 선언 순서대로 엄격하게 배치됩니다. 요소가 모든 이웃에 바인딩될 수 있는 RelativeLayout이나 제약 조건 시스템을 가진 ConstraintLayout과 달리, LinearLayout은 "첫 번째 왼쪽/위, 그 다음 두 번째, 두 번째 다음 세 번째" 원칙으로 작동합니다. Google I/O 2017에 따르면, Google Play 상위 1000개 앱의 78% 화면이 LinearLayout을 기본 또는 보조 컨테이너로 사용합니다.
LinearLayout의 주요 메트릭은 LinearLayout.MeasureSpec — 크기 관리 시스템입니다. VERTICAL 방향에서는 각 요소의 너비가 부모의 너비(match_parent 또는 고정)와 같고, 높이는 콘텐츠 또는 속성에 의해 결정됩니다. HORIZONTAL의 경우 반대입니다. 높이는 부모와 같고 너비는 콘텐츠에 따릅니다. HORIZONTAL 행의 요소 총 너비가 부모의 너비를 초과하면 요소가 화면 밖으로 넘칠 수 있습니다(weight를 사용하지 않는 경우).
LinearLayout은 ViewGroup의 onMeasure와 onLayout을 재정의합니다. VERTICAL 방향에서는 onLayout이 Y축을 따라 요소를 배치합니다: 첫 번째 자식 — y=0, 두 번째 — y=prev.bottom + divider.hidden, 등등. HORIZONTAL의 경우 — X축을 따라 배치합니다. 구분선은 android:divider와 android:showDividers(beginning, middle, end)를 통해 설정됩니다. 구분선은 색상, drawable 또는 사용자 정의 Shape 리소스가 될 수 있습니다.
방향 — 자식 요소 배치 방향을 결정하는 LinearLayout의 핵심 속성입니다. android:orientation="vertical"은 요소를 위에서 아래로 배치하고, android:orientation="horizontal"은 왼쪽에서 오른쪽으로 배치합니다(RTL 지역화의 경우 오른쪽에서 왼쪽). 방향이 설정되지 않은 경우 기본적으로 HORIZONTAL이 사용됩니다(이전 버전에서). 그러나 예측 가능한 동작을 위해서는 명시적 지정이 필요합니다.
android:gravity — LinearLayout 내부의 콘텐츠를 정렬합니다. top, bottom, left, right, center, center_horizontal, center_vertical, fill, clip_vertical, clip_horizontal의 조합을 허용합니다. VERTICAL 레이아웃에서 gravity="center" 값을 사용하면 모든 요소가 가로로 중앙 정렬됩니다. android:layout_gravity — 자식 요소 속성으로, 부모 LinearLayout 내에서 요소를 정렬합니다. 예: VERTICAL 레이아웃에서 layout_gravity="right"인 버튼은 버튼을 오른쪽 가장자리로 밀어냅니다.
차이점: gravity — 부모 속성(자식이 그 안에 어떻게 배치되는지), layout_gravity — 자식 속성(부모 내에서 어떻게 동작하는지). 이러한 속성을 혼동하는 것은 초보자에게 가장 흔한 실수 중 하나입니다.
텍스트 요소(TextView, Button, EditText)가 있는 HORIZONTAL 레이아웃의 경우 android:baselineAligned="true"가 유용합니다. 패딩, 크기 또는 높이에 관계없이 모든 텍스트를 글꼴 기준선으로 정렬합니다. 이렇게 하면 아이콘, 제목 및 설명이 있는 행이 시각적으로 깔끔해집니다. 텍스트가 위아래로 튀지 않습니다.
layout_weight — LinearLayout의 자식 요소 속성으로, 여유 공간의 얼마를 차지할지 결정합니다. 방향 방향(HORIZONTAL의 경우 너비, VERTICAL의 경우 높이)에서만 작동합니다. 계산 공식: 크기 = 자체 크기 + (여유 공간 × (요소 가중치 / 모든 가중치의 합)). 여유 공간 = 부모 크기 — 모든 자식의 자체 크기 합계.
layout_weight은 적응형 인터페이스에서 고정 크기보다 효율적입니다. 예를 들어, 왼쪽 패널이 화면의 30%를 차지하고 오른쪽 패널이 70%를 차지해야 하는 경우 weight=3과 weight=7을 설정합니다. 올바른 작동을 위해 방향의 크기는 일반적으로 0dp로 설정됩니다(HORIZONTAL의 경우 android:layout_width="0dp"). 그러면 요소의 자체 크기가 0으로 간주되고 전체 크기는 weight에 의해서만 결정됩니다. 이것은 Google의 표준 권장사항입니다(Android Developers Guide, 2024).
중요: 가중치는 weight가 설정되지 않은 요소를 포함하여(가중치는 0으로 간주) LinearLayout의 모든 요소에 대해 합산됩니다. 한 요소의 weight=1이고 다른 요소의 weight=2인 경우 첫 번째는 여유 공간의 1/3을 차지하고 두 번째는 2/3를 차지합니다. weightSum(부모 속성)은 최대 가중치 합계를 설정할 수 있습니다. 자식 가중치의 합이 weightSum보다 작으면 차이는 빈 공간으로 남습니다.
로그인 양식은 세 가지 요소로 구성됩니다: 이메일 필드(화면 높이의 2/4 차지), 비밀번호 필드(1/4), 버튼(1/4). weight가 있는 세 요소 모두 VERTICAL 방향에서 높이에 의해서만 작동합니다.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:hint="Email" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:hint="비밀번호"
android:inputType="textPassword" />
<Button
android:id="@+id/login_button"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="로그인" />
</LinearLayout>
각 요소의 높이 = (parent_height — padding) × (weight / sumWeights). email은 높이의 2/4, 비밀번호와 버튼은 각각 1/4을 얻습니다. 모두 height=0dp이므로 자체 크기는 고려되지 않으며 weight만 작동합니다.
세 개의 버튼이 있는 패널: 뒤로가기 아이콘(wrap_content), 제목(나머지 채움), 메뉴 아이콘(wrap_content). 제목의 weight=1, 버튼은 weight 없음.
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical">
<ImageButton
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow_back" />
<TextView
android:id="@+id/toolbar_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="제목"
android:textSize="18sp" />
<ImageButton
android:id="@+id/btn_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_more_vert" />
</LinearLayout>
뒤로가기 및 메뉴 버튼은 아이콘에 필요한 정확한 공간을 차지합니다. width=0dp와 weight=1이 있는 제목(TextView)은 그 사이의 남은 모든 공간으로 확장됩니다. gravity="center_vertical"은 모든 요소를 세로로 가운데 정렬합니다.
제품 카드: 가로 행(이미지 + 세로 텍스트 블록). 외부 LinearLayout은 HORIZONTAL, 내부는 제목, 설명 및 가격을 위해 VERTICAL입니다.
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/product_image"
android:scaleType="centerCrop" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="상품명"
android:textStyle="bold"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="두 줄로 간략한 상품 설명"
android:textSize="14sp"
android:textColor="@color/gray" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1,299 RUB"
android:textSize="18sp"
android:textColor="@color/accent" />
</LinearLayout>
</LinearLayout>
왼쪽에 이미지(80×80dp), 오른쪽에 세 줄의 텍스트로 된 세로 블록. 내부 LinearLayout의 weight=1이므로 이미지 이후의 남은 모든 공간을 차지합니다. 이것은 수천 개의 앱에서 볼 수 있는 클래식 리스트 카드 패턴입니다.
LinearLayout의 주요 단점 — 복잡한 레이아웃에 중첩 컨테이너가 필요하다는 점입니다. 요소를 2×2 그리드로 배치하거나 양쪽 가장자리에 정렬된 행을 만들어야 하는 경우 최소 2~3개의 중첩 수준이 필요합니다. 각 LinearLayout 수준은 2개의 onMeasure 패스를 추가하여, 약한 기기에서 렌더링 중 눈에 띄는 지연을 초래합니다.
Android Performance Blog(Google, 2017)에 따르면, 중첩 수준이 하나 추가될 때마다 레이아웃 시간이 30~50μs 증가합니다. 3개의 중첩 수준이 있는 20개 항목 목록의 경우 지연이 요소당 3ms에 도달할 수 있으며, 이는 스크롤 시 눈에 띄는 끊김 현상(jank)입니다. ConstraintLayout은 플랫 계층 구조를 통해 이 문제를 해결하여 3~4개의 중첩된 LinearLayout을 단일 제약 컨테이너로 대체합니다.
권장 사항: 간단한 선형 레이아웃(최대 10개 요소, 하나의 중첩 수준)에는 LinearLayout을 사용하세요. 목록 카드, 다양한 정렬이 필요한 양식 및 테이블 구조의 화면에는 ConstraintLayout을 선택하세요. RecyclerView의 반복 요소에는 항목 레이아웃 파일에서 ConstraintLayout을 사용하세요. 이는 스크롤 성능에 매우 중요합니다.
자주 묻는 질문
가장 일반적인 이유 — 방향의 크기가 0dp로 설정되지 않았습니다. HORIZONTAL 레이아웃의 경우 android:layout_width="0dp"가 필요하고, VERTICAL의 경우 android:layout_height="0dp"가 필요합니다. 요소에 wrap_content 또는 match_parent가 있는 경우 weight가 작동하지 않거나 예기치 않은 결과가 발생할 수 있습니다. 두 번째 이유 — 부모의 weightSum이 자식 가중치의 합보다 작은 경우입니다. 세 번째 — 반대 방향의 자식 요소에서 match_parent를 사용하는 경우입니다.
layout_weight는 각 자식 요소에 설정되며 여유 공간에서의 몫을 결정합니다. weightSum — 부모 LinearLayout의 속성으로 최대 가중치 합계를 설정합니다. 기본적으로 weightSum = 모든 자식의 layout_weight 합계입니다. weightSum = 3으로 설정하고 자식의 weight=1인 경우 각각 1/3을 차지합니다. weight=1인 자식이 두 개만 있는 경우 나머지 1/3은 비어 있습니다. weightSum은 요소를 추가/제거할 때 재계산 없이 비율을 고정하는 데 편리합니다.
기본적으로 간격이 없으며 요소는 서로 인접하게 배치됩니다. 간격이 나타나면 부모의 android:divider 및 android:showDividers, 자식 요소의 android:layout_margin을 확인하세요. 추가로 android:weightSum이 영향을 미칠 수 있습니다. 요소가 weight를 사용하는 경우 잘못된 계산으로 인해 요소 사이에 빈 공간이 남을 수 있습니다. 간격을 없애려면 모든 자식 여백을 0dp로 설정하고 구분선을 제거하세요.
단순한 단방향 레이아웃(행/열에 1~5개 요소)의 경우 LinearLayout이 약간 더 빠릅니다 — 제약 조건 계산에 리소스를 소비하지 않습니다. 복잡한 레이아웃(5개 이상의 요소, 그리드, 다양한 정렬 유형)의 경우 ConstraintLayout이 더 빠릅니다 — 중첩된 LinearLayout 대신 플랫 계층 구조를 사용하기 때문입니다. Google 테스트(Android Performance Blog)에 따르면 ConstraintLayout은 3+ 수준의 LinearLayout 중첩이 필요한 레이아웃에서 우수합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.