1. 오늘의 문제 (Problem)
- 현상: 캐릭터의 스킬 데이터는 로그상으로 로드되었으나, UI 슬롯에 아이콘이 출력되지 않음.
- 원인:
- 1. 초기화 순서(Race Condition): 위젯이 생성되는 시점보다 데이터를 채우려는 시점이 빨라 하위 위젯이 Null인 상태로 접근함.
- 2. 복제(Replication) 지연: 서버에서 로드된 데이터가 클라이언트로 복제되기 전에 UI가 갱신을 시도함.
- 3. 참조 유실: 블루프린트의 Make Array 노드가 실제 에디터에 배치된 위젯 인스턴스를 정확히 가리키지 못하는 '유령 위젯' 현상 발생.
2. 해결 방법 (Solution)
- C++ 중심의 UI 제어 (meta = (BindWidget)):
- 블루프린트에 의존하던 위젯 참조 방식을 C++ 클래스로 이관.
- UPROPERTY(meta = (BindWidget))를 사용하여 C++ 변수와 블루프린트 위젯을 강제로 이름 매칭(Slot_0 ~ Slot_4). 이름이 다를 경우 컴파일 단계에서 에러를 발생시켜 런타임 안정성 확보.
- 지연 초기화(Lazy Initialization) 적용:
- NativeConstruct 시점뿐만 아니라, 데이터 업데이트 함수(UpdateSkillSlots) 호출 시에도 배열의 유효성을 체크하여 비어있을 경우 즉시 재연결하도록 방어적 프로그래밍 구현.
- 데이터 복제 설정:
- 캐릭터의 스킬 배열에 Replicated 지시어를 추가하고 GetLifetimeReplicatedProps를 통해 서버-클라이언트 간 데이터 동기화 보장.
3. 핵심 코드 (Code Snippets)
// AFSkillMainWidget.cpp
void UAFSkillMainWidget::UpdateSkillSlots(const TArray<FAFSkillInfo>& CharacterSkills)
{
// 방어적 프로그래밍: 배열이 비어있다면 지연 초기화 수행
if (SlotArray.Num() == 0) {
InitializeSlotArray(); // Slot_0 ~ Slot_4를 배열에 Add
}
// 데이터와 슬롯 개수 중 최소값만큼 안전하게 루프
int32 MaxLoop = FMath::Min(SlotArray.Num(), CharacterSkills.Num());
for (int32 i = 0; i < MaxLoop; ++i) {
if (SlotArray[i]) {
SlotArray[i]->SetSkillSlotInfo(CharacterSkills[i]);
}
}
}
4. 회고 및 포인트 (Insight)
- 기술적 성장: 블루프린트의 Is Valid 에러(isnotvalid)를 해결하기 위해 C++ 레벨에서 위젯 수명 주기를 관리하는 방법을 익힘.
- 협업 관점: 데이터 테이블 기반으로 스킬 시스템을 구축하여, 기획자가 C++ 수정 없이 데이터 테이블만으로 캐릭터 스킬 구성을 변경할 수 있는 유연한 구조를 설계함.
- 멀티플레이어 이해: 서버와 클라이언트의 실행 시점이 다르다는 점을 인지하고, 이를 고려한 UI 갱신 타이밍(Timer 및 Replication)의 중요성을 체득함.