MAUI CollectionView는 화면에 표시되는 항목 수가 많아지거나 스크롤이 자주 발생하는 UI에서 반드시 고려해야 할 가상화 개념과 템플릿 재사용 메커니즘을 기반으로 동작합니다. 덕분에 메모리를 절약하고 렌더링 성능을 높이는 장점이 있지만, 반대로 UI 내부 상태가 템플릿과 함께 재사용되는 방식 때문에 체크박스와 같은 상태 기반 컨트롤에서 오류가 발생하기 쉽습니다. 특히 체크박스가 스크롤 이후에 갑자기 풀리거나, 다른 항목의 체크 여부가 복사된 것처럼 반영되는 문제는 CollectionView의 구조적 특성과 개발자의 데이터 흐름 설계 방식이 맞지 않을 때 나타나는 대표적인 현상입니다. 이 글에서는 이러한 오류가 발생하는 근본 원인을 구조적으로 분석하고, 단순한 코드 수정이 아니라 MAUI의 렌더링 메커니즘을 이해한 상태에서 구성해야 하는 실무 기준 해결 전략을 상세하게 다룹니다.

1. 스크롤 시 CheckBox 상태가 재설정되는 이유 — 템플릿 재활용 원리
MAUI CollectionView는 스크롤되는 목록을 효율적으로 그리기 위해 데이터 수와 동일한 ItemTemplate을 생성하지 않습니다. 대신 화면에 보이는 최소한의 템플릿만 만들고, 스크롤 시 기존 템플릿을 새로운 데이터에 연결하는 방식으로 재활용합니다.
이때 문제가 되는 것은 CheckBox의 UI 상태가 템플릿 내부 필드에 남아 있다는 사실입니다. 즉 체크가 되어 있던 셀(template)이 화면에서 사라졌다가 새로운 항목을 표시하기 위해 재활용되면, CheckBox는 이전 사용자의 선택 상태를 그대로 유지한 상태로 새로운 항목을 그리게 됩니다.
만약 이 시점에서 데이터 모델의 IsSelected 값과 UI의 체크 상태가 서로 맞지 않는다면, 결국 UI는 이전 항목의 체크 정보가 새 항목에 복사된 것처럼 보이는 문제를 일으킵니다.
이 현상은 다음 조건에서 극심해집니다
- 템플릿 내부 CheckBox의 초기값이 XAML에 직접 정의되어 있는 경우
- CheckBox가 VM의 값과 적절히 동기화되어 있지 않은 경우
- PropertyChanged가 제대로 호출되지 않아 UI가 갱신되지 않은 경우
즉 이는 버그가 아니라, 재활용을 전제로 설계된 구조적 특성을 이해하지 못했을 때 발생하는 자연스러운 결과입니다.
2. MVVM 패턴을 따르지 않을 때 데이터와 UI의 불일치가 심해짐
CollectionView는 화면에서 벗어난 템플릿을 언제든지 재활용하기 때문에
UI 컨트롤 내부에 상태를 저장하는 방식은 절대 안정적인 방법이 될 수 없습니다.
예를 들어 다음과 같은 방식은 잘못된 접근입니다
- CheckBox의 체크 이벤트에서 직접 CheckBox.IsChecked 값을 조작
- Code-behind에서 UI 상태를 저장하고 다시 복원
- 특정 시점에 UI 상태를 강제로 초기화
문제는 UI가 스크롤될 때마다 BindingContext가 빠르게 바뀌기 때문에 UI 내부 상태는 더 이상 신뢰할 수 없다는 점입니다. 결국 체크 상태의 ‘진짜 값’은 반드시 ViewModel에 있어야 하며, UI는 언제 어디서든 단순히 그 값을 표시하는 역할만 해야 합니다.
MAUI의 MVVM 구조는 이를 전제로 설계되어 있으며, UI에서 발생하는 이벤트는 ViewModel로 전달되고, 데이터 변경은 OnPropertyChanged를 통해 UI로 다시 반영되는 형태를 지향합니다.
이 흐름이 지켜지지 않으면 스크롤할 때마다 상태가 무너지고, 템플릿 재활용이 발생할 때 UI가 예측 불가능하게 동작하게 됩니다.
3. ObservableCollection을 사용하지 않으면 UI 동기화 자체가 어렵다
CollectionView는 ObservableCollection을 기준으로 화면을 갱신합니다.
일반 List를 사용할 경우 다음과 같은 문제가 발생합니다
- 항목 변경을 CollectionView가 감지하지 못함
- 스크롤되는 순간 BindingContext가 재설정되면서 UI 상태가 혼재
- 체크 값이 언제 데이터에 반영되었는지 추적이 어려워짐
- 체킹한 정보가 다른 셀에 복사된 것처럼 보이는 현상 발생
예를 들어 사용자가 체크한 항목을 UI에서 직접 읽어 처리하려고 하면, ObservableCollection 기반의 변경 알림이 없는 구조 때문에 UI와 데이터 모델 간의 시간이 맞지 않아 체크 여부가 섞이는 문제가 나타납니다.
따라서 Selection, Toggle, Checked 등 모든 상태는 반드시 ObservableCollection과 ViewModel 속성으로 관리해야 하며, UI는 그 상태를 단순히 반영하는 구조여야 합니다.
4. INotifyPropertyChanged 누락은 체크 오류를 일으키는 가장 직접적인 원인
스크롤 시 템플릿이 재활용되면 BindingContext가 새로운 항목을 가리키게 되는데, 이때 VM의 IsSelected 값이 변경되었음에도 PropertyChanged 이벤트가 일어나지 않으면 UI는 기존 값을 그대로 표시하게 됩니다.
예를 들어 다음과 같은 상황을 생각해 보겠습니다
- A 항목에 체크를 했지만 VM에서 PropertyChanged를 호출하지 않음
- 스크롤함
- 템플릿이 B 항목을 그리기 위해 재사용됨
- 템플릿 내부 CheckBox는 여전히 A의 체크 상태를 유지하고 있음
- 결과적으로 B 항목이 체크된 것처럼 보임
이는 개발자 입장에서는 UI 버그처럼 보이지만
실제로는 데이터 변경 알림 누락으로 인해 VM과 UI가 서로 다른 값을 가지고 있는 상태인 것입니다.
따라서 CheckBox로 관리되는 모든 속성은 반드시 OnPropertyChanged 기반으로 구현해야 하며, UI는 항상 VM의 값만을 표시하도록 유지해야 합니다.
5. 복잡한 ItemTemplate은 CheckBox 이벤트 충돌 가능성을 높인다
ItemTemplate 내부에 여러 Grid, Layout, Border가 중첩되어 있거나, Label과 GestureRecognizer가 혼재되면 CheckBox까지 터치 이벤트가 도달하지 못하는 경우가 있습니다.
이러한 경우 CheckBox의 체크 이벤트가 무시되거나, 연속 클릭 시 상태가 반영되지 않아서 결국 스크롤 시 상태 오류처럼 보이게 됩니다.
특히 안드로이드에서는 터치 우선순위가 매우 민감하게 작동하기 때문에
- InputTransparent 속성
- GestureRecognizer
- Tap 처리 레이어
이 세 요소는 반드시 점검해야 합니다.
템플릿을 간결하게 만들고
CheckBox가 직접 터치를 받을 수 있도록 구조를 정리하면
스크롤 이후에도 상태가 안정적으로 유지됩니다.
6. 올바른 구조를 사용할 때 체크 상태는 스크롤과 상관없이 완벽히 유지된다
스크롤 기반 체크 초기화 문제는 표면적으로는 UI의 문제처럼 보이지만
실제로는 MAUI CollectionView의 렌더링 방식과 MVVM 데이터 구조를 제대로 이해하지 못했을 때 나타나는 자연스러운 현상입니다.
다음 다섯 가지 원칙을 지키면 이 문제는 완전히 해결됩니다
- 템플릿 재활용 구조를 이해하고 UI에 상태를 저장하지 않는다
- 체크 상태의 단일 진실 소스는 항상 ViewModel이 되도록 구성한다
- ObservableCollection을 사용해 변경 사항을 UI가 자동으로 감지하도록 한다
- INotifyPropertyChanged를 누락하지 않고 데이터 변경을 정확하게 알린다
- 복잡한 Template 구조를 단순화해 CheckBox 이벤트가 직접 전달되게 한다
이 원칙을 기반으로 설계하면 스크롤을 아무리 많이 해도
CheckBox는 데이터 모델의 값을 반영해 항상 일관된 상태를 유지하게 됩니다.