본문 바로가기

MAUI CollectionView 스크롤 초기화 문제 해결, Virtualization과 데이터 갱신 구조 분석

@veedeeo 2025. 12. 11. 20:49

1. 서론 — CollectionView 스크롤이 갑자기 튀어 올라가는 이유

MAUI CollectionView를 사용하면 다음과 같은 현상이 자주 발생한다.

  • 항목을 추가(Add)하면 스크롤이 맨 처음으로 이동
  • 항목을 수정하면 리스트 전체가 다시 그려짐
  • ObservableCollection을 갱신해도 스크롤이 유지되지 않음
  • Filtering(Search) 후 다시 원래 목록으로 돌아가면 스크롤이 초기화됨

많은 초보자는 이를 단순 버그로 오해하지만, 실제로는 MAUI CollectionView의 Virtualization 구조와 ItemsSource 변경 방식이 핵심 원인이다.

이 글에서는 스크롤 초기화가 발생하는 이유를 내부 구조 관점에서 분석하고, 실전 해결 전략을 제시한다.

 

MAUI CollectionView Scrool Reset Illustration


2. 스크롤 초기화가 발생하는 핵심 원인 분석

원인 1: ItemsSource 전체를 새로운 컬렉션으로 교체하는 경우

다음 코드는 전형적인 문제 코드를 보여준다.

Items = new ObservableCollection<MyItem>(filtered);
 
 

ItemsSource가 새로운 객체로 바뀌면 MAUI는 다음 동작을 수행한다.

  1. VirtualizingLayout 초기화
  2. 모든 셀 재생성
  3. 스크롤 위치 Reset

즉, “리스트 전체를 새로 고치기 때문에” 스크롤이 당연히 위로 튀게 된다.


원인 2: 셀 재활용(Recycling) 구조 때문에 수정된 항목 위치에서 스크롤이 흔들림

MAUI CollectionView는 Virtualization을 사용한다.

스크롤이 내려갈 때:

  • 화면 밖 항목은 버리고
  • 재활용된 셀을 재사용하며
  • BindingContext만 새로 바꾼다

이 과정에서
항목 갱신 시 셀 레이아웃이 재계산되면 스크롤이 흔들릴 수 있다.

특히 다음 같은 경우:

  • 항목의 Height가 동적으로 바뀜
  • DataTemplate이 Conditional 방식
  • ItemsSource가 정렬(Sort) 변경됨

원인 3: Replace(=대체) 업데이트가 아닌 Reset 이벤트가 발생

ObservableCollection은 3가지 이벤트를 발생시킨다.

  • Add
  • Remove
  • Replace
  • Reset

이 중 Reset은 스크롤 초기화를 유발한다.

예:
다음 코드는 모든 변경을 Reset으로 처리한다.

 
Items.Clear();
foreach (var x in filtered)
    Items.Add(x);
 
 

이 경우 UI는 다음처럼 판단한다.

“이 리스트는 완전히 새로운 목록이다 → 스크롤 위치를 처음으로 재설정하자”


원인 4: 필터링(Search) 후 ItemsSource를 원래 리스트로 되돌릴 때 항상 초기화됨

예:

Items = new ObservableCollection<MyItem>(AllItems.Where(...));
 
 

필터링이 끝나고 원래 리스트로 돌아가는 순간
항목 수가 크게 변한 것처럼 보여
CollectionView는 스크롤 위치를 초기화한다.


원인 5: Android iOS 플랫폼 차이

Android는 Virtualization이 강하게 적용되어 있어 위치가 튀기 쉽고,
iOS는 UI 스레드 성능이 좋아 상대적으로 부드럽다.

따라서 동일 코드라도 Android에서 문제가 더 많이 발생한다.


3. 실전에서 흔히 보는 잘못된 패턴 3가지

❌ 잘못된 패턴 1 — 필터링 후 Items 새로 생성

Items = new ObservableCollection<MyItem>(filtered);
 
 

스크롤 초기화 100% 발생.


