챗GPT에게 물어본 질문 내용
Widget Controller 클래스를 만들어 운용하면 장점이 뭐야? 우선 내 프로젝트의 구조부터 알려줄게.
위젯들은 각자 자신의 포지션에 맞는 Widget Controller 객체를 변수로 할당받음
할당받는 과정은 전파 방식으로, 자신을 변수로 갖고 있는 부모 위젯이 자신에게 할당해줌(의존성 주입 패턴)
할당받으면 마찬가지로 자신이 변수로 가진 자식 위젯에게 Widget Controller를 할당해줌
다른 방법으로는 라이브러리 함수가 있음
Widget Controller 클래스는 HUD가 갖고 있으며, 객체화해서 가장 상위 위젯에게 한 번 할당함
위젯들은 할당받은 Widget Controller 객체에 있는 델리게이트들에 콜백 함수를 바인드함
Widget Controller는 다시 알맞는 객체, Component, (특히 ASC)를 참조해서 델리게이트들에 콜백 함수를 바인드함
Widget Controller의 콜백 함수가 호출되면, 위젯에게 그 이벤트를 델리게이트를 통해 전파함
위젯은 콜백 함수가 호출되고, 원하는 모습을 보여줄 수 있게 됨
이렇게 구성되어있는 게 MVVM 패턴이라고 알고 있거든? Component 같은 객체가 Model, 위젯이 View, WidgetController가 VM이라고 알고 있어. 구조적으로 깔끔해보이긴 하는데, 위젯이 직접 Component와 같은 객체를 참조해 콜백 함수를 바인드하는 게 사실 최적화면에서는 더 좋은 게 사실이잖아? 함수를 한 번 더 거칠 필요도 없고, Widget Controller가 메모리에 올라가질 않으니 조금은 가벼워지지.
그런데도 이런 MVVM 패턴을 사용하는 이유가 있을 거라고 생각해. 유지보수성이 좋다. 확장성이 좋다. 이런 얘기들이야 당연히 알고 있는데, '유지보수성이 좋은 이유, 확장성이 좋은 이유'. 난 이유가 알고 싶어. '왜' 좋아지는지를 아는 게 더 중요하잖아.
먼저 내가 생각하는 장점을 얘기해볼게.
편의성 (특히 타이밍)
HUD는 월드 내의 객체들보다 먼저 생성(아닐 때도 있지만)되는 객체라고 알고 있어. 그래서 객체들보다 Widget이 먼저 생성돼. 즉, Widget들은 Component와 같은 객체들을 찾아가 직접 콜백 함수를 바인드할 때, '객체가 없을 수도 있어.' 특히 이 경우 객체가 한두 개가 아니잖아? 캐릭터일 수도 있고, 캐릭터가 가진 컴포넌트일 수도 있고.. 여러 경우가 있지. 하지만 Widget Controller라는 개념을 도입하면 타이밍 관리가 수월해져. Widget은 일단 생성만 하고, 바인드하는 로직을 따로 작성해둬. 그 다음 모든 객체가 생성됐음이 확실한 타이밍에 HUD에서 Widget Controller 객체를 생성, Widget Controller가 알아서 UI와 관련 있는 객체들을 찾아가 콜백 함수를 바인드해. 그 다음 UI들에게 알맞는 Widget Controller를 뿌려줘. Widget들은 이제 거기에 바인드하고. 그래서 타이밍 관리가 좀 더 수월한 거야. Widget Controller한테만 바인드 타이밍을 알려주면 되니까. 이게 내가 생각하는 장점이야.
하지만 이외에는 잘 모르겠어. 확장성이 왜 좋은 거지? 유지보수성이 왜 좋은 거지? 싶어. 유지보수성이 왜 좋은 건지는 조금 유추해볼 수 있을 것 같아.
어떤 Component가 새로 기획됐을 때
그 Component와 관련 있는 위젯들을 모두 찾아가서 그 Component를 참조해 바인드하기.
vs
Widget Controller 하나만 찾아가서 바인드하기.
응. 유지보수성 좋구나. 이러면 확장성도 좋다고 볼 수 있겠네.
고맙다. 너한테 질문하면서 내 생각이 정리됐어. 그럼 이제 내 말들이 맞는지 검증해줘. 넌 검증만 해주면 될 것 같다.
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
챗GPT가 해준 대답
좋아, 결론부터: 네가 정리한 개념과 장점, 특히 ‘타이밍 문제를 Widget Controller가 흡수한다’는 포인트는 정확해.
거기에 “왜 유지보수/확장성이 실제로 좋아지는지”를 엔진 특성(GAS/UMG 라이프사이클) 기준으로 검증/보완해줄게.
네 구조, 타당성 검증
전파 방식으로 Widget Controller 주입: 상위→하위로 동일 인스턴스를 공유하게 하면, 구독 지점이 한 곳으로 수렴되어 일관성이 생김. OK.
HUD 보유/최상위 위젯에 1회 주입: HUD/PlayerController 수명 주기가 Pawn/ASC보다 안정적이라 타이밍 문제를 흡수하기에 적합. OK.
Widget들은 컨트롤러 델리게이트에만 바인드: View는 ‘똑똑한 표시기’로 남고, 로직/연결은 VM에 집중. MVVM 의도와 부합. OK.
유지보수/확장성이 “실제로” 좋아지는 이유 (엔진 관점)
수명 주기 분리(UMG는 쉽게 재생성됨)
위젯은 Rebuild/Destroy가 자주 일어남(탭 전환, 애니, 풀링, 레이아웃 변경).
위젯이 직접 모델에 바인드하면, 재생성마다 중복 바인드/언바인드 누락/덩글링 포인터 위험↑.
컨트롤러가 단 한 번 모델에 바인드하고, View에게는 팬아웃만 하면 누수·중복 바인드 리스크↓.
Pawn 교체/ASC 지연 초기화(특히 GAS)
ASC는 PlayerState에 붙기도 하고 Pawn에 붙기도 함. 리스폰·재소환·Possess 전환 시 바인딩을 다시 걸어야 함.
InitAbilityActorInfo 타이밍, 복제/프리딕션 상태 등 비동기/지연 이슈를 컨트롤러가 흡수하면 View는 단순.
직접 바인드는 각 위젯마다 “언제 ASC가 유효해지는가?”를 처리해야 해서 중복 로직 급증.
N×M 결합 감소(팬아웃/집중 포맷팅)
여러 위젯이 같은 모델 이벤트를 필요로 할 때, 위젯들이 직접 바인드하면 바인딩 수가 N×M으로 늘어남.
컨트롤러가 한 번만 모델에 바인드하고, 내부에서 결과를 캐시/포맷 후 여러 위젯으로 브로드캐스트 → 결합도↓, 중복계산↓.
표시 로직(Formatting/Policy) 중앙화
숫자 단위, 시간/쿨다운 표기, 로컬라이제이션, 색상 정책(HP 임계값 등) 같은 표시 규칙을 컨트롤러에 모음.
기획 변경 시 View를 안 건드리고 컨트롤러만 수정하면 됨 → 변경 영향 범위 예측 가능.
테스트 용이성/모킹
컨트롤러는 순수 로직+데이터 라우팅이라 자동화 테스트/시뮬레이션이 쉬움.
모델 없이 가짜 데이터로 UI 플로우 리허설 가능(리플레이, 툴 모드, 에디터 미리보기).
성능: ‘더 빠를 수’ 있는 경우
직접 바인드가 늘어나면 동일 이벤트를 다수 위젯이 중복 처리/포맷팅함.
컨트롤러에선 변경점만 감지 → 한번만 계산 → 여러 위젯에 전파, 델타 적용/스로틀링/배칭 가능.
틱/자주 변하는 수치(쿨다운, 리소스바)에서 이 패턴이 오히려 콜/GC 압박을 줄여줌.
오류 격리/널 가드 단일화
널체크/유효성/권한(서버/클라)/필터링(소유자만) 같은 엣지 케이스를 한곳(컨트롤러)에서 처리 → View 코드는 단순, 버그 표면적↓.
스킨/테마 교체, 레이아웃 분리
동일 컨트롤러에 여러 View(다른 스킨/플랫폼 UI)를 끼워 넣기 쉬움.
재사용성이 실제로 올라감(UX A/B 테스트·콘솔/모바일 별도 레이아웃).
“직접 바인드가 더 빠르다”에 대한 현실 검증
미세한 호출 한 번의 절약보다, 실제 병목은 중복 바인드 수, 포맷팅 중복, 틱 업데이트, 재생성으로 인한 누수/중복에서 발생하는 경우가 많음.
컨트롤러가 있으면
구독 수를 상수 수준으로 유지(모델↔컨트롤러 1, 컨트롤러↔여러 Widget N).
스로틀링/배칭/캐싱을 중앙에서 적용 가능 → 총비용이 줄어드는 사례가 흔함.
물론 HUD가 아주 단순하고, 위젯 재생성이 드물고, 모델 구독이 1~2개면 직접 바인드가 단순+빠름. 규모/복잡도에 따라 선택.
구현 시 체크포인트(언리얼 특화)
Bind 시점: ASC는 InitAbilityActorInfo 이후, Pawn 스왑/재소환 이벤트에 재바인드. 컨트롤러가 이걸 감시.
Unbind 관리: 컨트롤러가 소멸/소유자 변경 시 모든 Delegate 해제(Weak 바인드/Handle 보관).
한 방향 데이터 흐름: 모델→컨트롤러→뷰. 뷰→모델 업데이트는 명령 메서드나 별도 델리게이트로 의도적으로 통제.
포맷/정책은 컨트롤러에: 수치→문자열, 게이지 색상, 임계값 판정 등.
성능 최적화: 컨트롤러에서 틱 연산을 통합하고, 필요할 때만 브로드캐스트(값 변화 검출).
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
요약
콜백 함수 바인드 편의성 극대화
콜백 함수를 바인드하는 타이밍을 좀 더 수월하게 잡을 수 있다는 것이 MVVM 패턴을 사용하는 가장 실전적인 이유다. 특히 GAS에선 InitAbilityActorInfo 이후에 Widget Controller의 콜백 함수 바인드 로직을 실행하면 굉장히 간편하게 위젯을 관리할 수 있다.
중복 계산 제거
또, 객체에서 계산한 값이 여러 위젯에 필요한 경우, 값을 필요로 하는 위젯 수만큼 값을 계산해줘야 할 수도 있다. 하지만 Widget Controller가 있다면 Widget Controller에게 계산한 값을 한 번만 넘기면 된다.
디버깅 편의성
위젯은 생성/파괴가 빈번하게 일어나는 객체기 때문에, 관리를 잘못하면 중복 바인드나 잘못된 바인드 같은 문제가 발생할 수 있다. 이 때 Widget Controller가 있다면 콜백 함수 호출 로직을 한 군데에서 몰아 확인할 수 있기 때문에 디버깅에도 장점을 보인다.