MAUI CollectionView SelectionChanged, PropertyChanged가 중복 호출되는 이유와 안전한 해결 패턴
CollectionView는 단순히 항목을 나열하는 UI가 아니라 내부적으로 셀 재활용, BindingContext 교체, PropertyChanged 전파, Selection 변경 이벤트가 동시에 작동하는 구조이다.
- 셀 재활용
- BindingContext 교체
- PropertyChanged 전파
- Selection 변경 이벤트
이 구조는 성능 측면에서는 매우 효율적이지만, 잘못된 조합으로 사용되면 이벤트가 예상보다 여러 번 실행되는 문제가 발생한다.
그리고 이 문제는 앱 전체의 흐름을 깨뜨리며, 특히 다운로드 UI, 재생목록 UI, 체크박스 기반 선택 UI에서 치명적인 버그로 이어진다.
오늘은 이 문제를 가장 깊이 있는 구조로 분석하고, MAUI CollectionView에서 이벤트를 한 번만 정확히 실행시키는 안정 패턴을 정리한다.
1 왜 이벤트가 중복 실행되는가?
MAUI CollectionView에서 중복 실행은 단순한 실수 때문이 아니라 구조적 특성 때문에 발생한다.
즉, 개발자가 잘못해서가 아니라 MAUI의 내부 설계와 이벤트 순서 때문에 자연스럽게 발생하는 현상이다.
아래 4가지가 실제 원인이다.
1.1 SelectionChanged가 UI 이벤트만이 아니라 내부 로직에서도 발생
CollectionView의 SelectionMode가 Single 또는 Multiple인 경우, 사용자가 클릭하지 않아도 MAUI 내부에서 다음 상황에서 SelectionChanged가 호출된다.
- 화면에 처음 표시될 때
- 스크롤할 때
- 셀이 재활용될 때
- BindingContext가 교체될 때
- UI Layout이 업데이트될 때
따라서 SelectionChanged 이벤트를 로직의 핵심 트리거로 사용하는 것은 매우 위험하다.
1.2 CheckBox 이벤트가 DataTemplate 재활용 시 다시 호출됨
셀 재활용 과정에서 다음 시나리오가 발생한다.
1 기존 셀의 CheckBox.IsChecked 값이 새로운 항목으로 교체
2 BindingContext 변경 → 체크박스 상태 초기화
3 PropertyChanged로 다시 체크값 전달
4 CheckBox.CheckedChanged 이벤트 또 발생
즉 실제로는 한 번 클릭해도 이벤트는 2~4번 호출될 수 있다.
1.3 PropertyChanged가 무한 루프를 발생시키는 경우
Model의 속성이 UI에서 변경되고, UI에서 변경된 값이 다시 Model에 설정되며, 다시 UI로 반영되면서 무한 반복되는 경우가 있다.
public bool IsSelected
{
get => _isSelected;
set
{
_isSelected = value;
OnPropertyChanged();
// 외부 이벤트에서 또 다시 IsSelected를 수정하는 경우 루프 발생
}
}
1.4 CollectionChanged가 트리거 되면서 추가적인 렌더링 발생
ObservableCollection에서 항목 추가 또는 제거가 발생하면 DataTemplate 재활용 구조상 셀 전체가 재렌더링되기도 한다.
- OnBindingContextChanged
- PropertyChanged
- CheckedChanged
- SelectionChanged
이 조합은 UI가 복잡할수록 중복이 기하급수적으로 늘어난다.
2 실제 앱에서 나타나는 문제들
이벤트 중복 호출 문제는 다음 현상으로 드러난다.
2.1 체크박스 클릭 시 UI가 순간적으로 깜빡인다
Binding → UI → Binding 순환이 여러 번 실행되기 때문이다.
2.2 다운로드 버튼이 비정상적으로 여러 번 실행됨
예를 들어 클릭하면 토스트가 연속 2~3번 뜬다.
2.3 선택한 항목이 많아질수록 CollectionView가 느려짐
이벤트 중복이 쌓여서 성능 저하로 연결된다.
2.4 SelectionChanged가 항목을 선택하지 않아도 실행됨
스크롤만 해도 실행되기 때문이다.
2.5 Model.IsSelected가 반대로 잘못 적용되거나 순서가 꼬임
Event loop 문제 때문이다.
3 중복 실행을 구조적으로 해결하기 위한 핵심 전략 7가지
아래 패턴은 MAUI CollectionView에서 이벤트 중복 호출 문제를 해결하는 가장 검증된 방법이다.
전략 1 SelectionMode="None"이 기본
SelectionChanged는 절대 사용하지 않는 것을 원칙으로 한다.
<CollectionView SelectionMode="None" />
선택 로직은 모두 Model.IsSelected 기반으로 통합한다.
전략 2 체크박스는 TwoWay 바인딩 단 하나만 위한 이벤트로 사용
CheckBox를 클릭했을 때 Model.IsSelected만 변경되도록 한다.
절대 CheckedChanged 이벤트에서 UI 조작을 하지 않는다.
전략 3 이벤트 핸들러 내부에서 UI 업데이트 금지
CheckBox.IsChecked = false;
이 코드 하나가 PropertyChanged → UI → 이벤트 호출이라는 중첩 호출의 출발점이 된다.
전략 4 이벤트 방어막 Guard Flag 사용
private bool _updating;
private void CheckBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
if (_updating) return;
try
{
_updating = true;
ViewModel.IsSelected = e.Value;
}
finally
{
_updating = false;
}
}
이 방식은 이중 호출, 재귀 호출, 루프를 완전히 차단하는 가장 안정적인 패턴이다.
전략 5 CollectionChanged는 대량 업데이트 시 단 1회만 발생하도록 한다
PlaylistItems.Clear();
foreach (var item in newItems)
PlaylistItems.Add(item);
절대 새로운 ObservableCollection으로 교체하지 않는다.
전략 6 OnBindingContextChanged에서 UI 초기화는 값 비교 후 적용
if (CheckBox.IsChecked != item.IsSelected)
CheckBox.IsChecked = item.IsSelected;
이 조건문은 이벤트 중복 호출을 크게 줄여준다.
전략 7 단일 진실 원본은 Model로만 유지
UI에서 상태를 저장하는 순간 중복 이벤트는 반드시 발생한다.
따라서 상태는 반드시 Model에서만 관리해야 한다.
4 안정적인 이벤트 구조를 위한 최종 아키텍처
- CollectionView.SelectionMode = None
- CheckBox.IsChecked ↔ Model.IsSelected, TwoWay 바인딩
- UI 초기화는 OnBindingContextChanged에서 수행
- 모델 상태를 기준으로 UI를 설정
- 이벤트 핸들러 내부에서 UI 값 변경 금지
- Guard Flag로 재귀 차단
- ObservableCollection은 Clear 후 Add 방식으로 갱신
이 구조는 수많은 MAUI 개발자들이 최종적으로 채택하는 아키텍처이기도 하다.
마무리
오늘 글에서는 MAUI CollectionView에서 가장 난이도 높은 문제 중 하나인 이벤트 중복 호출 문제를 다뤘다.
핵심 요약
1 SelectionChanged는 내부적으로 반복 호출되므로 사용 금지
2 CheckBox 이벤트는 Model 값만 갱신해야 한다
3 UI를 직접 조작하면 반드시 중복 호출이 발생한다
4 Guard Flag로 재귀를 차단해야 한다
5 Model을 단일 상태 저장소로 유지해야 한다
이 구조를 이해하면 MAUI CollectionView 기반 앱의 안정성이 훨씬 높아지고, 버그 발생 빈도는 극적으로 줄어들 것이다.
'MAUI CollectionView 문제 해결 > Korean Version' 카테고리의 다른 글
| MAUI Dependency Injection 적용 실패 문제 해결, IHostBuilder 구조 분석과 실전 대응 전략 (0) | 2025.12.11 |
|---|---|
| MAUI CommandParameter 전달 실패 문제 해결, 구조적 원인 분석과 실전 대응 전략 (0) | 2025.12.10 |
| MAUI BindingContext 꼬임 문제 해결, 데이터 바인딩 실패 원인과 구조적 해결 전략 (0) | 2025.12.10 |
| MAUI CollectionView에서 비동기 처리로 인해 발생하는 UI 불안정 문제 (0) | 2025.12.09 |
| MAUI CollectionView에서 체크 상태가 즉시 반영되지 않고 UI가 오래된 값을 유지하는 문제의 구조적 원인과 실전 해결 전략 (0) | 2025.12.09 |