멀티플레이어 게임 최적화
복제량을 측정하고 예측·보간·권한 검증을 조정해 대역폭과 지연이 플레이 경험에 미치는 비용을 줄입니다.
이전 절에서 복제와 RPC를 익혔다면, 이제 멀티플레이어 품질을 좌우하는 성능 최적화 단계로 넘어갑니다.
멀티플레이어 개발은 기능 구현만으로 끝나지 않습니다.
지연(Latency), 대역폭 제한(Bandwidth), 패킷 손실(Packet Loss) 같은 네트워크 이슈를 제어하지 못하면 플레이 경험이 빠르게 무너집니다.
이번 절에서는 언리얼 엔진에서 멀티플레이어 게임의 성능을 향상시키기 위한 다양한 최적화 기법과 고려 사항에 대해 자세히 살펴보겠습니다.
핵심 흐름은 네트워크 통계·트레이스로 병목을 측정하고 복제량을 조정한 뒤, 지연·손실 조건에서 플레이 결과를 다시 확인하는 것입니다.
네트워크 대역폭 관리
네트워크 대역폭은 제한된 자원이며, 이를 효율적으로 사용하는 것이 멀티플레이어 최적화의 핵심입니다.
필요한 것만 복제하기
- 변수 복제 최소화: 모든 변수를 무조건 복제하지 마세요. 클라이언트에게 반드시 동기화되어야 하는 데이터만
UPROPERTY(Replicated)로 설정해야 합니다. 예를 들어, 서버에서만 사용되는 임시 계산 값이나 예측에 불필요한 중간 계산 값은 전송에서 제외할 수 있습니다. 예측하더라도 권위 상태와의 비교·보정에 필요한 정보는 전달해야 합니다. - 조건부 복제 (
DOREPLIFETIME_COND):COND_OwnerOnly,COND_SkipOwner,COND_InitialOnly등 조건부 복제 플래그를 적극적으로 활용하여 특정 클라이언트에게만 데이터를 보내거나, 해당 연결의 초기 복제 묶음에만 보내는 방식으로 대역폭을 절약합니다.- 예시: 플레이어의 개인 인벤토리 정보는
COND_OwnerOnly로 복제하여 해당 플레이어에게만 보내고, 다른 플레이어에게는 복제하지 않습니다.
- 예시: 플레이어의 개인 인벤토리 정보는
- RPC 사용 최소화: RPC는 변수 복제보다 대역폭을 더 많이 소비할 수 있습니다. 특히
ReliableRPC는 재전송 메커니즘 때문에 더 그렇습니다. 빈번한 업데이트에는 변수 복제를 우선적으로 고려하고, 이벤트성 액션에만 RPC를 사용하세요.
압축 및 패킷 최적화
언리얼 엔진은 기본적으로 네트워크 패킷을 최적화하지만, 개발자가 더 나은 압축을 위해 데이터를 준비할 수 있습니다.
- 표현 정밀도: 범위와 오차 허용치를 먼저 정하고
FVector_NetQuantize같은 지원되는 네트워크 표현을 검토합니다. C++ 타입의 메모리 크기만으로 실제 전송 비트 수가 정해지지는 않으므로 직렬화 결과를 측정합니다. - 불필요한 데이터 제거: 복제되는 구조체나 배열에 불필요한 필드가 없는지 확인하세요.
- 관련성(Relevancy): 소유 연결·거리·관련성 규칙에 따라 연결별 복제 대상을 고릅니다.
bOnlyRelevantToOwner,NetCullDistanceSquared등을 검토하며, 카메라 밖이거나 다른 물체에 가려졌다는 이유만으로 자동 제외된다고 가정하지 않습니다. - 휴면(Dormancy): 잘 바뀌지 않는 액터를 복제 검사에서 제외합니다. C++에서는 복제 속성을 바꾸기 전에 소유 액터를 깨우거나
FlushNetDormancy로 갱신해야 변경을 잃지 않습니다. 관련성과 휴면은 다른 기준입니다.
네트워크 지연(Latency) 숨기기 및 예측
네트워크 지연은 물리적으로 피할 수 없는 현상입니다.
플레이어가 입력 후 바로 결과를 보지 못하고 지연되는 렉(Lag)을 줄이기 위해 다양한 기법을 사용합니다.
클라이언트 예측 (Client-Side Prediction)
- 플레이어가 입력을 했을 때, 서버의 응답을 기다리지 않고 클라이언트가 먼저 결과를 예측하여 적용합니다 (예: 이동, 총 발사).
- 이후 서버에서 진실된 결과가 도착하면, 클라이언트는 자신의 예측이 맞았는지 확인하고, 예측이 틀렸다면 보정(Correction)하여 상태를 동기화합니다.
- 이 과정이 복잡하고 버그를 유발하기 쉽지만, 반응성을 크게 향상시켜 플레이어 경험을 부드럽게 만듭니다. 언리얼 엔진의
UCharacterMovementComponent는 이동 예측을 기본적으로 처리합니다.
보간 (Interpolation) 및 보정 (Smoothing)
- 보간: 다른 플레이어의 캐릭터나 복제된 액터의 위치가 네트워크 업데이트 간에 부자연스럽게 점프하는 것을 방지하기 위해, 클라이언트는 이 액터들의 과거 위치와 현재 수신된 위치 사이를 부드럽게 보간하여 움직임을 매끄럽게 만듭니다.
- 보정: 클라이언트 예측 후 서버로부터 정확한 데이터가 도착했을 때, 예측과 권위 상태의 차이를 조정합니다. 오차 크기와 설정에 따라 부드럽게 보정하거나 즉시 위치를 맞출 수 있습니다.
네트워크 프로파일링 및 디버깅
최적화는 측정 없이는 불가능합니다.
언리얼 엔진은 강력한 네트워크 디버깅 도구를 제공합니다.
stat net: 지원되는 개발 환경에서 네트워크 통계를 확인합니다.- Networking Insights: Unreal Insights를 준비하고
-trace=net -NetTrace=1로 기록할 수 있습니다. 에디터에서는-tracehost=localhost등 수집 연결 설정도 확인합니다. 패킷·속성·RPC별 비용을 비교합니다. - Network Profiler: 기존 프로파일러를 지원하는 STATS 빌드에서는
netprofile enable/netprofile disable로 기록합니다. 엔진 버전과 빌드 구성을 먼저 확인합니다. - 로그:
log LogNet Verbose또는log LogNet VeryVerbose로 지원되는 로그 범위를 늘립니다. - 네트워크 에뮬레이션: 에디터의 Network Emulation 설정에서 지연·손실 조건을 적용합니다. 버전별 UI·지원 항목을 확인하며, 이 문서는 해당 조건을 실행 측정한 결과가 아닙니다.
측정된 병목에 맞춰 변경하고 전달 정확성을 다시 확인합니다.
| 관측할 비용 | 검토할 변경 | 다시 확인할 동작 |
|---|---|---|
| 속성·액터 전송량 | 필요한 상태, 소유 조건과 관련성 범위를 좁힙니다. | 접속 직후·소유권 변경·거리 재진입에도 필요한 상태가 도착하는지 확인합니다. |
| 변하지 않는 액터의 검사 비용 | 변경 빈도가 낮은 액터에 휴면을 검토합니다. | 속성 변경 전에 깨우거나 flush하고, 재수면 뒤에도 값이 유실되지 않는지 확인합니다. |
| RPC 빈도·재전송 비용 | 중복 호출과 페이로드를 줄이고 이벤트 의미에 맞는 신뢰성을 고릅니다. | 지연·손실에서도 영구 상태가 어긋나지 않는지, 복구할 상태 경로가 있는지 확인합니다. |
| 예측·보정 비용 | 표현 정밀도와 이동 업데이트 정책을 조정합니다. | 반응성뿐 아니라 권위 상태와의 오차, 순간 보정과 시각적 품질을 함께 비교합니다. |
- 속성·액터 전송량
- 검토할 변경: 필요한 상태, 소유 조건과 관련성 범위를 좁힙니다.다시 확인할 동작: 접속 직후·소유권 변경·거리 재진입에도 필요한 상태가 도착하는지 확인합니다.
- 변하지 않는 액터의 검사 비용
- 검토할 변경: 변경 빈도가 낮은 액터에 휴면을 검토합니다.다시 확인할 동작: 속성 변경 전에 깨우거나 flush하고, 재수면 뒤에도 값이 유실되지 않는지 확인합니다.
- RPC 빈도·재전송 비용
- 검토할 변경: 중복 호출과 페이로드를 줄이고 이벤트 의미에 맞는 신뢰성을 고릅니다.다시 확인할 동작: 지연·손실에서도 영구 상태가 어긋나지 않는지, 복구할 상태 경로가 있는지 확인합니다.
- 예측·보정 비용
- 검토할 변경: 표현 정밀도와 이동 업데이트 정책을 조정합니다.다시 확인할 동작: 반응성뿐 아니라 권위 상태와의 오차, 순간 보정과 시각적 품질을 함께 비교합니다.
수치가 제시된 벤치마크가 아니라 측정·판단 항목입니다. World Partition의 셀 스트리밍은 별도 서버 분산 기능이 아닙니다.
서버 권한(Authority) 유지 및 치트 방지
네트워크 최적화뿐만 아니라 보안도 중요합니다.
서버 권한을 올바르게 유지하는 것은 치트를 방지하는 핵심입니다.
- 모든 중요한 게임 로직은 서버에서: 데미지 계산, 아이템 획득, 탄약 소모, 점수 부여 등 게임의 핵심 규칙과 상태 변경은 반드시 서버에서만 처리되어야 합니다. 클라이언트가 조작된 데이터를 보내도 서버가 이를 검증하고 무시해야 합니다.
HasAuthority()검사: 서버에서 생성한 공유 액터의 권위 상태 변경을 제한합니다. 이는 액터 역할 검사이며 클라이언트 전용 로컬 액터도 Authority일 수 있으므로 서버 프로세스 검사와 혼동하지 않습니다.- 서버 검증: 중요한 입력과 게임 규칙은 서버에서 검사합니다.
WithValidation은 선택 사항이며 false는 연결을 끊습니다. 정상적인 게임상 거절은_Implementation의 분기로 처리할 수 있습니다.
연결 관리 및 세션 시스템
플레이어의 접속과 해제, 매치메이킹 등은 네트워크 최적화에 간접적으로 영향을 미칩니다.
- 세션 관리: Online Subsystem의
IOnlineSubsystem에서 얻은IOnlineSession인터페이스 등으로 세션 생성·검색·참여를 처리합니다. 실제 지원 기능과 로그인 절차는 선택한 플랫폼 서비스에 따라 다릅니다. - 연결 끊김 처리: 네트워크 문제로 인해 플레이어의 연결이 끊겼을 때, 게임이 안정적으로 처리되고 다른 플레이어에게 영향을 주지 않도록 로직을 구현해야 합니다.
로드 밸런싱 및 확장성 (고급)
대규모 멀티플레이어 게임의 경우, 단일 서버의 성능 한계를 넘어 여러 서버가 동시에 작동하는 로드 밸런싱과 확장성 설계가 필요합니다.
- 서버 영역 분할: 여러 서버에 영역을 나누고 이동 시 상태를 인계하려면 별도의 서버·세션 설계가 필요합니다. 엔진의 World Partition은 월드 셀과 스트리밍을 관리하며 서버 분산·핸드오프를 자동 구현하지 않습니다.
- 인스턴스 시스템: 던전이나 인스턴스 전장처럼 특정 구역에 대한 독립적인 서버 인스턴스를 생성하여 부하를 분산합니다.
동일한 장면과 연결 조건으로 전후 비용을 비교하고, 수신 대상에서 상태·이벤트가 여전히 올바르게 보이는지 함께 확인합니다.