UE의 MVVM 플러그인은 UMG UI와 게임플레이 데이터를 직접 연결하지 않고, 중간에 ViewModel을 두어 UI 갱신을 관리하는 구조.
| 구분 | 역할 |
|---|---|
| Model | 실제 게임 데이터 |
| View | UMG Widget |
| ViewModel | Model 데이터를 UI용 값으로 가공하고 변경 알림을 보내는 중간 계층 |
flowchart LR
A[Model<br/>게임 데이터] --> B[ViewModel<br/>UI용 데이터]
B --> C[View<br/>UMG Widget]
C --> D[화면 표시]
기존 UMG Binding과의 차이
UMG의 Property Binding은 매 프레임 또는 자주 평가되는 방식으로 사용되기 쉬워 성능과 추적성 문제가 생길 수 있다.
MVVM View Binding은 값이 변경될 때만 바인딩된 위젯에 업데이트를 보내는 이벤트 기반 구조에 가깝다. ViewModel은 변수를 업데이트할 때 관련 위젯만 업데이트하므로
원시 Attribute Binding보다 효율적인 역할을 한다
| 구분 | 기존 UMG Binding | MVVM View Binding |
|---|---|---|
| 기준 | 위젯이 값을 가져오는 방식 | |
| MVVM 기준 | ViewModel이 변경 알림을 보냄 | |
| 갱신 방식 | 평가 기반이 되기 쉬움 | |
| MVVM 갱신 | FieldNotify 기반 | |
| 결합도 | Widget이 Model을 직접 알기 쉬움 | |
| MVVM 결합도 | Widget은 ViewModel만 참조 | |
| 적합한 용도 | 간단한 임시 UI | |
| MVVM 적합 | 인벤토리, 체력, 퀘스트, 설정, 상점 등 상태 기반 UI |
ViewModel
ViewModel은 UI에서 사용할 값을 보관하고, 값이 바뀌면 UI에 알림을 보내는 객체
C++에서 UMVVMViewModelBase를 상속함.INotifyFieldValueChanged를 구현하거나, 이를 기본 구현하는 UMVVMViewModelBase를 확장해서 ViewModel을 만들 수 있다.
UCLASS(BlueprintType)
class PROJECTFT_API UWInventoryViewModel : public UMVVMViewModelBase
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, FieldNotify, Setter, Getter, Category = "Inventory")
int32 CurrentWeight = 0;
UPROPERTY(BlueprintReadWrite, FieldNotify, Setter, Getter, Category = "Inventory")
int32 MaxWeight = 20;
void SetCurrentWeight(int32 InCurrentWeight)
{
UE_MVVM_SET_PROPERTY_VALUE(CurrentWeight, InCurrentWeight);
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetWeightText);
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetWeightPercent);
}
int32 GetCurrentWeight() const
{
return CurrentWeight;
}
void SetMaxWeight(int32 InMaxWeight)
{
UE_MVVM_SET_PROPERTY_VALUE(MaxWeight, InMaxWeight);
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetWeightText);
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetWeightPercent);
}
int32 GetMaxWeight() const
{
return MaxWeight;
}
UFUNCTION(BlueprintPure, FieldNotify, Category = "Inventory")
FText GetWeightText() const
{
return FText::FromString(
FString::Printf(TEXT("%d / %d kg"), CurrentWeight, MaxWeight)
);
}
UFUNCTION(BlueprintPure, FieldNotify, Category = "Inventory")
float GetWeightPercent() const
{
return MaxWeight > 0 ? static_cast<float>(CurrentWeight) / MaxWeight : 0.0f;
}
};
| 요소 | 설명 |
|---|---|
UMVVMViewModelBase |
UE MVVM용 ViewModel 기반 클래스 |
FieldNotify |
값 변경을 View Binding에 알릴 수 있게 하는 지정자 |
Setter / Getter |
ViewModel 외부에서 값을 읽고 쓸 수 있게 하는 접근 지정 |
UE_MVVM_SET_PROPERTY_VALUE |
값이 실제로 바뀐 경우에만 할당 및 알림 처리 |
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED |
계산형 FieldNotify 함수의 변경을 수동으로 알림 |
C++에서는 FieldNotify 브로드캐스트를 수동으로 호출해야 한다. 또한 FieldNotify가 없는 변수는 One Time 모드에서만 바인딩할 수 있다.
FieldNotify
FieldNotify가 붙은 변수나 함수는 View Binding 대상이 될 수 있고, 값이 바뀌었을 때 UI에 업데이트를 알릴 수 있다.
UPROPERTY(BlueprintReadWrite, FieldNotify, Setter, Getter)
int32 ItemCount = 0;
FieldNotify 함수
UFUNCTION(BlueprintPure, FieldNotify)
FText GetItemCountText() const
{
return FText::FromString(FString::Printf(TEXT("x%d"), ItemCount));
}
FieldNotify 함수는 Pure, Const, 단일 반환값, 입력 변수 없음 조건을 만족해야 한다.
| FieldNotify 대상 | 용도 |
|---|---|
| 변수 | 실제 UI 상태값 저장 |
| 함수 | 표시용 텍스트, 퍼센트, Visibility 등 계산값 반환 |
예를 들어 CurrentHealth가 바뀌면 HealthPercent, HealthText, HealthColor도 같이 바뀌어야 할 수 있다.
void UWHealthViewModel::SetCurrentHealth(float InHealth)
{
if (UE_MVVM_SET_PROPERTY_VALUE(CurrentHealth, InHealth))
{
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthPercent);
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHealthText);
}
}
View Binding
Widget의 속성과 ViewModel의 필드를 연결
| Widget 속성 | ViewModel Field |
|---|---|
WeightTextBlock.Text |
GetWeightText() |
WeightProgressBar.Percent |
GetWeightPercent() |
ItemNameText.Text |
ItemName |
ItemIcon.Image |
ItemIconBrush |
WarningPanel.Visibility |
GetWarningVisibility() |
UMG 에디터에서는 Widget Blueprint에 ViewModel을 추가한 뒤, View Binding 창에서 위젯 속성과 ViewModel 변수를 연결한다.
| 방향 | 의미 | 예시 |
|---|---|---|
| One Time to Widget | 처음 한 번만 ViewModel 값을 Widget에 반영 | 초기 제목, 고정 라벨 |
| One Way to Widget | ViewModel 변경 시 Widget 갱신 | 체력바, 무게, 아이템 수량 |
| One Way to ViewModel | Widget 입력값을 ViewModel로 전달 | 텍스트 입력, 설정 값 |
| Two Way | 양방향 반영 | 옵션 UI, 슬라이더 설정 |
View Binding 방향은 Widget과 ViewModel 사이에서 정보가 흐르는 방식을 결정한다.
ViewModel 생성 방식
Widget에 ViewModel을 붙일 때는 생성 방식을 선택할 수 있다.
| 생성 방식 | 설명 | 사용 예시 |
|---|---|---|
| Create Instance | 위젯이 ViewModel 인스턴스를 자동 생성 | 독립적인 팝업, 단일 위젯 |
| Manual | 외부에서 ViewModel을 생성하고 위젯에 직접 할당 | 인벤토리, 캐릭터 상태 UI |
| Global Viewmodel Collection | MVVM Game Subsystem의 전역 ViewModel 사용 | 그래픽 옵션, 계정 정보, 공통 설정 |
| Property Path | 위젯 기준 경로를 따라 ViewModel을 찾음 | PlayerController, Vehicle, Character가 가진 ViewModel 참조 |
구현 시 주의점
MVVM을 쓸 때 가장 자주 발생하는 문제는 “값은 바뀌었는데 UI가 안 바뀌는 상황”이다.
| 문제 | 원인 |
|---|---|
| UI가 갱신되지 않음 | FieldNotify 누락 |
| C++에서 값 변경 후 UI 미갱신 | Broadcast 호출 누락 |
| 바인딩 목록에 변수가 안 보임 | BlueprintReadWrite, FieldNotify, Getter/Setter 접근 문제 |
| 함수가 바인딩 목록에 안 보임 | Pure, Const, 단일 반환, 입력 없음 조건 불만족 |
| ProgressBar 값 이상 | Percent는 보통 0.0 ~ 1.0이어야 함 |
| ViewModel이 Null | 생성 방식이 Manual인데 할당하지 않음 |
| 여러 위젯 값이 따로 움직임 | Create Instance로 각각 다른 ViewModel을 생성함 |
| 배열 UI 갱신 안 됨 | 슬롯 단위 ViewModel 또는 ListView 갱신 구조 부족 |
'Unreal Engine' 카테고리의 다른 글
| UE 애니메이션 리타게팅 (0) | 2026.07.03 |
|---|---|
| 언리얼 모듈, Build.cs, 리플렉션 (0) | 2026.07.02 |
| Asset Manager + PrimaryAsset (0) | 2026.06.30 |
| 언리얼 레퍼런스 (0) | 2026.06.29 |
| DataTable, DataAsset (0) | 2026.06.26 |