❌ 잘못된 패턴 2 — Clear + Add 반복

 
Items.Clear();
foreach (var x in list)
    Items.Add(x);
 
 

ObservableCollection은 Reset 이벤트를 발생시키므로 스크롤 유지 불가능.


❌ 잘못된 패턴 3 — DataTemplate이 자주 바뀌는 조건부 UI

DataTemplate이 바뀌면 셀이 완전히 재생성되므로 스크롤이 초기화된다.


4. 스크롤 초기화를 방지하는 정석 전략

이제 실전적으로 검증된 해결책을 제시한다.


✔ 전략 1: ItemsSource 교체 금지, 기존 ObservableCollection을 유지한 채 Add/Remove만 수행

예:

 
Items.Clear();  // ❌ 금지
Items = new ObservableCollection<MyItem>(); // ❌ 금지
 
 

올바른 방식:

 
Items.Clear();
foreach (var x in filtered)
    Items.Add(x);
 
 

물론 이것도 Reset을 유발할 수 있으므로,
다음 전략과 함께 사용하는 것이 중요하다.


✔ 전략 2: CollectionView의 스크롤 위치를 저장 후 복원

 
double offset = MyCollectionView.ScrollY;

MyCollectionView.ScrollTo(offset, position: ScrollToPosition.Start, animate: false);
 
 

MAUI 8부터 안정적으로 동작하는 구조.


✔ 전략 3: 항목을 변경할 때 Replace가 아닌 Modify 방식 사용

예:

 
var item = Items[index];
item.Name = "Updated"; // 내부 값만 수정
 
 

ObservableCollection Replace 이벤트가 아닌
PropertyChanged만 발생하므로 스크롤 영향이 거의 없다.


✔ 전략 4: 필터링 구조 변경 — ItemsSource를 교체하지 않고 "IsVisible" 활용

필터링 시 이렇게 하지 말고:

Items = new ObservableCollection<MyItem>(filtered);
 
 

이렇게 설계:

 
foreach (var item in Items)
    item.IsVisible = MatchesFilter(item);
 
 

그리고 XAML:

 
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid IsVisible="{Binding IsVisible}">
 
 

스크롤은 절대 초기화되지 않는다.


✔ 전략 5: Android에서 스크롤 튐 완화를 위한 handler 설정

 
#if ANDROID
CollectionViewHandler.Mapper.AppendToMapping("NoAnimation", (handler, view) =>
{
    handler.PlatformView.ItemAnimator = null;
});
#endif
 
 

애니메이션 제거로 스크롤 튐이 크게 감소한다.


5. 실전 예제 — 필터링(Search) 시 스크롤 유지하는 최적 구조

✔ ViewModel

 
public ObservableCollection<MyItem> Items { get; } = new();

private void ApplyFilter(string keyword)
{
    foreach (var item in Items)
        item.IsVisible = item.Title.Contains(keyword, StringComparison.OrdinalIgnoreCase);
}
 
 

✔ XAML

 
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Frame IsVisible="{Binding IsVisible}">
                <Label Text="{Binding Title}" />
            </Frame>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
 
 

이 방식의 장점:

  • ItemsSource 변경 없음
  • 스크롤 유지
  • Virtualization 유지
  • UI 부드러움
  • 대용량 데이터에서도 빠름

6. 마무리 — 스크롤 초기화 문제를 해결하면 UX 품질이 크게 향상된다

CollectionView 스크롤 초기화 문제는 단순한 버그가 아니라

  • Virtualization 구조
  • ItemsSource 변경 방식
  • ObservableCollection 이벤트
  • 셀 재활용
  • 플랫폼별 렌더링 차이

가 모두 연결된 복합적인 문제이다.

이 글에서 제시한 전략을 적용하면:

  • 스크롤이 튀지 않고
  • 필터링이 부드럽게 동작하며
  • 항목 변경 시 UI 흔들림 없이
  • 안정적인 UX를 제공할 수 있다.

MAUI UI 성능 품질이 확실히 올라간다.

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

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

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

목차