
ConstraintLayout chain weight는 10편에서 본 chain 기본을 실제 화면 비율 배치로 확장할 때 꼭 만나는 개념입니다. 버튼 두 개를 같은 폭으로 만들거나, 가운데 콘텐츠 영역만 더 넓게 만들고 싶을 때 단순히 margin을 조절하는 방식으로는 한계가 있습니다.
이번 글에서는 chain weight가 언제 동작하고, 왜 0dp와 함께 봐야 하는지부터 정리합니다. 그리고 1:1 버튼, 1:2:1 영역 분할, 같은 폭 탭 UI, packed bias와의 차이까지 XML 예시와 이미지로 비교하겠습니다.
먼저 한 줄로 정리
- chain weight는 chain 안의 0dp View들이 남은 공간을 나눠 갖는 비율입니다.
wrap_contentView에 weight만 붙여도 폭이 자동으로 비율 배치된다고 이해하면 안 됩니다.- 같은 폭이 필요하면 각 View를
0dp로 두고 같은 weight를 줍니다. - 1:2:1처럼 다른 비율이 필요하면 각 View의 weight 값을 다르게 둡니다.
- packed bias는 weight와 다릅니다. packed bias는 묶음의 위치를 옮기고, weight는 0dp View의 크기를 나눕니다.

ConstraintLayout chain weight가 동작하는 조건
공식 문서 기준으로 weighted chain은 spread 또는 spread_inside chain에서 MATCH_CONSTRAINT를 쓰는 요소들이 남은 공간을 나눠 쓰는 방식입니다. XML에서는 보통 android:layout_width="0dp" 또는 android:layout_height="0dp"로 표현합니다.
여기서 중요한 점은 weight가 View 사이 간격을 직접 조절하는 값이 아니라는 점입니다. chain 구조가 있고, 해당 방향 크기가 0dp인 View가 있을 때, 그 View들이 사용할 수 있는 공간을 어떤 비율로 나눌지 정하는 값입니다.
1:1 버튼 두 개 만들기

가장 쉬운 예시는 하단 버튼 두 개입니다. 텍스트 길이가 달라도 두 버튼의 클릭 영역은 같은 폭이어야 하는 경우가 많습니다. 이때 두 버튼을 chain으로 묶고, 둘 다 width를 0dp로 둔 뒤 같은 weight를 주면 됩니다.
1:1 버튼 XML 예시
<Button
android:id="@+id/primaryButton"
android:layout_width="0dp"
android:layout_height="48dp"
android:text="확인"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/secondaryButton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintHorizontal_weight="1" />
<Button
android:id="@+id/secondaryButton"
android:layout_width="0dp"
android:layout_height="48dp"
android:text="취소"
app:layout_constraintStart_toEndOf="@id/primaryButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/primaryButton"
app:layout_constraintHorizontal_weight="1" />이 예시에서 두 버튼은 모두 가로 방향 크기가 0dp입니다. 그래서 내용의 글자 수가 아니라 chain 안에서 사용할 수 있는 공간을 기준으로 폭이 계산됩니다. 둘 다 weight가 1이므로 결과는 1:1에 가깝게 나뉩니다.
1:2:1 영역 분할 만들기

같은 폭이 아니라 가운데 영역을 더 넓게 만들고 싶다면 weight 값을 다르게 주면 됩니다. 예를 들어 왼쪽, 가운데, 오른쪽 영역을 1:2:1로 만들면 가운데 영역이 양쪽보다 더 넓은 공간을 차지합니다.
1:2:1 XML 예시
<TextView
android:id="@+id/navArea"
android:layout_width="0dp"
android:layout_height="56dp"
android:text="Nav"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/contentArea"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintHorizontal_weight="1" />
<TextView
android:id="@+id/contentArea"
android:layout_width="0dp"
android:layout_height="56dp"
android:text="Content"
app:layout_constraintStart_toEndOf="@id/navArea"
app:layout_constraintEnd_toStartOf="@id/sideArea"
app:layout_constraintTop_toTopOf="@id/navArea"
app:layout_constraintHorizontal_weight="2" />
<TextView
android:id="@+id/sideArea"
android:layout_width="0dp"
android:layout_height="56dp"
android:text="Side"
app:layout_constraintStart_toEndOf="@id/contentArea"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/navArea"
app:layout_constraintHorizontal_weight="1" />이 구조는 LinearLayout의 layout_weight와 비슷하게 느껴질 수 있습니다. 다만 ConstraintLayout에서는 먼저 chain이 성립해야 하고, weight가 적용될 View의 해당 방향 크기가 0dp여야 한다는 점을 같이 봐야 합니다.
같은 폭 탭 UI에 chain weight 쓰기

