상세 컨텐츠

본문 제목

팀프로젝트 05. 목표 HUD를 MVVM으로 연결

Unreal C++

by hyunjunstar 2026. 8. 7. 19:07

본문

오늘 한 것은 작업 목표 체크리스트 HUD를 서버 데이터에 연결한 것이다. 위젯이 ACWGameState를 직접 구독하지 않고 ViewModel을 한 겹 두는 구조로 갔다.

GameState ─델리게이트→ ObjectiveViewModel ─FieldNotify→ ObjectivePanel

위젯이 GameState를 직접 알게 하면 화면이 늘어날 때마다 같은 구독·해제 코드가 복사된다. 한 겹을 두면 그 책임이 ViewModel 한 곳으로 모인다. 대신 ViewModel이 떠안아야 할 것이 생긴다. 정리한 것은 아래 네 가지다.

1. 무엇을 켜야 하나

Edit → Plugins에서 UMG Viewmodel을 체크했다. 그런데 나중에 .uproject를 열어보니 항목이 없었다. Saved/, Intermediate/, Binaries/ 어디에도 흔적이 없었다. 체크만 하고 재시작을 하지 않아 프로젝트 파일에 저장되지 않은 상태였다.

{
    "Name": "ModelViewViewModel",
    "Enabled": true
}

에디터 UI의 체크 상태와 파일 내용은 별개다. 직접 열어서 봐야 한다.

그리고 플러그인만 켜면 블루프린트 MVVM까지만 된다. UMVVMViewModelBase를 상속한 C++ 클래스를 만들려면 모듈이 따로 필요하다.

"ModelViewViewModel",
"FieldNotification",   // 세트다. 하나만 넣으면 링크 에러

 

2. View를 데이터 모양에 맞췄다

ViewModel을 쓰기로 했으면 위젯이 받을 수 있는 모양이 뭔지부터 봐야 한다. 서버 구조체를 열었다.

USTRUCT(BlueprintType)
struct FCWStageObjective
{
    UPROPERTY(BlueprintReadOnly) FText DisplayName;
    UPROPERTY(BlueprintReadOnly) int32 CompletedCount = 0;
    UPROPERTY(BlueprintReadOnly) int32 TotalCount = 0;
    UPROPERTY(BlueprintReadOnly) int32 WorkOrder = 0;
};

TArray로 온다. 즉 줄 수가 가변이다. 기획서에 목표가 4줄이라 Entry를 4개 고정 배치해 뒀는데, 실제 로그는 3줄이었다.

[Stage] 작업 존 11개를 찾았습니다.
[Stage] 목표 3줄 (존 1/11)

존 11개를 이름으로 묶어 3줄이 됐고, 맵마다 배치가 다르니 줄 수도 달라진다. 배열을 그대로 받으려면 위젯이 먼저 바뀌어야 했다.

전 후
Entry 4개 고정 배치 빈 VerticalBox
목표별 setter 4개 RefreshObjectiveEntries(배열)
디자이너에 목표 이름 입력 서버 DisplayName 그대로 표시

지우는 데 시간이 걸린 건 Entry가 아니라 그래프에 박혀 있던 SetGuidelineProgress() 같은 목표별 setter 4개였다. "목표 = 가이드라인"이 컴파일 타임에 박혀 있는 셈이라 가변 배열과 공존할 수 없다.

완료 여부도 변수로 저장하지 않고 TotalCount > 0 AND CompletedCount >= TotalCount로 매번 계산한다. 이 게임은 차량이 젖은 페인트를 밟으면 끝났던 존이 다시 열린다. 완료가 되돌아가는 데이터는 플래그로 들고 있으면 안 된다.

ViewModel을 만들기 전에 위젯만 더미 배열로 먼저 검증했다. 3개·4개·5개를 넣어 줄 수와 중복 생성만 확인하는 단계인데, 이걸 먼저 끊어두니 이후에 목록이 안 뜰 때 "위젯 문제인가 ViewModel 문제인가"를 고민할 필요가 없었다.

3. 알림은 매크로를 거쳐야 간다

ViewModel 본체다.

UCLASS(BlueprintType)
class UCWObjectiveViewModel : public UMVVMViewModelBase
{
private:
    UPROPERTY(BlueprintReadOnly, FieldNotify, meta = (AllowPrivateAccess = "true"))
    TArray<FCWStageObjective> Objectives;
};

FieldNotify를 붙였으니 값만 바꾸면 되는 줄 알았는데 아무 일도 일어나지 않았다.

Objectives = InGameState->GetObjectives();   // 알림 안 감

FieldNotify는 필드 ID를 만들어줄 뿐이고, 브로드캐스트는 전용 매크로가 한다.

```cpp
void UCWObjectiveViewModel::SetObjectives(const TArray& InObjectives)
{
// 값이 실제로 변경된 경우에만 MVVM 알림 발생
UE_MVVM_SET_PROPERTY_VALUE(Objectives, InObjectives);
}

관련글 더보기