
오늘은 프로젝트에서 Shop UI를 구현하고, 특히 상점 리스트에서 아이템을 선택했을 때 상세 패널에 정보가 반영되는 흐름과 아이템 수량(Quantity) 증감 UI를 중심으로 작업을 진행하였습니다. 또한 상점 UI를 여는 과정에서 PlayerController–NPC–ShopComponent 사이의 통신 구조를 다시 정리하고, 이를 코드 레벨에서 점검하는 시간을 가졌습니다.
이번 작업의 핵심은 다음과 같습니다. CMShopContentElementWidget을 클릭 → CMShopWidget에서 선택 상태 갱신 → 상세 UI에 이름/설명/수량 표시 → 수량 버튼으로 Quantity 증감까지의 흐름을 자연스럽게 만드는 것이었습니다.

CreateShopWidget에서 이미 상점 위젯 인스턴스가 있을 경우에는 SetShopItems + BuildShopList만 다시 호출하여 재사용하도록 되어 있음을 확인했습니다.FCMShopItemContent CurrentSelectedItem;int32 SeletedItemQuantity = 0;UTextBlock* SelectedItemNameText;UTextBlock* SelectedItemQuantityText;UTextBlock* ItemDescriptionText;UButton* AddItemQuantityButton;UButton* SubtractItemQuantityButton;NativeOnInitialized()에서 다음과 같이 버튼과 핸들러를 바인딩AddItemQuantityButton -> OnClickedAddItemQuantityButtonSubtractItemQuantityButton -> OnClickedSubtractItemQuantityButtonUFUNCTION()으로 선언해 델리게이트와 호환되도록 처리UCMShopContentElementWidget(리스트 요소)을 클릭했을 때 호출되는 콜백입니다.
FCMShopItemContent를 받아와 현재 선택 아이템으로 저장개념적으로는 다음과 같습니다.
CurrentSelectedItem = ElementWidget->GetItemContent();SeletedItemQuantity = 1;UpdateSelectedItemDisplay();선택된 아이템 정보를 실제 UI 텍스트에 반영하는 책임을 가집니다.
SelectedItemNameText->SetText(...) 호출FCMShopItemContent 내부의 이름 필드 타입을 고려하여 FName → FText::FromName, 또는 이미 FText라면 그대로 세팅SelectedItemQuantityText->SetText(FText::AsNumber(SeletedItemQuantity));ItemDescriptionText->SetText(CurrentSelectedItem.ItemDescription);에디터에서 바인딩한 텍스트 위젯들이 nullptr일 수 있으므로, 각 항목마다 if (SelectedItemNameText) 같은 널 체크 후 세팅하는 패턴으로 작성했습니다.
수량 버튼 양쪽에서 공통으로 사용하는 내부 함수입니다.
int32 Delta : +1, -1 등의 증감 값SeletedItemQuantity = FMath::Clamp(SeletedItemQuantity + Delta, 1, 99);UpdateSelectedItemDisplay(); 호출하여 UI를 동기화이렇게 함으로써, 수량이 UI와 항상 동일한 상태를 유지하게 했습니다.
OnClickedAddItemQuantityButton()UpdateSelectedItemQuantityDelta(1);OnClickedSubtractItemQuantityButton()UpdateSelectedItemQuantityDelta(-1);버튼은 단순히 증감 방향만 결정하고, 실제 로직은 UpdateSelectedItemQuantityDelta에 몰아 넣어 중복을 줄였습니다.

UCMShopContentElementWidget은 상점 리스트의 개별 아이템 슬롯입니다. 오늘은 이 위젯이 어떻게 상위 ShopWidget에 "선택됨"을 알리는지 흐름을 명확히 이해하는 데 집중했습니다.
CMNpcShopComponent에서 TArray<FCMShopItemContent>를 생성 및 관리ACMPlayerController::Server_RequestShopData_ImplementationUCMNpcShopComponent에서 GetShopItemContents로 아이템 배열 획득CreateShopWidget(ShopItems) 호출UCMShopWidget::BuildShopList()ShopItems를 순회하면서 UCMShopContentElementWidget 인스턴스들을 생성SetItemDisplayData(Item, this)와 같은 방식으로UCMShopContentElementWidget 내부에서 OnClicked 등 이벤트 발생 시UCMShopWidget::HandleElementSelected(this) 호출이 과정을 통해 슬롯 → 상점 메인 위젯으로 자연스럽게 선택 이벤트가 올라오도록 설계되어 있습니다.

오늘 도중에 발견한 네이밍 혼선을 정리했습니다. 기존에는 일부 버튼/텍스트가 Count라는 이름을 사용하고 있었고, 다른 부분에서는 Quantity를 사용하고 있었습니다. 이를 전부 Quantity로 통일했습니다.
AddItemCountButton → AddItemQuantityButtonSubtractItemCountButton → SubtractItemQuantityButtonSelectedItemCountText → SelectedItemQuantityTextOnClickedAddItemCountButton → OnClickedAddItemQuantityButtonOnClickedSubtractItemCountButton → OnClickedSubtractItemQuantityButton이 정리 덕분에 코드 가독성과 의도가 보다 명확해졌습니다.
오늘은 Shop UI에서 아이템 선택 → 상세 정보 반영 → 수량 증감이라는 한 흐름에 집중해서 구현과 리팩터링을 진행하였습니다. 특히 CMShopContentElementWidget과 CMShopWidget 사이의 이벤트 전달 구조를 확실히 이해하고 정리하면서, 이후 구매/판매 요청(RequestBuyItem, RequestSellItem)을 구현할 때도 같은 패턴을 확장해서 사용할 수 있겠다는 생각이 들었습니다.
또한, 이름을 Count에서 Quantity로 통일하고 버튼/텍스트/함수를 일관되게 정리한 덕분에, 나중에 상점 관련 기능을 확장할 때 혼동이 줄어들 것으로 기대합니다. 다음 단계에서는 오늘 만든 선택/수량 정보를 바탕으로 실제 서버 RPC(Server_RequestBuyItem, Server_RequestSellItem)와 연동하여 아이템 구매/판매 로직을 완성해 볼 계획입니다.