아이콘이 이름마다 다르게 잘렸다 — 범인은 Compose Row의 weight 한 줄

버그 중에 제일 사람 헷갈리게 하는 게 “어떤 건 되고 어떤 건 안 되는” 종류예요. 전부 안 되면 차라리 원인 찾기가 쉬운데, 들쭉날쭉하면 “내가 뭘 잘못 봤나” 싶어지죠. 제 안드로이드 앱의 이름 추천 카드가 딱 그랬습니다. 카드 오른쪽에 공유·저장 아이콘을 뒀는데, 어떤 이름엔 둘 다 보이고, 어떤 이름엔 공유만, 어떤 이름엔 아주 작게, 어떤 이름엔 아예 안 보였어요.

처음엔 랜덤인 줄 알았어요. 그런데 자세히 보니 규칙이 있었습니다. 이 규칙을 발견한 순간 원인이 풀렸고, 알고 보니 화면을 그리는 도구(Jetpack Compose)의 아주 전형적인 함정이었어요. 초보가 Compose로 가로 배치를 짤 때 꼭 한 번은 밟는 지뢰라, 정리합니다.

증상 — 잘림이 ‘한자풀이 길이 순’이었다

아이콘이 보이고 안 보이고가 이름의 한자풀이 길이를 정확히 따라갔어요. 표로 보면 한눈에 들어옵니다.

이름한자풀이보이던 아이콘
김예준짧음공유 + 하트
김하준좀 김공유만
김도현더 김공유가 잘려 아주 작게
김서준2줄없음

풀이가 길어질수록 오른쪽 아이콘이 하나씩 밀려나다가 결국 사라졌어요. 이렇게 증상이 특정 값(여기선 텍스트 길이)에 비례하면, 원인은 십중팔구 “공간을 나눠 갖는 방식”에 있습니다. 채점으로 치면, 답이 틀리는 문제마다 공통점(숫자가 커질 때만 틀림)이 보이면 실수 지점이 좁혀지는 것과 같아요. (오늘의 수학쌤 비유는 여기까지.)

왜 그랬나 — Row가 왼쪽부터 폭을 다 가져갔다

카드 첫 줄은 가로 배치(Row) 안에 왼쪽 텍스트 묶음(Column: 이름 + 한자풀이)과 오른쪽 액션 아이콘들이 나란히 있는 구조였어요. 문제는 그 왼쪽 텍스트 묶음에 폭을 얼마나 쓸지 지정(weight)이 없었다는 거예요.

weight(가중치): 가로/세로 배치에서 남는 공간을 자식들이 비율로 나눠 갖게 하는 지정. 안 주면 그 자식은 “필요한 만큼” 다 가져간다.

Compose의 Row는 배치할 때 순서가 있어요. weight가 없는 자식을 먼저, 순서대로 측정합니다. 그래서 왼쪽 텍스트 Column이 “나는 이만큼 필요해” 하고 행 폭을 먼저 다 선점해버리고, 뒤에 오는 오른쪽 아이콘들은 남은 폭만 받았던 거예요. 풀이가 짧으면 아이콘 자리가 남고, 길면 남는 게 없어 아이콘이 잘리거나 사라졌던 거죠. 증상이 “풀이 길이 순”이었던 이유가 정확히 이거였습니다.

어떻게 고쳤나 — weight 한 줄

고친 건 정말 한 줄이었어요. 왼쪽 텍스트 Column에 “너는 남는 폭만 가져라”를 뜻하는 weight(1f)를 붙였습니다.

Column(modifier = Modifier.weight(1f)) {
    // 이름 + 한자 + 한자풀이
}

이렇게 하면 Compose는 weight 없는 오른쪽 아이콘들의 폭을 먼저 확보하고, 남은 공간을 텍스트 Column에 줍니다. 순서가 뒤집힌 거예요. 이제 풀이가 아무리 길어도 아이콘 자리는 늘 보장되고, 텍스트는 남은 폭 안에서 줄바꿈되며 접힙니다. (첫 줄을 NameCardHeader라는 별도 조각으로 떼어내 정리도 같이 했어요.)

배운 것 — 테스트는 ‘크기’가 아니라 ‘위치’로

여기서 회귀(같은 버그가 되살아나는 것)를 막을 테스트를 붙이다가 하나 더 배웠어요. 처음엔 “아이콘 폭이 충분한가”로 검사하려 했는데, 그게 안 통했습니다.

Row는 폭이 모자라도 자식을 줄이지 않고 밖으로 밀어내요. 그러니 화면 밖으로 밀려나 안 보이는 아이콘도 측정된 폭은 원래 크기(48dp) 그대로입니다. “폭이 48dp 이상인가?”로는 잘린 걸 절대 못 잡는다는 뜻이죠.

레이아웃 테스트는 크기가 아니라 위치로 단언하자. 밀려난 요소도 크기는 멀쩡하니, “이 아이콘의 오른쪽 끝이 카드 폭 안에 있는가”(getUnclippedBoundsInRoot().right)를 봐야 잘림을 잡습니다. 저는 짧은/긴/아주 긴 풀이 3가지로 테스트를 짜고, 일부러 weight(1f)를 빼봤더니 긴 풀이 2개가 정확히 실패하더라고요 — 실기기 증상과 똑같은 패턴으로요.

기존에 있던 색·대비 스크린샷 테스트가 이 버그를 못 잡은 것도 같은 맥락이에요. 아이콘 은 멀쩡했고 이 문제였으니까요. 무엇이 틀렸는지에 맞는 잣대로 재야 한다는 걸 다시 배웠습니다.

그래서 뭘 하면 되나

Compose로 가로 배치를 짜다 요소가 잘리면, 이 순서로 보세요.

신호판단액션
잘림이 텍스트 길이에 비례🔴 폭 배분 문제유연하게 늘 텍스트에 weight(1f)
고정 크기 요소(아이콘)가 밀려남🟡 측정 순서 탓weight로 남는 폭만 텍스트에
“폭 충분한가” 테스트가 통과🔴 크기론 잘림 못 잡음위치(경계)로 단언
색 스크린샷 테스트만 있음🟡 치수 사각지대레이아웃 치수 테스트 보강

한 문장 요약: Compose Row에서 요소가 잘리면 십중팔구 유연한 자식에 weight가 빠진 것이다 — 늘 텍스트에 weight(1f)를 주고, 잘림 테스트는 크기가 아니라 위치(경계가 부모 안인지)로 단언하라.

이 버그가 남긴 교훈은, 화면 배치도 결국 “누가 먼저 공간을 갖느냐”의 순서 문제라는 거였어요. weight 한 줄이 그 순서를 뒤집어 아이콘 자리를 지켜줬고요. 눈에 보이는 게 전부인 UI일수록, 안 보이게 만든 규칙 하나를 찾는 게 핵심이더라고요.


댓글 남기기