1. 디버그 로깅 매크로
// 프로그램명.h
#pragma once
#include "CoreMinimal.h"
#pragma region NetLogging
DEDICATEDX_API DECLARE_LOG_CATEGORY_EXTERN(LogDXNet, Log, All);
#define NETMODE_TCHAR ((GetNetMode() == ENetMode::NM_Client) ? *FString::Printf(TEXT("Client%02d"), UE::GetPlayInEditorID()) : ((GetNetMode() == ENetMode::NM_Standalone) ? TEXT("StandAlone") : TEXT("Server")))
#define FUNCTION_TCHAR (ANSI_TO_TCHAR(__FUNCTION__))
#define DX_LOG_NET(LogCategory, Verbosity, Format, ...) UE_LOG(LogCategory, Verbosity, TEXT("[%s] %s %s"), NETMODE_TCHAR, FUNCTION_TCHAR, *FString::Printf(Format, ##__VA_ARGS__))
#pragma endregion
// 프로그램명.cpp
#include "DedicatedX.h"
#include "Modules/ModuleManager.h"
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, DedicatedX, "DedicatedX" );
#pragma region NetLogging
DEFINE_LOG_CATEGORY(LogDXNet);
#pragma endregion
이 매크로는 모든 로그 앞에 [Server], [Client01] 같은 표시와 함수명을 자동으로 붙여준다.
PIE에서 여러 클라이언트를 동시에 띄워 테스트해도 어느 쪽 로그인지 바로 구분할 수 있다는 점에서 멀티플레이 디버깅의 출발점이 되는 도구다
*FString 표기에 대해
*ClientConnection->GetName()
GetName()은 FString을 반환하는데, UE_LOG의 %s는 TCHAR*만 받을 수 있다.
FString 앞에 *를 붙이면 그 내부의 TCHAR 문자열을 꺼내주는 변환 연산자가 동작한다.
(포인터 역참조의 *와 모양은 같지만 다른 용도)
"FString을 로그에 찍을 땐 앞에 별표를 붙인다"는 약속으로 받아들이는 게 편하다
2. 접속 과정 디버깅: PreLogin → Login → PostLogin
void ADXGameModeBase::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
UNetDriver* ServerNetDriver = GetNetDriver();
if (IsValid(ServerNetDriver) == true)
{
for (const auto& ClientConnection : ServerNetDriver->ClientConnections)
{
if (IsValid(ClientConnection) == true)
{
DX_LOG_NET(LogDXNet, Log, TEXT("Client Connection: %s"), *ClientConnection->GetName());
}
}
}
}
GameMode::PostLogin()은 서버에만 존재하는 액터의 함수라서 "서버가 클라이언트 접속을 인지하고 있는가"를 확인하기에 가장 적합한 위치다.
ServerNetDriver->ClientConnections를 순회하면 지금 서버에 붙어있는 모든 연결을 확인할 수 있다.
반대로 클라이언트 입장에서 "내가 서버에 잘 연결됐는가"를 확인하려면 PlayerController::PostNetInit()을 본다 PlayerController는 로컬 클라이언트 자신에게만 스폰되기 때문이다
이게 실제로 어떻게 쓰이는가
이 로그는 무언가를 직접 고치는 코드가 아니라 "문제가 어느 단계에서 발생했는지" 범위를 좁히는 용도다
예를 들어 "클라이언트가 접속했는데 캐릭터가 안 보인다"는 증상이 있을 때, PostLogin에서 ClientConnection이 찍히지 않는다면 네트워크 연결 자체의 문제(방화벽, 포트 등)다
ClientConnection은 찍히는데 캐릭터가 안 보인다면 연결은 정상이고 캐릭터 스폰 로직이나 그 이후 단계의 문제다.
3. Owner 속성
PlayerController가 Pawn을 Possess하면 그 Pawn의 Owner는 해당 PlayerController로 설정된다
일부 RPC와 Property Replication은 "이 액터의 Owner가 누구인가"에 따라 전송 대상이 달라지기 때문에, 캐릭터 관련 복제 버그를 만나면 "Owner가 제대로 잡혔는가"를 의심 목록에 넣어야 한다는 걸 배웠다
'학습 > Unreal' 카테고리의 다른 글
| NetLoadOnClient, Replication Notify (OnRep), Conditional Property Replication (0) | 2026.06.29 |
|---|---|
| 게임플레이 프레임워크 (0) | 2026.06.24 |
| Property Replication (기초) (0) | 2026.06.22 |
| 서버를 거치는 통신 구조 (0) | 2026.06.22 |
| Remote Procedure Call 기초 (RPC 기초) (0) | 2026.06.19 |
