오늘은 아케인보드 시스템에서 직업을 변경하고 빠르게 위젯을 열면 이전 직업의 그리드가 보이는 문제를 해결했음
처음엔 델리게이트 타이밍이나 위젯 초기화 순서 문제인 줄 알았는데, 알고 보니 네트워크 복제 지연과 UI 반응성 사이의 근본적인 동기화 문제였음
최종적으로 델리게이트 방식을 포기하고 버튼 클릭 시 즉시 업데이트하는 직접 호출 방식으로 해결했음
아케인보드 시스템을 테스트하니까 뭔가 이상했음
문제 상황

사용자 입장에서는 직업을 바꿨는데 이전 설정이 보이니까 정말 불쾌한 경험이었음
코드 흐름을 자세히 추적해봤음
현재 시스템 구조
1. 버튼 클릭 → Server_SetSeekerJob() RPC 호출
2. 서버에서 PlayerState 업데이트
3. 클라이언트로 복제 (시간 소요 )
4. OnRep_SeekerJob() → 델리게이트 호출
5. ArcaneBoardLPS::OnPlayerJobChanged() 실행
6. BoardManager 업데이트
핵심 문제점
여러 해결책을 시도해봤음
시도 1: 델리게이트에 직업 정보 추가
// 기존: OnJobChangedDelegate(EPlayerRole)
// 수정: OnJobChangedDelegate(EPlayerRole, ESeekerJob)
→ 여전히 네트워크 복제 지연 문제 해결 안 됨
시도 2: 위젯에서 PlayerState 델리게이트 바인딩
// 아케인보드 위젯이 열린 후에도 직업 변경 감지
Widget->BindPlayerStateDelegate()
→ 위젯이 열린 후 그리드가 바뀌는 UX 문제 발생
시도 3: 타이머 기반 재확인
// 0.1초 후 다시 확인
GetWorld()->GetTimerManager().SetTimer(...)
→ 불확실하고 근본적 해결책이 아님
결국 델리게이트를 포기하고 버튼 클릭 시 즉시 업데이트하는 방식으로 변경했음
최종 해결책
// GS_CharacterSelectList.cpp
void UGS_CharacterSelectList::OnCharacterSelectClicked(int32 CharacterID, EPlayerRole PlayerRole)
{
if (PlayerRole == EPlayerRole::PR_Seeker)
{
// 즉시 아케인보드 업데이트 (새로 추가)
if (UGS_ArcaneBoardLPS* LPS = LocalPlayer->GetSubsystem<UGS_ArcaneBoardLPS>())
{
ECharacterClass NewCharacterClass = LPS->MapSeekerJobToCharacterClass((ESeekerJob)CharacterID);
LPS->OnPlayerJobChanged(NewCharacterClass); // 직접 호출
}
// 기존 RPC 호출
GSPlayerState->Server_SetSeekerJob((ESeekerJob)CharacterID);
}
}
코드 단순화

이제 직업을 변경하고 즉시 아케인보드를 열어도 올바른 그리드가 바로 보임
설계 개선사항
깨달은 점
멀티플레이어 게임에서 네트워크 복제는 항상 지연이 있다는 것을 항상 고려해야 함. 특히 UI 반응성이 중요한 부분은 복제를 기다리지 말고 즉시 로컬에서 처리하는 것이 사용자 경험에 훨씬 좋음.
// 안티패턴: 복제 완료를 기다림
버튼 클릭 → RPC → 복제 → 델리게이트 → UI 업데이트
// 권장패턴: 즉시 로컬 업데이트 + 백그라운드 동기화
버튼 클릭 → 즉시 UI 업데이트 + RPC (백그라운드)