1. INavigationService 개요

더보기

✔️ INavigationService 

  • INavigationService 인터페이스 기반으로 ViewModel 간 탐색이 가능한 구조로 확장
  • INavigationService를 정의하고 NavigationService에서 SubWindow 열기 기능 구현
  • MainViewModel에 INavigationService를 생성자 주입
  • App.xaml.cs에서 IoC 컨테이너에 .AddSingleton<INavigationService, NavigationService>() 등록
  • 이렇게 하면 ViewModel은 UI(Window)를 직접 알 필요 없이 Navigation 요청만 수행하므로 MVVM 원칙을 더욱 충실히 구현

 

 

 

 

📁 최종 WPF MVVM 프로젝트 구조

WPF_INavigationService01/
├── Messages/
│   └── LogUpdateMessage.cs      ← View 간 메시지 전달 클래스
├── Services/
│   ├── ILoggerService.cs        ← 로그 인터페이스
│   ├── Logger.cs                ← Singleton Logger
│   ├── INavigationService.cs    ← 탐색 인터페이스
│   └── NavigationService.cs     ← Window 열기 구현
├── ViewModels/
│   ├── MainViewModel.cs         ← Logger + Navigation 사용
│   └── SubViewModel.cs          ← Logger 메시지 수신
├── Views/
│   ├── MainWindow.xaml          ← 메인 뷰
│   ├── MainWindow.xaml.cs
│   ├── SubWindow.xaml           ← 서브 뷰 (로그 공유 확인)
│   └── SubWindow.xaml.cs
└── App.xaml
    └── App.xaml.cs              ← IoC 컨테이너 구성

구성  MVVM 패키지 사용 장점
Singleton Logger 전역 상태 관리, 로그 공유
Messenger ViewModel 간 통신 (느슨한 연결)
IoC (Ioc.Default) 테스트 가능하고 유지보수 쉬운 DI 구조
NavigationService ViewModel에서 View 직접 접근 없이 탐색

 

 

 

 

📦 NuGet 패키지 설치 필요 항목

CommunityToolkit.Mvvm
Microsoft.Extensions.DependencyInjection

 

 

 

 

🔁 동작 흐름

  1. App.xaml.cs에서 DI 컨테이너에 서비스 및 ViewModel 등록
  2. MainWindow는 MainViewModel을 IoC로 주입
  3. MainViewModel에서 INavigationService를 통해 SubWindow 실행
  4. Logger.Instance.Log(...)가 실행되면 메시지 전송
  5. MainViewModel, SubViewModel 모두 메시지를 수신하고 로그를 동기화 표시

 

 

 

 

 

2. 예제1

더보기

📁 Message 구현

WPF_INavigationService01/
  ├── Messages/
  │   └── LogUpdateMessage.cs      ← View 간 메시지 전달 클래스
//├── Services/
//│   ├── ILoggerService.cs        ← 로그 인터페이스
//│   ├── Logger.cs                ← Singleton Logger
//│   ├── INavigationService.cs    ← 탐색 인터페이스
//│   └── NavigationService.cs     ← Window 열기 구현
//├── ViewModels/
//│   ├── MainViewModel.cs         ← Logger + Navigation 사용
//│   └── SubViewModel.cs          ← Logger 메시지 수신
//├── Views/
//│   ├── MainWindow.xaml          ← 메인 뷰
//│   ├── MainWindow.xaml.cs
//│   ├── SubWindow.xaml           ← 서브 뷰 (로그 공유 확인)
//│   └── SubWindow.xaml.cs
//└── App.xaml
//    └── App.xaml.cs              ← IoC 컨테이너 구성


✔️ LogUpdateMessage 메시지 
구현

// 메시지 정의
public class LogUpdateMessage : ValueChangedMessage<string>
{
    public LogUpdateMessage(string value) : base(value) { }
}

 

 


 

📁 Services 구현

WPF_INavigationService01/
//├── Messages/
//│   └── LogUpdateMessage.cs      ← View 간 메시지 전달 클래스
  ├── Services/
  │   ├── ILoggerService.cs        ← 로그 인터페이스
  │   ├── Logger.cs                ← Singleton Logger
  │   ├── INavigationService.cs    ← 탐색 인터페이스
  │   └── NavigationService.cs     ← Window 열기 구현
