이번 강좌에서는 단순 이벤트 기반 메모장이 아니라, 실무에서 가장 많이 사용하는 MVVM(Model-View-ViewModel) 패턴으로 메모장을 만들어 보겠습니다.
MVVM이란?
WPF에서 UI와 비즈니스 로직을 분리하기 위한 패턴입니다.
기존 방식
View(XAML)
↓
Code Behind
MVVM 방식
View
↓
ViewModel
↓
Model
구조
MemoProject
│
├─ Models
│ └─ MemoModel.cs
│
├─ ViewModels
│ └─ MainViewModel.cs
│
├─ Commands
│ └─ RelayCommand.cs
│
└─ Views
└─ MainWindow.xaml
Model 만들기
Models/MemoModel.cs
namespace MemoProject.Models
{
public class MemoModel
{
public string Content? { get; set; }
}
}
Model은 데이터만 보관합니다.
RelayCommand 만들기
MVVM에서는 Button Click 대신 Command를 사용합니다.
Commands/RelayCommand.cs
using System;
using System.Windows.Input;
namespace MemoProject.Commands
{
public class RelayCommand(Action execute) : ICommand
{
private readonly Action _execute = execute;
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
_execute();
}
public event EventHandler? CanExecuteChanged;
}
}
ViewModel 만들기
ViewModels/MainViewModel.cs
using MemoProject.Commands;
using Microsoft.Win32;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace MemoProject.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
private string _memoText;
public string MemoText
{
get => _memoText;
set
{
_memoText = value;
OnPropertyChanged();
}
}
public ICommand NewCommand { get; }
public ICommand SaveCommand { get; }
public ICommand OpenCommand { get; }
public MainViewModel()
{
NewCommand = new RelayCommand(NewMemo);
SaveCommand = new RelayCommand(SaveMemo);
OpenCommand = new RelayCommand(OpenMemo);
}
private void NewMemo()
{
MemoText = "";
}
private void SaveMemo()
{
SaveFileDialog dialog = new SaveFileDialog();
dialog.Filter = "텍스트 파일|*.txt";
if (dialog.ShowDialog() == true)
{
File.WriteAllText(
dialog.FileName,
MemoText);
}
}
private void OpenMemo()
{
OpenFileDialog dialog =
new OpenFileDialog();
dialog.Filter = "텍스트 파일|*.txt";
if (dialog.ShowDialog() == true)
{
MemoText =
File.ReadAllText(
dialog.FileName);
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(
[CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(
this,
new PropertyChangedEventArgs(propertyName));
}
}
}
View(XAML) 작성
MainWindow.xaml
<Window x:Class="MemoProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MVVM 메모장"
Height="450"
Width="800">
<DockPanel>
<StackPanel Orientation="Horizontal"
DockPanel.Dock="Top"
Margin="5">
<Button Content="새로 만들기"
Width="100"
Margin="5"
Command="{Binding NewCommand}"/>
<Button Content="열기"
Width="100"
Margin="5"
Command="{Binding OpenCommand}"/>
<Button Content="저장"
Width="100"
Margin="5"
Command="{Binding SaveCommand}"/>
</StackPanel>
<TextBox Text="{Binding MemoText,
UpdateSourceTrigger=PropertyChanged}"
AcceptsReturn="True"
AcceptsTab="True"
FontSize="15"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"/>
</DockPanel>
</Window>
ViewModel 연결하기
MainWindow.xaml.cs
using MemoProject.ViewModels;
using System.Windows;
namespace MemoProject
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext =
new MainViewModel();
}
}
}

데이터 바인딩 이해하기
이 부분이 MVVM의 핵심입니다.
<TextBox Text="{Binding MemoText}" />
동작 과정
사용자 입력
↓
TextBox
↓
MemoText 속성
↓
ViewModel
반대로
ViewModel
↓
MemoText 변경
↓
UI 자동 변경
즉 UI와 코드가 직접 연결되지 않습니다.
글자 수 표시 기능 추가
ViewModel
public int CharacterCount
{
get => MemoText?.Length ?? 0;
}
MemoText 수정
set
{
_memoText = value;
OnPropertyChanged();
OnPropertyChanged(nameof(CharacterCount));
}
XAML
<TextBlock Text="{Binding CharacterCount}" />

MVVM 방식의 장점
이벤트 코드가 없음
기존
Click="Button_Click"
MVVM
Command="{Binding SaveCommand}"
테스트 가능
viewModel.SaveCommand.Execute(null);
버튼 없이도 테스트 가능 디버깅 창에서 위 명령어를 실행하면 Test
유지보수 쉬움
View
↕
Binding
↕
ViewModel
↕
Model
각 역할이 명확합니다.
'Programming > C#.NET' 카테고리의 다른 글
| Prism Template Pack 소개 (0) | 2026.07.22 |
|---|---|
| C#에서 Dynamic하게 DLL의 Entity 타입 찾기 (AppDomain vs BuildManager) (0) | 2026.07.20 |
| C# WPF MVVM이란? 유지보수가 쉬운 데스크톱 애플리케이션 개발 패턴 (0) | 2026.07.08 |
| [C#] WPF 기초 강좌 #3 (0) | 2026.07.07 |
| [C#] WPF 기초 강좌 #2 (0) | 2026.07.05 |