03.1 ObservableObject

1️⃣ ObservableProperty 개요
📚 Microsoft Docs: Microsoft CommunityToolkit MVVM - ObservableObject

✅ ObservableProperty 개요
- ObservableObject는 MVVM Toolkit에서 제공하는 MVVM의 핵심 클래스입니다.
- ObservableObject는 INotifyPropertyChanged, INotifyPropertyChanging 인터페이스가 구현되어 있습니다.
- 즉, MVVM 구조에서 Observable하지 않은 기존 객체를 MVVM에 통합하는 핵심 기술입니다.
✅ ObservableProperty 사용법
ㅇ
✅ 예제 소스 코드
2️⃣ 비교 ( 전통적 구현 vs ObservableObject vs [ObservableProperty] )
✔️ 1. 동작 시나리오

- 공통 시나리오: 이름(Name)을 입력하고
- 인사 메시지(Greeting)를 출력
✔️ 2. 구현
2.1 (공통) XAML 구현
xmlns:vm="clr-namespace:WpfApp1.ViewModel" Title="INotifyPropertyChanged 비교" Height="200" Width="300"> <Window.DataContext> <vm:ManualViewModel /> <!-- 변경 가능: ManualViewModel, ObjectViewModel --> </Window.DataContext> <StackPanel Margin="20"> <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Margin="0 0 0 10" /> <TextBlock Text="{Binding Greeting}" FontSize="16" /> </StackPanel> </Window>
2.2 INotifyPropertyChanged
class ManualViewModel : INotifyPropertyChanged { private string? name; public string Name { get => name ?? string.Empty; set { if (name != value) { name = value; OnPropertyChanged(nameof(Name)); OnPropertyChanged(nameof(Greeting)); } } } public string Greeting => $"안녕하세요, {Name}님!"; public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
🟢 장점
- Source Generator 없이 작동
- 완전한 제어 가능
🔴 단점
- 매 속성마다 반복되는 boilerplate 코드
2.2 [ObservableProperty]
public partial class AttributeViewModel : ObservableObject { [ObservableProperty] [NotifyPropertyChangedFor(nameof(Greeting))] private string? name; public string Greeting => $"안녕하세요, {Name}님!"; }
🟢 장점
- 가장 간결함 (속성 한 줄이면 끝)
- OnNameChanged 같은 partial method 지원
- 관련된 속성 알림도 연동 가능 (NotifyPropertyChangedFor)
- 커맨드 상태 자동 갱신도 가능 (NotifyCanExecuteChangedFor)
🔴 단점
- C# Source Generator 기능이 필요한 환경에서만 작동 (Visual Studio 2019+ / .NET 5+)
2.3 ObservableObject
public class ObjectViewModel : ObservableObject { private string? name; public string Name { get => name ?? string.Empty; set { if (SetProperty(ref name, value)) { OnPropertyChanged(nameof(Greeting)); } } } public string Greeting => $"안녕하세요, {Name}님!"; }
SetProperty 매개변수 | 설명 |
ref name | 실제 저장되는 필드 |
value | 새로 설정할 값 |
내부 처리 | 값이 다를 경우 필드를 변경하고 PropertyChanged 이벤트 발생 |
🟢 장점
- SetProperty를 통해 OnPropertyChanged를 깔끔하게 처리
- Source Generator 없이도 깔끔한 구조
- 커스텀 로직을 섞기 용이함
🔴 단점
- 여전히 property 선언을 직접 작성해야 함
- [ObservableProperty]보다 코드량 많음
✔️ 3. MVVM 구현 방식 비교
방식 | 핵심 키워드 | 코드 작성량 | 유지보수성 | 가독성 | Source Generator |
전통적 방식 | INotifyPropertyChanged 수동 구현 | ❌ 많음 | ❌ 낮음 | ❌ 불편 | ❌ 필요 없음 |
Toolkit 자동 | [ObservableProperty] + [INotifyPropertyChanged] |
✅ 매우 적음 | ✅ 높음 | ✅ 매우 좋음 | ✅ 필요 |
Toolkit 수동 | ObservableObject 상속 SetProperty 수동 작성 |
🔸 보통 | 🔸 보통 | 🔸 명시적 | ✅ 필요 |
3️⃣ 예제1
✔️ 구현 목표
- ObservableObject + SetProperty 방식과
[ObservableProperty] 방식의 차이점을 직접 구현하여 비교할 수 있도록 구현
📁 프로젝트 구조
ObservableComparisonDemo/ ├── ViewModel/ │ ├── SetPropertyViewModel.cs │ └── ObservablePropertyViewModel.cs ├── MainWindow.xaml └── MainWindow.xaml.cs
✔️ ViewModel 클래스 만들기 (ObservablePropertyViewModel.cs)

public partial class ObservablePropertyViewModel : ObservableObject { // 자동으로 public Name 속성 생성 + PropertyChanged 알림 [ObservableProperty] private string? name; }
✔️ ViewModel 클래스 만들기 (SetPropertyViewModel.cs)

public class SetPropertyViewModel : ObservableObject { private string name = string.Empty; public string Name { get => name; set => SetProperty(ref name, value); // 수동 구현 } }
✔️ XAML 화면 구성

Title="ObservableProperty 비교 실습" Height="250" Width="400"> <StackPanel Margin="20"> <TextBlock Text="① SetProperty 방식 (수동)" FontWeight="Bold"/> <StackPanel Orientation="Vertical"> <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,5"/> <TextBlock Text="{Binding Name}" /> </StackPanel> <Separator Margin="10"/> <TextBlock Text="② ObservableProperty 방식 (자동)" FontWeight="Bold"/> <StackPanel Orientation="Vertical"> <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,5"/> <TextBlock Text="{Binding Name}" /> </StackPanel> </StackPanel>
✔️ XAML 코드 비하인드 구성

// 아래 둘 중 하나를 주석 해제하여 실습 // DataContext = new SetPropertyViewModel(); DataContext = new ObservablePropertyViewModel();
4️⃣ 예제2
✔️ 구현 목표
- 탭 전환 비교
UI ObservableObject + SetProperty 방식 VS [ObservableProperty] 방식 비교 - OnChanged 후처리 비교
SetProperty 방식의 조건부 후처리 vs [ObservableProperty]의 partial void On___Changed() 활용
📁 프로젝트 구조
WPF_ToolKit06_Ex01/ ├── ViewModel/ │ ├── SetPropertyViewModel.cs │ └── ObservablePropertyViewModel.cs ├── MainWindow.xaml └── MainWindow.xaml.cs
✔️ ViewModel 클래스 만들기 (ObservablePropertyViewModel.cs)
using CommunityToolkit.Mvvm.ComponentModel; namespace WPF_ToolKit06_Ex01.ViewModels { // SetPropertyViewModel은 MVVM Toolkit의 ObservableObject를 상속받아 // INotifyPropertyChanged를 간편하게 구현하는 ViewModel입니다. // 이 클래스에서는 수동으로 속성을 정의하고, SetProperty() 메서드를 통해 속성 변경 시 알림을 발생시킵니다. public class SetPropertyViewModel : ObservableObject { // 이름을 저장하는 private 필드 private string name = string.Empty; // 사용자에게 보여줄 메시지를 저장하는 private 필드 private string message = string.Empty; // Name 속성은 사용자의 입력값을 바인딩합니다. // set 접근자 내부에서 SetProperty()를 사용하면 값이 변경될 때 자동으로 UI에 알림이 전파됩니다. // 또한 값이 바뀐 이후에 메시지를 업데이트하는 후처리 로직을 직접 구현할 수 있습니다. public string Name { get => name; set { // 값이 실제로 바뀌었을 때만 PropertyChanged 이벤트 발생 if (SetProperty(ref name, value)) { // 후처리: 메시지 속성에 변경 내용을 저장 Message = $"[SetProperty] 이름이 '{name}'으로 변경되었습니다."; } } } // Message 속성은 후처리 결과를 화면에 출력하기 위해 바인딩됩니다. // SetProperty를 통해 변경되었음을 UI에 알립니다. public string Message { get => message; set => SetProperty(ref message, value); } } }
✔️ ViewModel 클래스 만들기 (SetPropertyViewModel.cs)
using CommunityToolkit.Mvvm.ComponentModel; namespace WPF_ToolKit06_Ex01.ViewModels { // ObservablePropertyViewModel 클래스는 MVVM Toolkit의 기능을 활용하여 // 속성을 간결하게 정의하고, 변경 알림 및 후처리까지 자동화하는 예제입니다. // ObservableObject를 상속하여 INotifyPropertyChanged를 내부적으로 처리하며, // [ObservableProperty] 특성을 사용해 필드 기반으로 속성을 자동 생성합니다. public partial class ObservablePropertyViewModel : ObservableObject { // [ObservableProperty]를 선언하면 다음과 같은 기능이 자동 생성됩니다: // - public string Name { get; set; } 속성 // - 값이 변경되면 PropertyChanged 이벤트 자동 발생 // - partial void OnNameChanged(string value) 메서드 호출 [ObservableProperty] private string name = string.Empty; [ObservableProperty] private string message = string.Empty; // OnNameChanged 메서드는 Name 속성의 값이 변경되었을 때 자동 호출됩니다. // 여기서는 후처리로 Message 속성을 업데이트하여 UI에 알림 메시지를 전달합니다. // MVVM Toolkit은 이러한 후처리 메서드를 partial 메서드로 지원합니다. partial void OnNameChanged(string value) { Message = $"[ObservableProperty] 이름이 '{value}'으로 변경되었습니다."; } } }
✔️ XAML 화면 구성
xmlns:vm="clr-namespace:WPF_ToolKit06_Ex01.ViewModels" Title="비교 실습" Height="300" Width="400"> <!-- ViewModel 인스턴스를 정적으로 생성하여 리소스로 등록 --> <Window.Resources> <!-- SetProperty 방식 ViewModel 등록 --> <vm:SetPropertyViewModel x:Key="SetVM"/> <!-- ObservableProperty 방식 ViewModel 등록 --> <vm:ObservablePropertyViewModel x:Key="ObsVM"/> </Window.Resources> <Grid> <!-- TabControl을 사용하여 두 가지 ViewModel의 차이를 비교 실습할 수 있도록 구성 --> <TabControl Margin="10"> <!-- 탭 1: 수동 방식 (SetProperty) --> <TabItem Header="SetProperty 방식"> <!-- 해당 탭의 DataContext는 SetVM 리소스(ViewModel) --> <StackPanel DataContext="{StaticResource SetVM}" Margin="10"> <TextBlock Text="이름 입력:"/> <!-- TextBox: Name 속성과 양방향 바인딩. 텍스트가 변경될 때마다 ViewModel에 전달됨 --> <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Text="메시지:" Margin="0,10,0,0"/> <!-- 메시지 출력: ViewModel에서 후처리로 생성된 문자열 출력 --> <TextBlock Text="{Binding Message}" FontWeight="Bold"/> </StackPanel> </TabItem> <!-- 탭 2: 자동 방식 (ObservableProperty) --> <TabItem Header="ObservableProperty 방식"> <!-- 해당 탭의 DataContext는 ObsVM 리소스(ViewModel) --> <StackPanel DataContext="{StaticResource ObsVM}" Margin="10"> <TextBlock Text="이름 입력:"/> <!-- ObservableProperty에 의해 자동 생성된 Name 속성과 바인딩 --> <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Text="메시지:" Margin="0,10,0,0"/> <!-- OnNameChanged()를 통해 자동 생성된 메시지 출력 --> <TextBlock Text="{Binding Message}" FontWeight="Bold"/> </StackPanel> </TabItem> </TabControl> </Grid> </Window>
5️⃣ 예제3 - Wrapping a Non-Observable Model
✔️ 구현 목표
항목 | 설명 |
문제 상황 | Model은 DB 엔터티나 외부 API 결과 등 외부 구조체인 경우 많음 |
제약 | 대부분 INotifyPropertyChanged를 구현하지 않음 (바인딩 불가) |
해결 방법 | ViewModel이 Model을 "감싸고(wrap)" 속성을 노출하며 변경 감지를 구현 |
핵심 도구 | ObservableObject + ViewModel 속성에서 SetProperty() 사용 |
📁 프로젝트 구조
WPF_ToolKit06_Ex01/ ├── ViewModel/ │ ├── SetPropertyViewModel.cs │ └── ObservablePropertyViewModel.cs ├── MainWindow.xaml └── MainWindow.xaml.cs
✔️ ViewModel 클래스 만들기 (ObservablePropertyViewModel.cs)
using CommunityToolkit.Mvvm.ComponentModel; namespace WPF_ToolKit06_Ex01.ViewModels { // SetPropertyViewModel은 MVVM Toolkit의 ObservableObject를 상속받아 // INotifyPropertyChanged를 간편하게 구현하는 ViewModel입니다. // 이 클래스에서는 수동으로 속성을 정의하고, SetProperty() 메서드를 통해 속성 변경 시 알림을 발생시킵니다. public class SetPropertyViewModel : ObservableObject { // 이름을 저장하는 private 필드 private string name = string.Empty; // 사용자에게 보여줄 메시지를 저장하는 private 필드 private string message = string.Empty; // Name 속성은 사용자의 입력값을 바인딩합니다. // set 접근자 내부에서 SetProperty()를 사용하면 값이 변경될 때 자동으로 UI에 알림이 전파됩니다. // 또한 값이 바뀐 이후에 메시지를 업데이트하는 후처리 로직을 직접 구현할 수 있습니다. public string Name { get => name; set { // 값이 실제로 바뀌었을 때만 PropertyChanged 이벤트 발생 if (SetProperty(ref name, value)) { // 후처리: 메시지 속성에 변경 내용을 저장 Message = $"[SetProperty] 이름이 '{name}'으로 변경되었습니다."; } } } // Message 속성은 후처리 결과를 화면에 출력하기 위해 바인딩됩니다. // SetProperty를 통해 변경되었음을 UI에 알립니다. public string Message { get => message; set => SetProperty(ref message, value); } } }
✔️ ViewModel 클래스 만들기 (SetPropertyViewModel.cs)
using CommunityToolkit.Mvvm.ComponentModel; namespace WPF_ToolKit06_Ex01.ViewModels { // ObservablePropertyViewModel 클래스는 MVVM Toolkit의 기능을 활용하여 // 속성을 간결하게 정의하고, 변경 알림 및 후처리까지 자동화하는 예제입니다. // ObservableObject를 상속하여 INotifyPropertyChanged를 내부적으로 처리하며, // [ObservableProperty] 특성을 사용해 필드 기반으로 속성을 자동 생성합니다. public partial class ObservablePropertyViewModel : ObservableObject { // [ObservableProperty]를 선언하면 다음과 같은 기능이 자동 생성됩니다: // - public string Name { get; set; } 속성 // - 값이 변경되면 PropertyChanged 이벤트 자동 발생 // - partial void OnNameChanged(string value) 메서드 호출 [ObservableProperty] private string name = string.Empty; [ObservableProperty] private string message = string.Empty; // OnNameChanged 메서드는 Name 속성의 값이 변경되었을 때 자동 호출됩니다. // 여기서는 후처리로 Message 속성을 업데이트하여 UI에 알림 메시지를 전달합니다. // MVVM Toolkit은 이러한 후처리 메서드를 partial 메서드로 지원합니다. partial void OnNameChanged(string value) { Message = $"[ObservableProperty] 이름이 '{value}'으로 변경되었습니다."; } } }
✔️ XAML 화면 구성
xmlns:vm="clr-namespace:WPF_ToolKit06_Ex01.ViewModels" Title="비교 실습" Height="300" Width="400"> <!-- ViewModel 인스턴스를 정적으로 생성하여 리소스로 등록 --> <Window.Resources> <!-- SetProperty 방식 ViewModel 등록 --> <vm:SetPropertyViewModel x:Key="SetVM"/> <!-- ObservableProperty 방식 ViewModel 등록 --> <vm:ObservablePropertyViewModel x:Key="ObsVM"/> </Window.Resources> <Grid> <!-- TabControl을 사용하여 두 가지 ViewModel의 차이를 비교 실습할 수 있도록 구성 --> <TabControl Margin="10"> <!-- 탭 1: 수동 방식 (SetProperty) --> <TabItem Header="SetProperty 방식"> <!-- 해당 탭의 DataContext는 SetVM 리소스(ViewModel) --> <StackPanel DataContext="{StaticResource SetVM}" Margin="10"> <TextBlock Text="이름 입력:"/> <!-- TextBox: Name 속성과 양방향 바인딩. 텍스트가 변경될 때마다 ViewModel에 전달됨 --> <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Text="메시지:" Margin="0,10,0,0"/> <!-- 메시지 출력: ViewModel에서 후처리로 생성된 문자열 출력 --> <TextBlock Text="{Binding Message}" FontWeight="Bold"/> </StackPanel> </TabItem> <!-- 탭 2: 자동 방식 (ObservableProperty) --> <TabItem Header="ObservableProperty 방식"> <!-- 해당 탭의 DataContext는 ObsVM 리소스(ViewModel) --> <StackPanel DataContext="{StaticResource ObsVM}" Margin="10"> <TextBlock Text="이름 입력:"/> <!-- ObservableProperty에 의해 자동 생성된 Name 속성과 바인딩 --> <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" /> <TextBlock Text="메시지:" Margin="0,10,0,0"/> <!-- OnNameChanged()를 통해 자동 생성된 메시지 출력 --> <TextBlock Text="{Binding Message}" FontWeight="Bold"/> </StackPanel> </TabItem> </TabControl> </Grid> </Window>
댓글을 사용할 수 없습니다.