PART 7 · 강의 2/3

위젯 풀링

위젯 오브젝트 풀, UMG ListView/TileView 가상화, 대량 데이터 효율적 표시를 학습합니다.

01

위젯 풀링의 필요성

동적 위젯 생성/파괴의 비용과 해결책

대량의 아이템 목록, 채팅 메시지, 리더보드 등에서 매번 위젯을 CreateWidget/RemoveFromParent하면 GC 부하와 메모리 할당 비용이 발생합니다. 위젯 풀링은 미리 생성한 위젯을 재사용하여 이 비용을 제거합니다.

풀링 없음
Create → Use → Destroy
매번 GC 부하
풀링 적용
Acquire → Use → Release
재사용, GC 없음
02

ListView / TileView

UMG의 내장 가상화 리스트 위젯

UListViewUTileView는 UMG에 내장된 가상화(Virtualized) 리스트 위젯입니다. 화면에 보이는 항목의 위젯만 생성하고, 스크롤 시 벗어난 위젯을 재활용합니다.

C++ - ListView 데이터 항목 정의
// ListView의 항목 데이터 (UObject 필수) UCLASS() class UInventoryItemData : public UObject { GENERATED_BODY() public: UPROPERTY() FName ItemID; UPROPERTY() FText DisplayName; UPROPERTY() UTexture2D* Icon; UPROPERTY() int32 Quantity; UPROPERTY() ERarity Rarity; }; // Entry Widget: IUserObjectListEntry 인터페이스 구현 UCLASS() class UInventoryListEntry : public UUserWidget, public IUserObjectListEntry { GENERATED_BODY() protected: UPROPERTY(meta = (BindWidget)) UImage* ItemIcon; UPROPERTY(meta = (BindWidget)) UTextBlock* ItemName; UPROPERTY(meta = (BindWidget)) UTextBlock* QuantityText; // IUserObjectListEntry 구현 // 위젯이 항목 데이터에 바인딩될 때 호출 virtual void NativeOnListItemObjectSet( UObject* ListItemObject) override { auto* ItemData = Cast<UInventoryItemData>(ListItemObject); if (!ItemData) return; // 위젯을 새 데이터로 갱신 ItemIcon->SetBrushFromTexture(ItemData->Icon); ItemName->SetText(ItemData->DisplayName); QuantityText->SetText( FText::AsNumber(ItemData->Quantity)); } };
C++ - ListView 사용
UPROPERTY(meta = (BindWidget)) UListView* InventoryListView; // ListView에 데이터 채우기 void UInventoryPanel::PopulateList( const TArray<FInventoryItem>& Items) { // 기존 항목 제거 InventoryListView->ClearListItems(); // 데이터 객체 생성 후 추가 for (const auto& Item : Items) { auto* Data = NewObject<UInventoryItemData>(this); Data->ItemID = Item.ItemID; Data->DisplayName = Item.DisplayName; Data->Icon = Item.Icon; Data->Quantity = Item.Quantity; // ListView에 데이터 추가 // → 화면에 보이는 항목만 Entry 위젯 생성 InventoryListView->AddItem(Data); } } // 1000개 아이템이 있어도 화면에 보이는 10개만 위젯 생성!
ListView vs TileView

ListView는 세로 방향의 1열 리스트, TileView는 가로로도 배치되는 그리드(타일) 형태입니다. 인벤토리 그리드에는 TileView가, 채팅 로그에는 ListView가 적합합니다. 두 위젯 모두 내부적으로 위젯 재활용 풀을 사용합니다.

03

커스텀 위젯 풀

ListView가 아닌 경우의 수동 풀링 구현

C++ - 커스텀 위젯 오브젝트 풀
template<typename T> class TWidgetPool { public: T* Acquire(APlayerController* PC, TSubclassOf<T> WidgetClass) { T* Widget = nullptr; if (InactivePool.Num() > 0) { // 풀에서 재사용 Widget = InactivePool.Pop(); Widget->SetVisibility(ESlateVisibility::SelfHitTestInvisible); } else { // 풀이 비었으면 새로 생성 Widget = CreateWidget<T>(PC, WidgetClass); } ActivePool.Add(Widget); return Widget; } void Release(T* Widget) { ActivePool.Remove(Widget); Widget->SetVisibility(ESlateVisibility::Collapsed); InactivePool.Add(Widget); } void ReleaseAll() { for (T* W : ActivePool) { W->SetVisibility(ESlateVisibility::Collapsed); InactivePool.Add(W); } ActivePool.Empty(); } private: TArray<T*> ActivePool; TArray<T*> InactivePool; };
04

풀링 모범 사례

효과적인 위젯 풀링을 위한 가이드라인

풀링이 필요한 경우

  • + 대량 목록 (인벤토리, 리더보드)
  • + 빈번한 생성/파괴 (채팅, 로그)
  • + 플로팅 텍스트 (데미지 넘버)
  • + 월드 마커 (NPC 체력바)

풀링이 불필요한 경우

  • - 고정 UI (HUD, 메뉴 프레임)
  • - 소수의 정적 위젯
  • - 한 번만 표시되는 UI
  • - ListView/TileView 이미 사용 시
SUMMARY

핵심 요약

  • 위젯 풀링은 CreateWidget/Destroy 비용을 제거하여 동적 UI의 성능을 대폭 개선합니다
  • UMG의 ListView/TileView는 내장 가상화로 화면에 보이는 항목만 위젯을 생성하고 재활용합니다
  • IUserObjectListEntry::NativeOnListItemObjectSet()에서 위젯을 새 데이터에 바인딩합니다
  • ListView가 적합하지 않은 경우 커스텀 위젯 오브젝트 풀을 구현하여 위젯을 재사용합니다
  • 대량 목록과 빈번한 생성/파괴 패턴에서 풀링의 효과가 가장 큽니다
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: ListView 가상화 구현

UListView를 사용하여 1000개의 아이템 데이터를 표시하는 인벤토리 리스트를 구현하세요. IUserObjectListEntry 인터페이스를 구현한 Entry Widget을 만들고, 스크롤 시 위젯이 재활용되는 것을 확인합니다.

실습 2: 커스텀 위젯 오브젝트 풀

위젯 풀 매니저 클래스를 C++로 구현하여, 데미지 숫자 팝업 같은 자주 생성/파괴되는 위젯을 풀에서 관리하세요. Acquire/Release 패턴으로 위젯을 재사용합니다.

심화 과제

UTileView를 활용하여 대량의 아이템을 그리드 형태로 가상화 표시하는 인벤토리를 구현하세요. 동적으로 열 수를 조정하고, 카테고리별 필터링과 정렬 기능을 추가합니다.