
얼마 전 깃허브에 올라온 팀 후배 PR을 리뷰하다 멈칫했습니다. 상품 그리드 코드 전체가 FlowRow 기반의 커스텀 레이아웃으로 바뀌어 있었거든요. 커밋 메시지엔 "Compose 최신 API로 반응형 레이아웃 교체"라고만 달려 있더라구요.
코드를 잠깐 들여다보고는, 바로 'Request Changes(수정 요청)'를 남길 수밖에 없었네요.
최근 컴포즈(Compose)에서 FlowRow와 ContextualFlowRow의 기능이 대폭 강화되면서 "이제 복잡한 LazyVerticalGrid는 구식 API 아니냐, 덜 써도 되는 것 아니냐"는 분위기가 생긴 건 저도 느끼고 있었습니다. 다만 그 판단이 좀 성급하다고 봅니다. 각각의 API는 애초에 다른 문제를 풀기 위해 설계된 거거든요.
LazyVerticalGrid와 FlowRow, 애초에 다른 문제를 풀기 위해 만들어졌습니다
최신 FlowRow는 웹의 CSS Flexbox 패러다임을 컴포즈로 매끄럽게 들여왔습니다. maxItemsInEachRow나 Modifier.weight() 같은 조합으로 반응형 배치를 아주 직관적으로 표현할 수 있거든요. 웹 개발 경험이 있는 분이라면 특히 직관적으로 느끼셨을 거라 생각합니다.
LazyVerticalGrid는 출발점 자체가 달라요. "대량 아이템을 버벅임 없이 스크롤하려면 어떻게 해야 하나"가 첫 번째 설계 질문이었거든요. GridCells.Adaptive(minSize = 160.dp) 한 줄로 반응형 다단 레이아웃을 짜면서도, 화면에 보이는 아이템만 렌더링하고 상태 변화 시 영향 범위를 제한하는 스코프 안정화 최적화가 기본으로 깔려 있습니다.
FlowRow 기반 레이아웃은 "어떻게 유연하게 배치할까"를 먼저 생각하고, LazyVerticalGrid는 "얼마나 많아도 버티나"를 먼저 생각합니다. 이 설계 의도의 차이가 곧 선택 기준이 되는 거예요.
대량 아이템 성능, Non-lazy 레이아웃은 LazyVerticalGrid를 따라가기 어렵습니다
FlowRow 같은 Non-lazy 레이아웃의 가장 큰 약점은 모든 아이템을 컴포지션 단계에서 한꺼번에 처리한다는 점이에요. 아이템이 수십 개 수준일 때는 크게 문제 안 되지만, 100~200개를 넘어가기 시작하면 재구성 비용이 급증하며 스크롤 시 프레임 드랍(Jank)이 눈에 띄게 늘어나는 편이거든요.
공식 자료와 커뮤니티 벤치마크를 보면, LazyVerticalGrid는 1,000개 이상 아이템에서도 60fps를 거뜬히 유지합니다. 반면 Non-lazy 방식으로 1,000개 이상의 아이템을 렌더링하면 앱이 심하게 끊기거나 멈출 수 있습니다. 이미지까지 포함된 수천 개짜리 그리드라면 메모리 부족(OOM) 오류로 앱이 죽을 위험도 충분히 현실적인 수준이에요. 이런 차이가 생기는 이유는 LazyVerticalGrid가 화면 밖 아이템을 컴포지션에서 아예 제외하기 때문이거든요. FlowRow는 그 구분이 없습니다.
인스타그램 피드처럼 이미지가 빽빽하게 들어찬 무한 스크롤 상품 카탈로그를 떠올리시면 됩니다. LazyVerticalGrid는 정확히 그 상황을 위해 만들어졌고, 일반 레이아웃은 그 타깃이 아니었던 셈이에요.
개인적으로는 아이템이 100~200개를 넘기 시작하는 화면에서는 뷰 복잡도를 고려해 무조건 LazyVerticalGrid를 유지하고 있습니다. 나중에 데이터가 늘어났을 때 다시 마이그레이션하면 두 배로 다시 일해야되기 때문이죠.

