📌 오늘 배운 핵심 요약 (Summary)
언리얼 엔진 5의 UMG와 Pure MVVM 패턴을 활용해 인게임 설정 창(WBP_Settings)을 구축했다. 시야각(FOV), 마우스 감도, Y축 반전, 사운드 볼륨, VSync, 해상도 등 다양한 옵션을 실시간으로 동기화했다. 이 과정에서 발생한 엔진 기동 크래시, 데이터 바인딩 유실, 양방향 통신 시 발생하는 재귀 참조(순환 루프) 오류를 분석하고 C++로 해결했다.
🏗️ 핵심 구현 내용
1) Pure MVVM 구조 정립
- UI 위젯에 불필요한 임시 변수를 두는 안티 패턴을 철저히 배제했다.
- C++ UCh4SettingsViewModel을 단일 진실 공급원(Single Source of Truth)으로 삼아 UMG View Bindings를 통한 동기화를 달성했다.
- 슬라이더, 텍스트 상자, 체크박스 등 상호작용 요소들의 값 변경과 저장 로직을 뷰모델 내부에 캡슐화하여 설계했다.
2) 팀원 코드를 보존하는 PlayerController 입력 가로채기
- 기존에 구현된 캐릭터 C++ 코드를 직접 수정하지 않고, ACh4_multiGamePlayerController의 AddPitchInput과 AddYawInput 함수를 오버라이드(Override)했다.
- GConfig에 저장된 마우스 감도(Sensitivity)와 Y축 반전(Invert) 설정을 컨트롤러 레벨에서 곱연산 처리하여 플레이어의 시선 조작에 실시간으로 반영되도록 구현했다.
🚨 트러블슈팅 (Troubleshooting Log)
Issue 1: 에디터 재시작 시 바인딩 유실 및 기동 크래시 (EXCEPTION_ACCESS_VIOLATION)
- 증상: 에디터를 껐다 켤 때마다 WBP_Settings의 뷰모델 등록과 바인딩이 전부 삭제되거나, 기동 시 엔진이 널 포인터 크래시로 종료됨.
- 원인: 콜스택 분석 결과 ACh4_multiGamePlayerController의 C++ 생성자에서 ConstructorHelpers::FClassFinder로 위젯을 하드 로드하고 있었다. C++ 생성자는 엔진 기동 극초기(CDO 생성 시점)에 실행되는데, MVVM 에디터 모듈이 아직 준비되지 않은 상태에서 위젯 바이트코드를 조기 파싱하려다 충돌이 발생한 것이다.
- 해결: C++ 생성자에서의 FClassFinder 강제 로드 코드를 완전히 제거했다. 실제 메뉴를 여는 시점(ShowPauseMenu)에 StaticLoadClass를 활용하여 지연 로드(Lazy Load) 하도록 전환하여 기동 충돌과 바인딩 증발 문제를 해결했다.
Issue 2: 양방향 바인딩 컴파일 에러 (Property is not writable at runtime)
- 증상: UMG View Bindings 창에서 양방향(↔) 바인딩을 걸었을 때 런타임 쓰기 불가 컴파일 에러 발생.
- 원인: C++ 뷰모델의 변수들이 BlueprintReadOnly로 선언되어 있어 엔진 바인딩 시스템이 위젯에서의 역방향 쓰기를 거부함.
- 해결: 조작이 필요한 속성들을 BlueprintReadWrite 및 Setter = "Set..." 속성으로 변경하여 UMG 바인딩 시스템이 C++ Setter를 자동 호출할 수 있도록 통로를 개방했다.
Issue 3: 무한 핑퐁 렉 & 재귀 바인딩 에러 (Recursive binding detected)
- 증상: 슬라이더를 드래그하는 순간 게임 프레임이 급격히 저하되며 엔진 로그에 재귀 바인딩 경고가 다수 출력됨.
- 원인: 슬라이더 조작 ➔ 뷰모델 값 갱신 ➔ 뷰모델 브로드캐스트 ➔ 바인딩된 슬라이더가 다시 신호를 받아 뷰모델을 찌르는 순환 참조(메아리 현상)가 발생했다. 로드 시점에도 체크박스가 초기값을 받으면서 역방향 Setter를 호출하는 문제가 동반되었다.
-
- 재귀 쉴드(bIsInternalUpdating): 뷰모델의 모든 Setter 및 Load 함수 입구에 가드 플래그를 두어, 내부 연산이 진행되는 프레임 동안 재유입되는 역방향 신호를 즉시 return으로 차단했다.
- 텍스트 처리 분리: 입력 상자(Editable Text Box)는 단방향(←) 바인딩으로 설정하고, 엔터 키 입력 시(OnTextCommitted)에만 C++ Setter를 1회 호출하도록 분리하여 에코(Echo)를 차단했다.해결 (안티 에코 패턴 적용):


Issue 4: 입력창 초과값 및 비정상 문자 미교정 현상
- 증상: FOV 입력창에 한계를 초과하는 수치나 문자를 섞어 입력했을 때, 내부 데이터는 정상적으로 보정되지만 화면 텍스트 입력창에는 사용자가 입력한 오타가 그대로 남음.
- 원인: 기존 C++ 최적화 코드에서 현재 FOV 값이 보정값(Clamped)과 동일하면 화면 텍스트 갱신 함수(Broadcast)를 건너뛰도록 설계되어 있었다.
- 해결: Str.IsNumeric() 검증을 추가했다. 내부 데이터 값이 이전과 동일하더라도, 사용자의 오타를 무조건 올바른 텍스트 포맷으로 덮어쓰도록 강제 브로드캐스트(Force Overwrite)를 적용하여 텍스트 불일치를 바로잡았다.
🧠 배운 점 (Insights)
- C++ 생성자에서 UMG 에셋 강제 로드 지양: CDO 생성 시점에 블루프린트 위젯을 하드 로드하면 엔진의 플러그인 로딩 순서와 충돌하여 예측 불가능한 크래시나 직렬화 유실을 유발한다. UI 에셋은 지연 로드(Lazy Load)나 에디터 노출 프로퍼티를 활용해 런타임에 로드해야 한다.
- Two-Way 바인딩과 안티 에코(Anti-Echo) 방어: 양방향 데이터 바인딩을 설계할 때는 조작 주체에게 역방송을 하지 않도록 구조를 분리하거나, 가드 플래그(bIsUpdating)를 두어 순환 참조를 차단해야 무한 루프로 인한 프레임 저하를 막을 수 있다.
- Pure MVVM의 가치: 위젯 블루프린트에 복잡한 노드 그래프를 짜맞추는 대신 C++ ViewModel에 비즈니스 로직을 집약시켰다. 그 결과, 요소가 많은 복잡한 설정 시스템임에도 유지보수가 매우 용이한 견고한 아키텍처를 완성할 수 있었다.