본문 바로가기

MAUI CollectionView의 Measure 폭주(Over-Measure) 문제

@veedeeo 2025. 12. 21. 13:41

CPU 스파이크·프레임 저하·UI 지연의 근본 원인과 최적 설계 패턴


1, 서론

다음과 같은 현상을 겪어본 적이 있는가?

  • 스크롤을 빠르게 하면 순간적으로 FPS가 떨어진다
  • 화면이 떨리듯 미세하게 끊긴다
  • 셀이 늦게 표시되며 몇 프레임 후에 안정된다
  • CPU가 갑자기 올라갔다 내려간다
  • 일부 셀만 늦게 나타나거나 깜빡인다
  • 데이터가 많아지면 초반 로딩이 느려진다

이런 문제를 단순 “List가 무겁다”라고 생각하면 해결할 수 없다.
실제 원인은 MAUI CollectionView의 Measure 사이클(측정) 폭주다.

Measure 폭주란 다음 같은 상황을 의미한다.

CollectionView가 스크롤 중 같은 셀을 수십 번 이상 반복 측정하는 병목 현상

이 문제는 Virtualization, 재활용, 이미지 로딩, Auto 크기 레이아웃이
서로 영향을 주며 발생하는 구조적 이슈다.

이번 글에서는 Measure 폭주가 왜 발생하는지
그리고 이를 완전히 제어하는 전문가 수준의 해결 전략을 설명한다.

 

요약 카드

제목
CollectionView Measure 폭주(Over-Measure) 문제와 CPU 스파이크의 근본 원인

요약

  • MAUI CollectionView는 셀이 화면에 나타날 때마다 Measure(크기 측정) → Arrange(배치) 단계를 반복 수행한다. 그런데 이미지 비동기 로딩, Label Auto Height, Grid 중첩, UI 상태 초기화 누락 등으로 인해 셀의 크기가 계속 변하면 Measure가 폭발적으로 증가(Over-Measure) 하며 CPU 사용률이 급격히 상승한다.
  • Measure가 반복되면 스크롤 중 프레임이 끊기고, 깜빡임·지연 렌더링·셀 늦게 표시 등 UI 품질이 급격히 떨어진다. 특히 AspectFit 이미지, 동적 텍스트 길이, 높이가 불안정한 Auto 레이아웃 구조는 Measure를 수십 번 유발하는 핵심 원인이다.
  • 해결 핵심은 셀 높이·요소 크기를 고정하거나 변동 요인을 제거하는 것이다. 즉 이미지 HeightRequest 고정, Label MaxLines 설정, Grid 최소화, OnBindingContextChanged에서 UI 완전 초기화, Skeleton UI 사용 등이 Measure 루프를 대폭 줄인다.
  • 고급 전략으로는 PreMeasure·PreWarm 기술을 활용해 셀 크기를 미리 계산하고 리소스를 선로딩해 스크롤 프레임을 안정화하는 방법이 있다. 이 방식은 SNS·쇼핑몰 앱들이 실제 사용하는 성능 최적화 패턴이며 MAUI에서도 매우 효과적이다.

 


2, 문제 정의 — Measure 폭주는 이렇게 나타난다

✔ 2.1 처음 스크롤 시 갑자기 버벅임

✔ 2.2 빠르게 이동하면 셀이 늦게 그려짐

✔ 2.3 CPU가 순간적으로 높아짐

✔ 2.4 텍스트나 이미지 로딩과 함께 셀이 흔들림

✔ 2.5 Virtualization이 작동할 때 셀의 크기를 매번 다시 계산

이 현상은 셀의 크기가 변하든 말든, 스크롤할 때마다 Measure가 반복되는 상황에서 악화된다.


3, 내부 구조 분석 — MAUI의 Measure 동작 방식

MAUI 레이아웃 엔진은 다음 흐름을 따른다.

  1. BindingContext 변경
  2. 템플릿 렌더링 요청
  3. Measure → Arrange
  4. 이미지·텍스트 로딩
  5. 요소 크기 변경 감지
  6. 다시 Measure 요청
  7. Virtualization이 재측정 요청
  8. 스크롤 이동 → 새 셀 등장 → 다시 Measure
  9. 필요 시 스크롤 보정

이 복잡한 루프에서
Measure 요청이 여러 번 발생하면 CPU가 폭주한다.

특히 다음 상황이 가장 치명적이다.

  • Auto Height(자동 높이) 요소
  • 이미지 비동기 로딩
  • Label의 줄 수가 동적으로 변함
  • Grid 중첩
  • Frame/Border의 Shadow·CornerRadius 계산
  • 재활용된 셀이 이전 상태를 유지하는 경우

MAUI는 Measure 비용이 매우 높은 구조라
그 자체가 성능 병목이 된다.


4, Measure 폭주를 만드는 8가지 주요 요인


✔ 4.1 이미지 로딩

이미지가 로드되기 전과 후의 크기가 달라지면
Measure가 재실행된다.

특히 AspectFit은 문제를 극대화한다.


✔ 4.2 Label의 Auto Height + 긴 텍스트