탭 UI에서는 텍스트 길이가 서로 달라도 각 탭의 클릭 영역이 같아야 자연스러운 경우가 많습니다. 홈, 검색, 내 정보처럼 글자 수가 달라도 세 탭을 모두 0dp와 weight 1로 두면 균등한 탭 영역을 만들 수 있습니다.
<TextView
android:id="@+id/homeTab"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="홈"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/searchTab"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_weight="1" />
<TextView
android:id="@+id/searchTab"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="검색"
app:layout_constraintStart_toEndOf="@id/homeTab"
app:layout_constraintEnd_toStartOf="@id/profileTab"
app:layout_constraintTop_toTopOf="@id/homeTab"
app:layout_constraintHorizontal_weight="1" />
<TextView
android:id="@+id/profileTab"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="내 정보"
app:layout_constraintStart_toEndOf="@id/searchTab"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/homeTab"
app:layout_constraintHorizontal_weight="1" />이때 중요한 것은 텍스트를 가운데 정렬하는 android:gravity="center"와 폭을 나누는 weight를 구분해서 보는 것입니다. gravity는 탭 안의 글자 위치를 정하고, chain weight는 탭 자체의 폭을 정합니다.
weight가 안 먹는 가장 흔한 실수

weight가 안 먹는다고 느껴질 때는 먼저 width 또는 height를 확인해야 합니다. 가로 chain weight라면 android:layout_width="0dp"가 필요하고, 세로 chain weight라면 android:layout_height="0dp"가 필요합니다.
실패 예시
<TextView
android:id="@+id/left"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:text="A"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/right" />
<TextView
android:id="@+id/right"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:text="Long text"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/left"
app:layout_constraintEnd_toEndOf="parent" />이 XML은 weight 속성이 보여도 두 View가 wrap_content이기 때문에 기대한 균등 폭 배치가 나오지 않을 수 있습니다. 글자 길이에 따라 View 폭이 먼저 잡히기 때문입니다.
수정 예시
<TextView
android:id="@+id/left"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="A"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/right" />
<TextView
android:id="@+id/right"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="Long text"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/left"
app:layout_constraintEnd_toEndOf="parent" />수정된 예시는 두 View 모두 width가 0dp입니다. 이제 텍스트 길이보다 chain 안의 사용 가능한 공간과 weight 값이 폭 계산의 기준이 됩니다.
margin이 있으면 weight 결과도 달라진다

10편에서 본 것처럼 chain은 margin을 무시하지 않습니다. weight를 사용할 때도 마찬가지입니다. View 사이에 margin이 있으면 그 margin이 먼저 공간을 차지하고, 남은 공간 안에서 0dp View들이 weight 비율대로 나뉩니다.
그래서 1:1 weight를 줬는데도 화면에서 꽉 찬 두 덩어리처럼 보이지 않는다면 margin을 확인해야 합니다. 이건 오류라기보다, margin까지 포함한 실제 배치 결과입니다.
packed bias와 chain weight는 무엇이 다를까

