1. 개요
멀티플레이어 게임을 개발하다 보면 클라이언트와 서버 간의 실행 흐름을 추적하는 것이 매우 중요하다. 기존 언리얼 엔진의 UE_LOG나 블루프린트의 Print String만으로는 데디케이티드 서버(Dedicated Server) 환경이나 복잡한 네트워크 권한(Role) 상태를 한눈에 파악하기 어렵다.
이를 해결하기 위해 프로젝트 전역에서 사용할 수 있는 멀티플레이어 전용 C++ 디버그 유틸리티 라이브러리를 구축했다.
2. 핵심 구현 내용
가. 커스텀 로그 매크로 (SM_LOG)
멀티플레이어 환경에서는 "이 코드가 도대체 어느 클라이언트에서, 혹은 서버에서 실행되었는가?"를 아는 것이 디버깅의 0순위다.
DEFINE_LOG_CATEGORY(LogSM)를 통해 프로젝트 전용 로그 카테고리를 생성했다.GetNetMode()와UE::GetPlayInEditorID()를 활용해 현재 환경이Client(번호),Server,StandAlone중 무엇인지 자동으로 접두사로 붙여주는 매크로를 작성했다.__FUNCTION__매크로를 조합해 호출된 함수 이름까지 로그에 한 번에 찍히도록 구성하여, 복잡한 GAS 어빌리티의 실행 순서를 완벽하게 추적할 수 있게 되었다.
나. 진화된 화면 출력 함수 (SMPrintString)
기존의 화면 출력 기능은 화면이 없는 데디케이티드 서버(Dedicated Server) 환경에서 무용지물이 되는 단점이 있었다. 이를 보완하는 스마트한 출력 함수를 만들었다.
- 현재 실행 중인 월드의 넷 모드(
GetNetMode())를 검사한다. - 화면이 존재하는
NM_Client나NM_ListenServer일 경우GEngine->AddOnScreenDebugMessage를 통해 화면에 즉각적으로 텍스트를 띄운다. - 화면이 없는 데디케이티드 서버일 경우, 허공에 텍스트를 날리지 않고
UE_LOG를 통해LogSM카테고리의 출력 로그 창으로 데이터를 안전하게 우회시킨다.
다. 네트워크 상태 추적기 (GetNetModeString & GetRoleString)
액터의 머리 위(TextRender)나 디버그 UI에 현재 접속 상태를 상시로 띄워두기 위한 유틸리티 함수다.
GetNetModeString: 현재 월드가 클라이언트인지, 서버인지 문자열로 반환한다.GetRoleString: 특정 액터의LocalRole과RemoteRole을Authority / SimulatedProxy형태로 깔끔하게 포맷팅하여 반환한다. 이를 통해 내 화면에 보이는 다른 유저의 껍데기 캐릭터가 정상적으로 동기화되고 있는지 직관적으로 확인할 수 있다.
3. 핵심 기술 인사이트: UBlueprintFunctionLibrary와 WorldContext
이번 유틸리티를 순수 C++ 클래스가 아닌 UBlueprintFunctionLibrary로 승급시키면서, 언리얼 엔진의 WorldContext(월드 컨텍스트) 개념을 깊이 이해하게 되었다.
- WorldContext의 필요성: 정적(Static) 함수는 특정 월드에 속해 있지 않기 때문에, 화면에 로그를 띄우려면 "어느 월드(에디터, PIE, 프리뷰 등)를 기준으로 실행할지" 엔진에게 알려주는 나침반이 필요하다.
AActor*vsUObject*: 처음에는 나침반 역할을 할 매개변수로AActor*를 받았으나, 이 경우 액터가 아닌 UI(UserWidget)나GameInstance에서는 함수를 호출할 수 없는 치명적인 제약이 있었다. 이를 최상위 클래스인UObject*로 변경하여 범용성을 극대화했다.- 숨겨진 메타 데이터의 마법:
UFUNCTION매크로에meta = (WorldContext = "WorldContextObject")를 추가했다. 이 한 줄 덕분에 블루프린트 에디터에서 해당 함수의WorldContextObject입력 핀이 자동으로 숨겨지며, 노드를 호출한 주체(Self)가 백그라운드에서 자동으로 연결된다. 팀원들은 복잡한 컨텍스트 연결 없이 일반 노드처럼 편하게 디버깅 함수를 꺼내 쓸 수 있게 되었다.
4. 마무리
팀 프로젝트에서 나 혼자 C++ 코드를 잘 짜는 것을 넘어, 기획자나 아트 등 다른 파트 팀원들도 블루프린트에서 손쉽게 멀티플레이어 환경을 디버깅할 수 있는 공용 인프라를 구축했다. 단순히 기능을 구현하는 코더가 아니라, 팀의 생산성을 높이는 아키텍처적 고민을 할 수 있었던 뜻깊은 시간이었다.
'프로그래밍 > Unreal Engine 5' 카테고리의 다른 글
| [Unreal Engine 5] GAS_멀티플레이에서 GameplayCue가 작동하는 방식 (1) | 2026.04.17 |
|---|---|
| [Unreal Engine 5] 멀티플레이 아키텍처: GAS 퀵슬롯과 인벤토리 설계 (0) | 2026.04.09 |
| [Unreal Engine 5] Module과 Plugin의 차이점 (0) | 2026.04.02 |
| [Unreal Engine 5] 싱글플레이어 vs 멀티플레이어(DedServer) GAS 아키텍처의 결정적 차이 (0) | 2026.03.30 |
| [Unreal Engine 5] 언리얼 엔진의 포인터 안전성 검사: Null 체크 vs IsValid vs C++17 (0) | 2026.03.27 |