텍스트 길이에 따라 높이가 뒤늦게 확정되면
매번 Measure 루프가 발생한다.


✔ 4.3 Grid 중첩 + Auto(*,Auto) 조합

Grid의 Measure 비용은 StackLayout보다 훨씬 비싸며
중첩 구조는 폭발적으로 비용 증가.


✔ 4.4 DataTemplate 내부에 Layout이 너무 복잡한 경우

예:

  • 4중 Grid
  • LayoutOptions.Fill combined
  • Dynamic WidthRequest/HeightRequest

✔ 4.5 VisualStateManager로 인한 Height 변경

Selected/Normal 상태에서 Padding·BorderWidth가 바뀌면
즉시 Measure가 다시 실행된다.


✔ 4.6 Garbage Collection(GC) 개입

Measure 과정이 무겁다면
GC가 들어오고 프레임 드랍이 생긴다.


✔ 4.7 Virtualization 타이밍 충돌

빠르게 스크롤하면 Virtualization이
“보여야 할 셀 크기”를 짐작해야 하고
정확한 값을 모르기 때문에 Measure를 반복 요청한다.


✔ 4.8 OnBindingContextChanged에서 UI 초기화가 불완전

초기화를 하지 않으면 셀 크기 변동이 훨씬 더 많이 발생한다.


5, 해결 전략 — Measure 폭주를 막는 설계 패턴


✔ 전략 1 — 이미지 크기 고정

비추천:

<Image Aspect="AspectFit" />
 
 

추천:

<Image HeightRequest="120" WidthRequest="120" Aspect="AspectFill" />
 
 

AspectFill은 내부 크기를 강제로 고정해
Measure 호출 수를 크게 줄인다.


✔ 전략 2 — Label에 MaxLines 적용

<Label MaxLines="2" />
 
 

텍스트로 인해 높이가 변하지 않으므로
Measure 반복이 사라진다.


✔ 전략 3 — Grid 최소화, 레이아웃 단순화

Grid는 필요한 부분에만 사용하고
나머지는 VerticalStackLayout 또는 HorizontalStackLayout로 대체.


✔ 전략 4 — 셀 레이아웃을 “불변(Immutable)” 구조로 설계

높이가 절대 변하지 않으면
Measure는 초기 한 번만 실행되고 이후 거의 호출되지 않는다.


✔ 전략 5 — OnBindingContextChanged에서 모든 UI 상태 초기화

초기화를 하지 않으면
이전 셀의 상태(높이·Padding·이미지 크기)가 남아
Measure 루프를 유발한다.


✔ 전략 6 — Skeleton UI 사용

이미지가 늦게 로딩될 경우를 대비해
셀 높이를 고정한 Skeleton을 미리 배치하면
Measure 변동이 0에 가깝게 된다.


✔ 전략 7 — ItemsUpdatingScrollMode="KeepScrollOffset"

스크롤 위치 보정을 덜 수행하므로
레이아웃 재측정 빈도 감소.


✔ 전략 8 — Virtualization 끄기(RecycleElements=false) [극단적 해법]

Measure 폭주가 극단적으로 심한 경우에만

<CollectionView ReuseElements="false" />
 
 

성능은 떨어지지만
Measure 폭주가 사라져 UI가 안정된다.


6, 전문가 고급 전략 — PreMeasure + PreWarm

대형 SNS·쇼핑 앱의 핵심 기술이다.

✔ PreMeasure

화면에 보이기 전에 셀 크기 계산을 완료.
MAUI에서도 다음으로 구현 가능:

  • 고정 크기 레이아웃
  • 프리뷰 텍스트 사용
  • 미리 Measure 실행

✔ PreWarm

이미지·텍스트 일부를 백그라운드에서 먼저 로딩 후
UI에 즉시 표시되도록 하는 방식.

결과: 스크롤 시작부터 안정적인 프레임 유지.


7, 결론

MAUI CollectionView의 성능 저하, 프레임 드랍, UI 지연의 핵심 원인은
Measure 폭주(Over-Measure) 이다.

핵심 요약:

  • Measure는 매우 비싸고 스크롤 중 반복되면 CPU 스파이크 발생
  • 이미지 로딩·텍스트 길이·Auto 레이아웃이 Measure 루프의 주범
  • 셀 높이가 변하면 Measure → Arrange → Scroll 보정 → 성능 저하
  • 고정 크기 레이아웃 + UI 초기화 = Measure 폭주 제어의 핵심

해결 전략 요약:

✔ 이미지·텍스트 크기 고정
✔ Grid 최소화
✔ UI 불변 구조
✔ OnBindingContextChanged 초기화
✔ Skeleton UI
✔ KeepScrollOffset
✔ PreMeasure/PreWarm

위 전략을 적용하면
CollectionView는 빠르고 안정적이며 네이티브 앱 수준의 스크롤 성능을 확보할 수 있다.

veedeeo
@veedeeo :: .net MAUI·Netlify·SEO·웹 최적화 기술 블로그

.net MAUI·Netlify·SEO·웹 최적화 기술 블로그

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차