FlowRow로 가변 너비 자동 줄바꿈과 세밀한 제어가 가능한 이유
FlowRow는 웹의 Flexbox와 같은 역할을 완벽히 수행합니다. 아이템마다 너비가 달라지는 상황에서 자동 줄 바꿈과 정렬을 처리해줘요. 단순 Row나 Column으로는 이게 안 되거든요. 자동 래핑 기능이 없어서 아이템이 화면 밖으로 밀려나거나, 줄 바꿈 시점을 직접 계산해야 하는 상황이 생기더라구요.
특히 최근 버전에서 Modifier.weight()나 Arrangement.spacedBy() 같은 속성으로 여백과 비율을 세밀하게 조절할 수 있게 된 점이 장점입니다. 과거의 FlowRow가 단순 수평 나열에 머물렀다면, 이제는 CSS의 flex-grow 개념까지 들어와서 남은 공간을 꽉 채우는 훨씬 더 세밀한 제어가 가능하게 되었습니다.

상품 그리드엔 LazyVerticalGrid, 태그 클라우드엔 FlowRow를 쓰는 기준
상품 목록 그리드는 보통 무한 스크롤이 붙고 데이터가 많습니다. 이때는 LazyVerticalGrid에 GridCells.Adaptive(minSize = 160.dp)를 사용하는 것이 정답이에요. 한 줄로 화면 크기에 맞게 열 수를 자동 조절해주니 반응형 상품 그리드에 딱 맞는 데다, 수백~수천 개 범위라도 성능 저하 없이 충분히 커버가 되거든요.
하지만, 태그 클라우드나 해시태그 목록은 얘기가 달라요. "여름", "캠핑", "반려동물 동반" 같이 글자 수가 들쑥날쑥한 태그를 고정 너비로 배치하면 왼쪽만 내용이 쏠리거나 여백이 어색해 질 수 있습니다. 그런데 FlowRow의 동적 너비와 자동 줄바꿈이 이 문제를 자연스럽게 해결해줘요. 태그는 대개 수십 개 수준이라 대규모 렌더링 성능보다 UI 표현력이 먼저인 상황이기도 하구요.
제가 보기엔 이렇게 정리하면 명확합니다.
- 아이템 수 수백 개 이상이거나 무한 스크롤 가능성 있음 → LazyVerticalGrid 유지
- 아이템마다 너비가 다르고 자동 줄바꿈 필요 (태그 클라우드 등) → FlowRow
- 열 개수가 고정된 소규모(수십 개 이내) UI → FlowRow (maxItemsInEachRow 활용)

컴포즈 최신 레이아웃 API 마이그레이션 전 주의할 점
컴포즈 1.11의 강화된 기능들은 무척 강력하지만, ContextualFlowRow 같은 최신 API들은 향후 엣지 케이스 처리나 API 미세 조정이 생길 여지가 있는 편이에요. LazyVerticalGrid는 몇 년간 프로덕션에서 수많은 앱을 통해 검증된 이력이 있는 것처럼요.
기존 코드베이스와 혼용하면 컴포넌트 설계 기준이 복잡해지는 경우가 생깁니다. 그렇기 때문에, 팀 단위로 작업할 때는 "우리는 어떤 기준으로 어느 레이아웃 API를 쓰나"를 먼저 정하지 않으면 코드 일관성이 흐트러질 수 있습니다.
마치며
처음에 팀 후배 PR에 달았던 댓글 얘기를 했었는데, 정확한 내용은 이거였어요. "자동 줄바꿈 같은 명확한 뷰포트 내 레이아웃 처리가 필요하면 FlowRow로 가되, 대량의 데이터를 다루는 LazyVerticalGrid가 이미 잘 돌고 있으면 지금 당장 바꿀 이유는 없다"라고 말이죠. 새 API라고 무조건 갈아엎는 게 아니라, 풀려는 문제가 새 API에 더 잘 맞을 때 갈아타야 된다는 점을 기억하셔야 합니다.
마이그레이션을 고민하신다면 아이템 수, 레이아웃(너비)의 가변성, 팀 코드 일관성 이 세 가지를 먼저 점검하시라고 꼭 말씀드리고 싶네요.
'Android 개발 > Jetpack Compose' 카테고리의 다른 글
| MeshGradient Modifier, 한 줄로 끝난다는 말의 진짜 의미 (0) | 2026.05.30 |
|---|---|
| Material3 1.5.0-alpha19 adaptive pane scaffold, 1.5.0 안정 직전 정리 (0) | 2026.05.19 |
| 최근 안정화된 Compose API, MeshGradient 말고 매일 쓸 3가지 (0) | 2026.05.13 |
| Jetpack Compose 업데이트, 진짜 '물건'은 따로 있었네요 (0) | 2026.05.10 |
| 안드로이드 Compose 테스트 컴파일 에러, Matcher 기반 마이그레이션 일지 (0) | 2026.05.08 |