10편에서 packed chain은 요소들을 묶은 뒤 bias로 묶음 위치를 조절할 수 있다고 설명했습니다. 하지만 packed bias와 weight는 목적이 다릅니다. packed bias는 View들의 묶음이 parent 안에서 어느 쪽에 가까운지를 바꾸고, weight는 0dp View들이 차지할 크기 비율을 바꿉니다.
- packed bias: View 묶음 전체의 위치를 조절한다.
- chain weight: 0dp View들의 폭 또는 높이를 비율로 나눈다.
- margin: weight가 나눌 수 있는 남은 공간 자체를 줄인다.
- gravity: View 안의 텍스트나 내용 위치를 조절한다.
세로 chain weight도 같은 원리다
본문에서는 가로 chain을 기준으로 설명했지만 세로 chain도 원리는 같습니다. 세로 방향에서는 android:layout_height="0dp"와 app:layout_constraintVertical_weight를 사용합니다.
<View
android:id="@+id/topPanel"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottomPanel"
app:layout_constraintVertical_weight="1" />
<View
android:id="@+id/bottomPanel"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/topPanel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_weight="2" />가로에서는 width 0dp와 horizontal weight를 보고, 세로에서는 height 0dp와 vertical weight를 보면 됩니다. 축만 다르고 읽는 순서는 같습니다.
문제가 생겼을 때 확인 순서

chain weight 문제는 속성 하나만 보고 해결하기 어렵습니다. 실제로는 chain 구조, 0dp, weight, margin, bias가 함께 결과를 만듭니다. 그래서 아래 순서대로 확인하면 원인을 훨씬 빨리 좁힐 수 있습니다.
- 양 끝 View가 parent 또는 같은 축의 기준에 연결되어 있는가
- 중간 View들이 서로 양방향으로 연결되어 chain이 성립하는가
- weight를 줄 View의 해당 방향 크기가
0dp인가 - 모든 0dp View에 의도한 weight 값이 들어갔는가
- margin이 기대보다 큰 공간을 먼저 차지하고 있지는 않은가
- packed chain을 쓰면서 weight처럼 기대하고 있지는 않은가
한 번에 정리
- ConstraintLayout chain weight는 chain 안의 0dp View 크기 분배 규칙이다.
- 가로 비율 배치는 width 0dp와
layout_constraintHorizontal_weight를 함께 본다. - 세로 비율 배치는 height 0dp와
layout_constraintVertical_weight를 함께 본다. - 같은 폭은 같은 weight, 다른 비율은 다른 weight로 만든다.
- packed bias는 묶음 위치 조절이고, weight는 크기 분배다.
즉, ConstraintLayout chain weight를 이해할 때는 weight 속성만 외우지 말고 chain 구조와 0dp가 먼저 준비되어 있는지를 확인해야 합니다.
마무리
ConstraintLayout chain weight는 실무에서 같은 폭 버튼, 균등 탭, 비율이 다른 콘텐츠 영역을 만들 때 유용합니다. 하지만 weight는 단독으로 동작하는 마법 속성이 아니라 chain, 0dp, margin과 함께 읽어야 하는 크기 계산 규칙입니다.
공식 기준은 Android Developers ConstraintLayout 가이드와 AndroidX ConstraintLayout 레퍼런스를 확인했습니다. 다음 12편에서는 보이지 않는 기준선인 Guideline으로 화면을 percent 기준으로 나누는 방법을 이어서 보겠습니다.
Android ConstraintLayout 완전 정리 이전 글 모음
Android ConstraintLayout 완전 정리 (1) – ConstraintLayout 기본 사용법: LinearLayout과 다른 제약 기반 배치 이해하기
Android ConstraintLayout 완전 정리 (3) – ConstraintLayout goneMargin과 margin 차이: View.GONE일 때 간격이 바뀌는 이유
Android ConstraintLayout 완전 정리 (5) – ConstraintLayout match_constraint 완전 정리: 0dp가 남은 공간을 채우는 원리
Android ConstraintLayout 완전 정리 (7) – ConstraintLayout baseline 정렬: TextView 글자 기준선을 맞추는 방법
Android ConstraintLayout 완전 정리 (8) – ConstraintLayout dimensionRatio 사용법: 1:1, 16:9 이미지 비율 맞추기
Android ConstraintLayout 완전 정리 (9) – ConstraintLayout percent min max 크기 정리: 화면 크기에 맞게 View 조절하기
Android ConstraintLayout 완전 정리 (10) – ConstraintLayout chain 사용법: spread, spread_inside, packed 차이