//├── ViewModels/
//│   ├── MainViewModel.cs         ← Logger + Navigation 사용
//│   └── SubViewModel.cs          ← Logger 메시지 수신
//├── Views/
//│   ├── MainWindow.xaml          ← 메인 뷰
//│   ├── MainWindow.xaml.cs
//│   ├── SubWindow.xaml           ← 서브 뷰 (로그 공유 확인)
//│   └── SubWindow.xaml.cs
//└── App.xaml
//    └── App.xaml.cs              ← IoC 컨테이너 구성

 

✔️ ILoggerService 인터페이스 구현

public interface ILoggerService
{
    void Log(string message);
    string GetLogs();
}

 

✔️ Logger 클래스 구현

public sealed class Logger : ILoggerService
{
    private static Logger? _instance = null;
    private static readonly object _lock = new();
    private readonly StringBuilder _logs = new();

    private Logger()
    {
        Console.WriteLine("Logger 인스턴스가 생성되었습니다.");
    }

    public static Logger Instance
    {
        get
        {
            lock (_lock)
            {
                return _instance ??= new Logger();
            }
        }
    }

    public void Log(string message)
    {
        string log = $"{DateTime.Now:HH:mm:ss} - {message}";
        _logs.AppendLine(log);
        // 로그가 업데이트되면 메시지를 발송하여 ViewModel에 알림
        WeakReferenceMessenger.Default.Send(new LogUpdateMessage(_logs.ToString()));
    }

    public string GetLogs()
    {
        return _logs.ToString();
    }
}

 

✔️ INavigationService 인터페이스 구현

public interface INavigationService
{
    void OpenSubWindow();
}

 

✔️ NavigationService 클래스 구현

 

 


 

📁 ViewModel 구현

WPF_INavigationService01/
//├── Messages/
//│   └── LogUpdateMessage.cs      ← View 간 메시지 전달 클래스
//├── Services/
//│   ├── ILoggerService.cs        ← 로그 인터페이스
//│   ├── Logger.cs                ← Singleton Logger
//│   ├── INavigationService.cs    ← 탐색 인터페이스
//│   └── NavigationService.cs     ← Window 열기 구현
  ├── ViewModels/
  │   ├── MainViewModel.cs         ← Logger + Navigation 사용
  │   └── SubViewModel.cs          ← Logger 메시지 수신
//├── Views/
//│   ├── MainWindow.xaml          ← 메인 뷰
//│   ├── MainWindow.xaml.cs
//│   ├── SubWindow.xaml           ← 서브 뷰 (로그 공유 확인)
//│   └── SubWindow.xaml.cs
//└── App.xaml
//    └── App.xaml.cs              ← IoC 컨테이너 구성

 

✔️ MainViewModel 구현

 

public partial class MainViewModel : ObservableObject
{
    private readonly INavigationService _navigation;
    private readonly ILoggerService _logger;

    [ObservableProperty]
    private string logText;

    public MainViewModel(ILoggerService logger, INavigationService navigation)
    {
        _navigation = navigation;
        _logger = logger;
        _logger.Log("MainWindow 열림");
        LogText = _logger.GetLogs();

        // 메시지 수신 등록
        WeakReferenceMessenger.Default.Register<LogUpdateMessage>(this, (r, m) =>
        {
            LogText = m.Value;
        });
    }

    [RelayCommand]
    private void WriteLog()
    {
        _logger.Log("MainWindow에서 로그 기록");
        var logs = _logger.GetLogs();
        LogText = logs;
        WeakReferenceMessenger.Default.Send(new LogUpdateMessage(logs));
    }

    [RelayCommand]
    private void OpenSubWindow()
    {
        //var sub = new SubWindow();
        //sub.Show();

        _navigation.OpenSubWindow();
    }
}

 

✔️ SubViewModel 구현

public partial class SubViewModel : ObservableObject
{
    private readonly ILoggerService _logger;

    [ObservableProperty]
    private string logText;

    public SubViewModel(ILoggerService logger)
    {
        _logger = logger;
        _logger.Log("SubWindow 열림");
        LogText = _logger.GetLogs();

        WeakReferenceMessenger.Default.Register<LogUpdateMessage>(this, (r, m) =>
        {
            LogText = m.Value;
        });
    }

