
Viemodel <->interface <- service (Iservice상속)
물론 그림은 어떻게 하냐에 따라 화살표가 다르나, TestVPro 기준.
(인터페이스에는 Service에서 구현해야할 메서드 또는 이벤트, 즉 ViewModel에서 interface로부터 간접적으로 불러올 Service의 메서드 또는 이벤트 정의)
이벤트를 사용하는 목적은 viewmodel이 service의 인터페이스로부터 데이터를 받을 목적으로 이벤트 정의
이벤트를 꼭 사용할 필요없다. 그저 di에서 해당 service의 메서드를 반환타입을 지정해서 반환값을 받으면 되기 때문이다(pull, 정방향)
이벤트는 push(역방향)이다. 이벤트를 사용하는 이유는 서비스에서 값을 제공해서 그 값을 여러 클래스 쪽에 보낼 때 사용하면 된다. (구독기반, 각 클래스마다 해당 이벤트를 등록해야하며, 사전조건으로 그 클래스들은 동일 인터페이스 상속 받아야함. )
그림을 보다시피 DI 기반 IOC 가 정립되며, IOC를 이루기 위한 하기 설명 참고.
App.xaml.cs에서 Di등록 후(인터페이스, 해당인터페이스상속받는 서비스 && 뷰모델)
Viewmodel에서 Iservice타입을 주입
해당 Iservice메서드호출시 (service가 정의한메서드진입)
Service가 비즈니스로직처리후 데이터를 모델에 저장 및 IService에 정의한 해당 모델타입 이벤트핸들러 정의된걸 사용해 이벤트를 invoke진행. 물론 데이터들은 해당 모델타입의 이벤트핸들러 인자로 넣음.
(해당 모델은 프로젝트별 공용사용 시 Core에 넣기.
뷰모델도 이벤트함수에서 매개변수가 해당 모델타입으로 데이터를 받는 용도)
사전준비로서 뷰모델에선 생성자에 di로 iservice의 이벤트핸들러에 메서드를 생성 및 로직처리 가정하에
Service에서 발생한 invoke로인해 뷰모델의 이벤트 메서드가 호출되어짐
결과적으로 인터페이스를 기반으로 ioc가 일어나 mvvm위배가아님.
서비스와 뷰모델이 직접 서로 데이터주고받는게아닌 인터페이스가 중재자로 간접적으로 데이터주고받기때문에 ioc임(DI기반)
전체 구조 요약
ViewModel <--> IService (Interface) <-- Service : IService
- ViewModel은 직접 Service를 알지 못하고, 오직 IService만 참조
- Service는 IService를 구현하고, 내부에서 비즈니스 로직 수행 및 이벤트 발생
- ViewModel은 IService의 이벤트를 구독해 데이터를 간접 수신
이벤트 흐름 설명
1. App.xaml.cs에서 DI 등록
services.AddSingleton<IService, Service>();
services.AddSingleton<ViewModel>();
2. ViewModel 생성자에서 IService 주입 및 이벤트 구독
public ViewModel(IService service)
{
_service = service;
_service.DataReceived += OnDataReceived;
}
private void OnDataReceived(MeasurementModel model)
{
// ViewModel에서 데이터 처리
}
3. Service 클래스 내부에서 데이터 처리 후 이벤트 발생
class Service : IService
{
public event Action<MeasurementModel> DataReceived;
...
private void ReceiveData()
{
var data = new MeasurementModel(...);
DataReceived?.Invoke(data);
}
...
}
IOC 구조로서 타당한 이유
- ViewModel은 Service에 직접 의존하지 않고, 인터페이스에 의존
- Service가 이벤트로 데이터를 ViewModel에 "푸시(Push)"
- ViewModel은 해당 이벤트를 구독만 함으로써, 관심사 분리(Separation of Concerns) 유지
이 방식의 장점
| 장점 | 설명 |
| 테스트 용이 | Mock<IService>를 주입하면 ViewModel 단위 테스트 가능 |
| 느슨한 결합 | ViewModel ↔ Service 간 직접 참조 없음 (IoC 실현) |
| 재사용성 | IService 구현체만 바꿔 끼워도 ViewModel은 변경 불필요 |
| MVVM 원칙 준수 | ViewModel은 View의 논리만 다루고, 데이터 전달은 외부에 위임 |
그림 보충 설명
- 그림에서 양방향 화살표는 ViewModel이 IService를 호출하고, IService는 이벤트로 데이터를 다시 돌려주기 때문
- Service → Interface는 구현(구현체), ViewModel → Interface는 사용(소비자)
결론
“ViewModel은 인터페이스를 통해 서비스 로직에 접근하고, 서비스는 인터페이스에 정의된 이벤트를 통해 데이터를 푸시하여 MVVM 구조와 IoC 원칙을 모두 만족시킨다.”
'C#(.Net) > WPF' 카테고리의 다른 글
| 다양한 뷰전환 방식 (0) | 2025.08.20 |
|---|---|
| CustomControl에서의 PART_ 네이밍 규칙 (1) | 2025.08.04 |