2026-07-15 TIL (98일차)
2026-07-15 TIL (98일차)
테스트 UI → 실제 UI 마이그레이션 (로비/역할 선택 연동)
오늘 한 일
팀원이 만든 실제 UI 위젯에, 그동안 테스트용 UI로 검증해온 멀티플레이 연동 로직을 이식하는 작업을 진행했다. 핵심은 로비에서 여러 클라이언트가 같은 역할 선택 화면을 공유하도록 만드는 것이다.
구조 파악: UI가 C++과 연결되는 지점
먼저 위젯이 어떻게 C++과 붙어있는지 정리했다.
- 진입점 (데이터 테이블): 화면 전환의 진입점은 데이터 테이블이다.
LocalPlayerSubsystem이GameState의OnCurrentMapChanged를 구독하다가, 맵이 바뀌면DA_CrazyRooStageInfo의StageUIClass를 로드해서CreateWidget→AddToViewport한다. 즉, 최상위 화면 교체는 데이터 테이블 행만 바꾸면 된다. - 순수 UUserWidget: 모든 위젯의 부모는 순수
UUserWidget이며, C++ 연동은 전부 그래프 내부의 함수 호출과 델리게이트 바인딩으로 이뤄진다. - 상태의 진실 (Source of Truth): “누가 어떤 역할인지”에 대한 진실은
PlayerState에 있고 리플리케이트(Replicate)된다. UI는 그걸 읽어서 그리는 역할만 담당한다.
핵심: “공유 화면”은 리플리케이션 + 이벤트
역할 선택을 모두가 같이 보려면, 각자 로컬에서 상태를 임의로 바꾸면 안 된다. 흐름은 이렇게 정리됐다.
- 클릭 → 서버에 역할 요청(RPC)
- 서버가
IsRoleTaken()으로 중복 검사 후 승인/거부 - 승인되면
PlayerState.PlayerRole변경 → 모든 클라이언트에 리플리케이트 - 각 클라이언트에서 값 도착 시 UI 갱신
- 델리게이트 통로 개설: 문제는
OnRep_PlayerRole이 로그만 찍고 UI에 알리는 통로가 없었다는 것. 그래서PlayerState에BlueprintAssignable델리게이트를 추가하고, 값이 바뀌는 4곳(OnRep, 서버 측Set함수)에서 브로드캐스트하도록 했다. - [배운 점 1] 리슨 서버/스탠드얼론의 한계: 리슨 서버나 스탠드얼론에서는
OnRep이 자동 호출되지 않는다. 그래서 클라용OnRep뿐 아니라 서버 측Set함수에서도 똑같이 브로드캐스트해줘야 호스트 화면도 정상 갱신된다. - [배운 점 2] 후발 참가자 바인딩 문제: 위젯이 뜬 뒤에 입장한 플레이어의
PlayerState에는 바인딩이 안 걸린다.GameState의AddPlayerState/RemovePlayerState를 오버라이드해서 “명단 바뀜” 이벤트를 쏘고, 그때마다 전체 재바인딩 + 다시 그리기로 해결했다. 호스트 판정이 위젯 생성보다 늦게 도착하는 타이밍 문제도 이 이벤트로 같이 풀렸다.
작업한 위젯들
RoomSearch: 방 검색/참가 로직은 이미 완성돼 있어서 내부의 테스트 에셋 참조(행 위젯, 확인 팝업)만 실제 에셋으로 교체.MainMenu: EOS 로그인 → 로그인 완료 시 시작 버튼 활성화 → 방 목록 표시로 이어지는 흐름 이식.RoleSelect(오늘의 본체): 로컬 즉시 갱신 로직을 걷어내고, 리플리케이션 기반으로 “누가 어느 역할을 골랐는지”를 전원이 공유하도록 재작성.
RoleSelect 표시 방식 결정
- P번호로 점유자 표시: 닉네임은 EOS 로그인 방식에 따라 개발 중엔 크리덴셜 문자열 같은 게 뜬다. 그래서 이미 리플리케이트되는
JoinOrder를 써서 “P1~P4”로 표시하기로 했다. 닉네임은 배포 단계에서 텍스트만 갈아끼우면 되고 구조는 안 바뀐다. - 선택 완료 비주얼 + 버튼 잠금: 누군가 역할을 고르면 그 카드가 작아지고 “선택 완료”로 바뀌며, 본인을 제외한 모두에게 그 버튼이 잠긴다. 상태의 근원이 하나(리플리케이트된 역할)라서 표시와 잠금이 어긋날 수 없다.
- 대기하기 = 선택 해제: 서버가
None요청을 항상 승인하고 역할을 반납하도록 구현돼 있어서, 별도 작업 없이 “대기하기”를 누르면 카드가 원상복구되고 버튼이 다시 풀린다. - UX 방어선은 서버: 리플리케이션 도착 전 짧은 틈에 남이 같은 버튼을 누를 수 있다. 버튼 잠금은 편의일 뿐, 진짜 방어는 서버 거부 + 거부 메시지 표시다.
발견한 이슈 (다음 작업)
RoomSearch팝업 버그:RoomSearch가 화면에 뜨자마자 “방을 생성하시겠습니까?” 팝업이 자동으로 뜨는 현상 발견.- 원인 추정: 이 게임은 데디케이티드 서버 1대 = 방 1개 구조라, 클라가 직접 방을 파는 게 아니라 빈 서버를 찾아 방장이 되는 방식이다. 따라서 팝업은 방 생성 버튼을 눌렀을 때만 떠야 하는데,
RoomSearch가RoomList를 복제·개조하는 과정에서Construct시점 동작이 달라진 것으로 추정된다.Construct체인의 자동 호출이나 팝업 기본 Visibility가 원인일 가능성이 높아 다음에 확인할 예정이다.
정리하며
가장 크게 배운 건, 멀티플레이 UI에서 “보이는 상태”의 근원을 딱 하나(서버가 리플리케이트하는 값)로 두면 모든 클라 화면이 저절로 일치한다는 감각이다. 로컬에서 미리 바꾸고 싶은 유혹을 참는 게 오히려 버그를 없앤다. 그리고 리플리케이션 타이밍(늦게 오는 값, 늦게 들어오는 플레이어)을 이벤트로 잡아주는 게 실전에서는 로직 자체만큼 중요했다.
This post is licensed under CC BY 4.0 by the author.