    [RelayCommand]
    private void WriteLog()
    {
        _logger.Log("SubWindow에서 로그 기록");
        var logs = _logger.GetLogs();
        LogText = logs;
        WeakReferenceMessenger.Default.Send(new LogUpdateMessage(logs));
    }
}

 

 

 

 

📁 View 구현

WPF_INavigationService01 /
//├── Messages/
//│   └── LogUpdateMessage.cs      ← View 간 메시지 전달 클래스
//├── Services/
//│   ├── ILoggerService.cs        ← 로그 인터페이스
//│   ├── Logger.cs                ← Singleton Logger
//│   ├── INavigationService.cs    ← 탐색 인터페이스
//│   └── NavigationService.cs     ← Window 열기 구현
//├── ViewModels /
//│   ├── MainViewModel.cs         ← Logger + Navigation 사용
//│   └── SubViewModel.cs          ← Logger 메시지 수신
  ├── Views /
  │   ├── MainWindow.xaml          ← 메인 뷰
  │   ├── MainWindow.xaml.cs
  │   ├── SubWindow.xaml           ← 서브 뷰 (로그 공유 확인)
  │   └── SubWindow.xaml.cs
//└── App.xaml
//    └── App.xaml.cs              ← IoC 컨테이너 구성

 

✔️ MainWindow.xaml 구현

    Title="MainWindow" Height="300" Width="400">
<StackPanel Margin="10">
    <Button Content="로그 남기기" Command="{Binding WriteLogCommand}" Margin="0 0 0 10"/>
    <Button Content="SubWindow 열기" Command="{Binding OpenSubWindowCommand}" Margin="0 0 0 10"/>
    <TextBox Text="{Binding LogText}" AcceptsReturn="True" Height="200" VerticalScrollBarVisibility="Auto"/>
</StackPanel>

 

✔️ MainWindow.xaml.cs 구현

public MainWindow()
{
    InitializeComponent();

    // DataContext = new MainViewModel(Logger.Instance);
    // DataContext = Ioc.Default.GetService<MainViewModel>();
    DataContext = Ioc.Default.GetRequiredService<MainViewModel>();
}

 

 

✔️ SubWindow 구현

    Title="SubWindow" Height="250" Width="350">
<StackPanel Margin="10">
    <Button Content="로그 남기기" Command="{Binding WriteLogCommand}" Margin="0 0 0 10"/>
    <TextBox Text="{Binding LogText}" AcceptsReturn="True" Height="150" VerticalScrollBarVisibility="Auto"/>
</StackPanel>

 

✔️ SubWindow.xaml.cs 구현

public SubWindow()
{
    InitializeComponent();

    // DataContext = new SubViewModel(Logger.Instance);
    // DataContext = Ioc.Default.GetService<SubViewModel>();
    DataContext = Ioc.Default.GetRequiredService<SubViewModel>();
}

 

 

 


📁
 Services
 구현

WPF_INavigationService01 /
//├── Messages/
//│   └── LogUpdateMessage.cs      ← View 간 메시지 전달 클래스
//├── Services/
//│   ├── ILoggerService.cs        ← 로그 인터페이스
//│   ├── Logger.cs                ← Singleton Logger
//│   ├── INavigationService.cs    ← 탐색 인터페이스
//│   └── NavigationService.cs     ← Window 열기 구현
//├── ViewModels /
//│   ├── MainViewModel.cs         ← Logger + Navigation 사용
//│   └── SubViewModel.cs          ← Logger 메시지 수신
//├── Views /
//│   ├── MainWindow.xaml          ← 메인 뷰
//│   ├── MainWindow.xaml.cs
//│   ├── SubWindow.xaml           ← 서브 뷰 (로그 공유 확인)
//│   └── SubWindow.xaml.cs
  └── App.xaml
      └── App.xaml.cs              ← IoC 컨테이너 구성

 

✔️ App.xaml 구현

 

✔️ App.xaml.cs 구현

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    // IoC 컨테이너에 싱글톤 Logger 등록
    Ioc.Default.ConfigureServices(
        new ServiceCollection()
            .AddSingleton<ILoggerService>(Logger.Instance)
            .AddTransient<MainViewModel>()
            .AddTransient<SubViewModel>()
            .BuildServiceProvider()
    );

    var mainWindow = new MainWindow();
    mainWindow.Show();
}

 

 

 

✔️ 소스코드

 

WPF_INavigationService.zip
0.09MB