성능 최적화: 데이터 바인딩
Windows Presentation Foundation (WPF) 데이터 바인딩은 응용 프로그램에서 데이터를 표시하고 데이터와 상호 작용하는 간단하고 편리한 방법입니다. 다양한 데이터 소스에서 CLR 개체 및 XML의 형태로 데이터에 요소를 바인딩할 수 있습니다.
이 항목에서는 데이터 바인딩과 관련된 성능 권장 사항을 제공합니다.
이 항목에는 다음 단원이 포함되어 있습니다.
- 데이터 바인딩 참조를 확인하는 방법
- 대형 CLR 개체에 바인딩
- ItemsSource에 바인딩
- IList를 IEnumerable이 아니라 ItemsControl에 바인딩
- 데이터 바인딩만을 위해 CLR 개체를 XML로 변환하지 마십시오
- 관련 항목
데이터 바인딩 참조를 확인하는 방법
데이터 바인딩 성능 문제를 다루기 전에 Windows Presentation Foundation (WPF) 데이터 바인딩 엔진에서 바인딩의 개체 참조를 확인하는 방법을 살펴보는 것이 도움이 됩니다.
Windows Presentation Foundation (WPF) 데이터 바인딩의 소스는 CLR 개체일 수 있습니다. CLR 개체의 속성, 하위 속성 또는 인덱서에 바인딩할 수 있습니다. 바인딩 참조는 Microsoft .NET Framework 리플렉션이나 ICustomTypeDescriptor를 사용하여 확인합니다. 다음은 바인딩의 개체 참조를 확인하는 세 가지 방법입니다.
첫 번째 방법에서는 리플렉션을 사용합니다. 이 경우 PropertyInfo 개체를 사용하여 속성의 특성을 검색하고 속성 메타데이터에 대한 액세스를 제공합니다. ICustomTypeDescriptor 인터페이스를 사용할 경우 데이터 바인딩 엔진은 이 인터페이스를 통해 속성 값에 액세스합니다. ICustomTypeDescriptor 인터페이스는 개체에 정적 속성 집합이 없는 경우에 특히 유용합니다.
INotifyPropertyChanged 인터페이스를 구현하거나 TypeDescriptor에 연결된 변경 알림을 사용하여 속성 변경 알림을 제공할 수 있습니다. 그러나 속성 변경 알림을 구현하는 더 나은 전략은 INotifyPropertyChanged를 사용하는 것입니다.
소스 개체가 CLR 개체이고 소스 속성이 CLR 속성이 경우 Windows Presentation Foundation (WPF) 데이터 바인딩 엔진에서는 먼저 소스 개체에 대해 리플렉션을 사용하여 TypeDescriptor를 가져온 다음 PropertyDescriptor에 대해 쿼리해야 합니다. 이러한 일련의 리플렉션 작업은 너무 많은 시간을 소비할 수 있으므로 성능 면에서 좋지 않습니다.
개체 참조를 확인하는 두 번째 방법에서는 INotifyPropertyChanged 인터페이스를 구현하는 CLR 소스 개체와 CLR 속성인 소스 속성을 사용합니다. 이 경우 데이터 바인딩 엔진에서는 소스 형식에 대해 직접 리플렉션을 사용하여 필요한 속성을 가져옵니다. 이 또한 최적의 방법은 아니지만 첫 번째 방법보다 작업 집합 요구 사항이 적어 비용이 적게 듭니다.
개체 참조를 확인하는 세 번째 방법에서는 DependencyObject인 소스 개체와 DependencyProperty인 소스 속성을 사용합니다. 이 경우 데이터 바인딩 엔진에서 리플렉션을 사용할 필요가 없습니다. 대신 속성 엔진과 데이터 바인딩 엔진이 함께 속성 참조를 독립적으로 확인합니다. 이것이 데이터 바인딩에서 사용되는 개체 참조를 확인하는 최적의 방법입니다.
아래 표에서는 이러한 세 가지 방법을 사용하여 TextBlock 요소 1000개의 Text 속성에 데이터를 바인딩할 때 속도를 비교합니다.
TextBlock의 Text 속성을 다음에 바인딩 |
바인딩 시간(ms) |
바인딩 시간을 포함한 렌더링 시간(ms) |
---|---|---|
CLR 개체의 속성 |
115 |
314 |
INotifyPropertyChanged를 구현하는 CLR 개체의 속성 |
115 |
305 |
90 |
263 |
대형 CLR 개체에 바인딩
수천 개의 속성이 포함된 단일 CLR 개체에 데이터 바인딩하는 경우 성능에 상당한 영향을 미칩니다. 이 단일 개체를 적은 수의 속성을 포함하는 여러 CLR 개체로 나누어 이러한 영향을 최소화할 수 있습니다. 아래 표에서는 하나의 큰 CLR 개체와 여러 개의 작은 개체에 데이터 바인딩할 때의 바인딩 시간과 렌더링 시간을 비교해서 보여 줍니다.
1000개의 TextBlock 개체를 다음에 바인딩 |
바인딩 시간(ms) |
바인딩 시간을 포함한 렌더링 시간(ms) |
---|---|---|
1000개의 속성이 있는 CLR 개체 |
950 |
1200 |
하나의 속성이 있는 1000개의 CLR 개체 |
115 |
314 |
ItemsSource에 바인딩
ListBox에 표시할 직원 목록이 저장된 CLR List<T> 개체가 있다고 가정합니다. 이 두 개체 간의 대응 관계를 만들려면 직원 목록을 ListBox의 ItemsSource 속성에 바인딩할 수 있습니다. 그런데 그룹에 새 직원이 입사했다고 가정합니다. 이 새 직원을 바인딩된 ListBox 값에 삽입하려는 경우 이 직원을 직원 목록에 추가하기만 하면 데이터 바인딩 엔진에서 이 변경 사항을 자동으로 인식할 것으로 생각할 수도 있습니다. 이 가정은 잘못된 것입니다. 실제로는 변경 사항이 ListBox에 자동으로 반영되지 않습니다. 이는 CLR List<T> 개체에서 컬렉션 변경 이벤트를 자동으로 발생시키지 않기 때문입니다. ListBox에서 변경 내용을 반영하게 하려면 직원 목록을 새로 만들어 ListBox의 ItemsSource 속성에 다시 연결해야 합니다. 이 솔루션은 효과는 있지만 성능에 큰 영향을 줍니다. ListBox의 ItemsSource를 다시 할당할 때마다 ListBox에서는 먼저 이전 항목을 버리고 전체 목록을 다시 생성합니다. ListBox가 복잡한 DataTemplate에 매핑되어 있는 경우에는 성능 영향이 더 커집니다.
이 문제에 대한 효율적인 솔루션은 직원 목록을 ObservableCollection<T>으로 만드는 것입니다. ObservableCollection<T> 개체에서는 데이터 바인딩 엔진에서 수신할 수 있는 변경 알림을 발생시킵니다. 이 이벤트는 전체 목록을 다시 생성하지 않고 ItemsControl의 항목을 추가하거나 제거합니다.
아래 표에서는 한 항목을 추가할 때 ListBox 업데이트에 걸리는 시간을 보여 줍니다(UI 시각화 해제한 경우). 첫 행의 숫자는 CLR List<T> 개체를 ListBox 요소의 ItemsSource에 바인딩하는 경우 경과된 시간을 나타냅니다. 둘째 행의 숫자는 ObservableCollection<T>을 ListBox 요소의 ItemsSource에 바인딩하는 경우 경과된 시간을 나타냅니다. ObservableCollection<T> 데이터 바인딩 전략을 사용하면 상당한 시간이 절약됨을 알 수 있습니다.
ItemsSource를 다음에 데이터 바인딩 |
1개 항목의 업데이트 시간(ms) |
---|---|
CLR List<T> 개체 |
1656 |
20 |
IList를 IEnumerable이 아니라 ItemsControl에 바인딩
ItemsControl 개체에 IList<T> 또는 IEnumerable 중에서 무엇을 바인딩할지 선택해야 하는 경우 IList<T> 개체를 선택하는 것이 좋습니다. IEnumerable을 ItemsControl에 바인딩하면 WPF에서 래퍼 IList<T> 개체를 만듭니다. 따라서 두 번째 개체의 불필요한 오버헤드로 인해 성능에 영향을 줍니다.
데이터 바인딩만을 위해 CLR 개체를 XML로 변환하지 마십시오
WPF에서는 XML 콘텐츠에 데이터바인딩할 수 있지만 XML 콘텐츠의 데이터 바인딩은 CLR 개체의 데이터 바인딩보다 느립니다. 데이터 바인딩만을 위해서는 CLR 개체 데이터를 XML로 변환하지 마십시오.
참고 항목
작업
연습: WPF 응용 프로그램에서 응용 프로그램 데이터 캐싱