Unreal Engine 5(UE5)에서 네트워크 멀티플레이어를 구축할 때는 RPC(Remote Procedure Call)Replication(리플리케이션) 두 가지 개념이 핵심입니다.
이 문서는 UE5 기준으로 RPC와 리플리케이션의 원리를 설명하고, C++에서 구현하는 방법 및 코드 예시, 그리고 대역폭 최적화·보안 기법·디버깅 팁 등을 정리한 심층 가이드입니다.


목차

  1. RPC(Remote Procedure Call)
    1. RPC 개념
    2. RPC 종류
    3. Reliable vs Unreliable
    4. RPC C++ 구현 예시
  2. Replication(리플리케이션)
    1. Replication 개념
    2. 변수/액터 리플리케이션 구현
    3. ReplicatedUsing / OnRep 사용
    4. Replication C++ 예시
  3. 최적화 고려 사항
    1. 신뢰성 수준(Reliable/Unreliable) 분배
    2. Replication 빈도 및 조건 제어
    3. Net Dormancy와 Relevancy
    4. 멀티캐스트(RPC) 최소화
  4. 보안 고려 사항
    1. 서버 권위 모델
    2. RPC WithValidation
    3. Owner·Role 검사 및 서버 측 논리 검증
  5. 네트워크 디버깅 & 프로파일링
    1. Stat Net
    2. Network Profiler & Unreal Insights
    3. 패킷 손실·지연 시뮬레이션
    4. 로그와 경고 확인
  6. 결론
  7. 참고 자료


1. RPC(Remote Procedure Call)

RPC 개념

  • RPC는 클라이언트와 서버가 서로 함수를 호출하는 메커니즘입니다.
  • UE5에서는 C++에서 UFUNCTION() 매크로에 특정 키워드를 붙여 구현합니다.
    • Server: 클라이언트가 함수를 호출하면 서버에서 실행
    • Client: 서버에서 함수를 호출하면 특정 클라이언트(소유자)에서 실행
    • NetMulticast: 서버에서 함수를 호출하면 서버 & 모든 클라이언트에서 실행

RPC 종류

  1. Server RPC (클라이언트 → 서버)

    • 클라이언트가 호출, 서버에서 실행
    • 주로 입력 처리(예: 공격 요청)나 상태 변경 알림을 서버에 전달할 때 사용
    • UFUNCTION(Server, Reliable/Unreliable)로 선언
  2. Client RPC (서버 → 특정 클라이언트)

    • 서버가 호출, 특정 클라이언트(Actor의 Owner)에서 실행
    • 개인적인 알림(예: 개인 점수 갱신)이나 UI 업데이트에 활용
    • UFUNCTION(Client, Reliable/Unreliable)
  3. NetMulticast RPC (서버 → 모든 클라이언트 + 서버 자신)

    • 서버가 호출, 연결된 모든 클라이언트 및 서버 본인에서 실행
    • 폭발 이펙트나 전역 이벤트 브로드캐스트 등에 사용
    • UFUNCTION(NetMulticast, Reliable/Unreliable)

주의: NetMulticast RPC는 서버에서만 브로드캐스트 효과가 발생합니다. 클라이언트가 호출하면 자기 자신에게만 실행되고 다른 클라이언트에는 전달되지 않습니다.

Reliable vs Unreliable

  • Reliable RPC

    • 반드시 도달이 보장, 재전송을 통해 안정성을 확보
    • 중요한 게임 로직(사망 판정, 아이템 획득 등)에 사용
    • 너무 자주 사용하면 네트워크 지연이 늘어날 수 있음
  • Unreliable RPC

    • 손실될 수 있지만(미보장) 오버헤드가 적음
    • 매우 빈번하거나, 한두 번 누락되어도 게임 진행에 큰 문제 없는 경우 사용
    • 탄환 발사 이펙트, 발소리 재생 등 시각·청각 효과 트리거에 적합

RPC C++ 구현 예시

// 예: 캐릭터가 공격(발사) 요청 시 서버에 알리고, 모든 클라이언트에 이펙트를 뿌리는 예

// MyCharacter.h
UCLASS()
class AMyCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    void HandleFire(); // 로컬 입력 핸들

protected:
    // 클라이언트 -> 서버
    UFUNCTION(Server, Reliable)
    void ServerHandleFire();

    // 서버 -> 전체(멀티캐스트, 비중요 효과는 Unreliable 사용 가능)
    UFUNCTION(NetMulticast, Unreliable)
    void MulticastPlayMuzzleFlash();

    void PerformFire();
    // 총알 스폰용 클래스
    UPROPERTY(EditDefaultsOnly)
    TSubclassOf<AActor> BulletClass;

    // 이펙트
    UPROPERTY(EditDefaultsOnly)
    UParticleSystem* MuzzleFlash;
};
// MyCharacter.cpp
#include "MyCharacter.h"
#include "Net/UnrealNetwork.h"

void AMyCharacter::HandleFire()
{
    // 서버 권한이면 직접 실행, 클라면 서버에 RPC
    if (HasAuthority())
    {
        PerformFire();
    }
    else
    {
        ServerHandleFire();
    }
}

void AMyCharacter::ServerHandleFire_Implementation()
{
    PerformFire(); // 서버에서 총알 스폰 + 이펙트 브로드캐스트
}

void AMyCharacter::PerformFire()
{
    if (!HasAuthority()) return;

    // 탄환 생성(서버)
    if (BulletClass)
    {
        FVector SpawnLoc = GetActorLocation() + GetActorForwardVector() * 100.0f;
        FRotator SpawnRot = GetControlRotation();

        FActorSpawnParameters Params;
        Params.Owner = this;
        Params.Instigator = this;

        GetWorld()->SpawnActor<AActor>(BulletClass, SpawnLoc, SpawnRot, Params);
    }

    // 총구이펙트 모든 클라에 뿌림 (Unreliable)
    MulticastPlayMuzzleFlash();
}

void AMyCharacter::MulticastPlayMuzzleFlash_Implementation()
{
    if (MuzzleFlash)
    {
        UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), MuzzleFlash, GetActorLocation());
    }
}


2. Replication(리플리케이션)

Replication 개념

  • Replication은 서버가 가지고 있는 Actor(또는 ActorComponent)와 변수의 상태를 클라이언트와 동기화해주는 시스템입니다.
  • 주로 지속적인 상태 데이터(체력, 탄약, 위치 등)를 자동으로 전파하고, 클라이언트는 이를 읽기 전용으로 사용합니다.

변수/액터 리플리케이션 구현

  1. Actor 복제 설정

    • bReplicates = true;로 설정해야 해당 Actor가 네트워크 복제 대상이 됩니다.
    • 서버에서 Spawn할 때 bReplicates가 설정된 Actor는 연결된 클라이언트들에게 자동으로 생성·동기화됩니다.
  2. Replicated 변수 선언

    • 헤더에서 UPROPERTY(Replicated) 또는 UPROPERTY(ReplicatedUsing=OnRep_X)를 선언
    • 클래스 안에서 GetLifetimeReplicatedProps 오버라이드하여 DOREPLIFETIME(MyClass, MyVar) 매크로로 변수 등록
// 예: 체력 변수를 리플리케이션
UPROPERTY(Replicated)
float Health;

virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(AMyCharacter, Health);
}

ReplicatedUsing / OnRep 사용

  • UPROPERTY(ReplicatedUsing=OnRep_Health)처럼 선언하면,
  • 클라이언트에서 해당 변수가 갱신될 때 자동으로 OnRep_Health()가 호출되어 추가 로직(예: UI 갱신)을 실행
  • 서버에는 OnRep 함수가 자동 호출되지 않으므로, 서버도 같은 로직이 필요하다면 별도 호출이 필요
// 체력 OnRep 예시
UPROPERTY(ReplicatedUsing=OnRep_Health)
float Health;

UFUNCTION()
void OnRep_Health();

// cpp
void AMyCharacter::OnRep_Health()
{
    // 클라이언트 측 체력 UI 갱신 등
    UpdateHealthUI(Health);
    if (Health <= 0.f) { PlayDeathAnimation(); }
}

Replication C++ 예시

// MyCharacter.h
UCLASS()
class AMyCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    AMyCharacter();

    UPROPERTY(ReplicatedUsing=OnRep_Health)
    float Health;

    UFUNCTION()
    void OnRep_Health();

    void TakeDamage(float DamageAmount);

protected:
    // 리플리케이션용 함수
    virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const override;

private:
    UPROPERTY(EditDefaultsOnly)
    float MaxHealth;
};

// MyCharacter.cpp
#include "MyCharacter.h"
#include "Net/UnrealNetwork.h"

AMyCharacter::AMyCharacter()
{
    bReplicates = true;
    bReplicateMovement = true;

    Health = 100.f;
    MaxHealth = 100.f;
}

void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(AMyCharacter, Health);
}

void AMyCharacter::TakeDamage(float DamageAmount)
{
    if (!HasAuthority()) return;

    Health = FMath::Clamp(Health - DamageAmount, 0.f, MaxHealth);
    if (Health <= 0.f)
    {
        // 서버측 사망 처리
    }

    // 클라이언트 OnRep_Health() 호출 유도
    OnRep_Health(); // (서버에서 시각 효과도 필요하면 수동 호출)
}

void AMyCharacter::OnRep_Health()
{
    UpdateHealthUI(Health);
    if (Health <= 0.f)
    {
        PlayDeathAnimation();
    }
}


3. 최적화 고려 사항

신뢰성 수준(Reliable/Unreliable) 분배

  • 중요 이벤트(사망, 아이템 획득 등)는 Reliable
  • 빈번하고 손실 가능(애니·이펙트 등)은 Unreliable
  • Reliable RPC를 과도하게 호출하면 재전송 대기로 인해 지연이 커지고, 최악의 경우 연결 종료 위험이 있음

Replication 빈도 및 조건 제어

  • NetUpdateFrequency: 초당 몇 번 Actor 상태를 체크/전송할지 설정 가능
  • 조건부 복제: DOREPLIFETIME_CONDITION(COND_OwnerOnly, COND_SkipOwner 등)을 사용해 특정 대상에게만 전송
  • 최소 필요 빈도로 전송하고, 불필요한 클라이언트에게는 상태를 안 보내도록 설계하면 대역폭 절감

Net Dormancy와 Relevancy

  • Dormancy: 더 이상 변경되지 않는 Actor를 휴면(Dormant) 상태로 전환해 네트워크 업데이트를 중지
  • 상태가 다시 변하면 FlushNetDormancy 등으로 깨워 전송 재개
  • Relevancy: Player와 너무 멀리 떨어진 Actor의 데이터를 보내지 않도록 관리 (NetCullDistanceSquared 등)

멀티캐스트(RPC) 최소화

  • NetMulticast RPC는 서버 → 모든 클라이언트 전송이므로, 플레이어 수가 많아질수록 트래픽 부담이 커짐
  • 꼭 필요한 전역 이벤트만 멀티캐스트하고, 일부 플레이어에게만 필요한 경우 개별 Client RPC조건부 Replication 사용


4. 보안 고려 사항

서버 권위 모델

  • 클라이언트는 신뢰할 수 없으므로, 중요 게임 로직(체력·위치·점수 등)은 반드시 서버에서 결정
  • 클라이언트에서 변수를 조작해도 서버가 승인하지 않으면 무의미

RPC WithValidation

  • UFUNCTION(Server, WithValidation)을 사용하면 _Implementation_Validate 함수를 구현할 수 있음
  • _Validate에서 false를 반환하면 엔진이 해당 클라이언트를 강제로 접속 종료 처리
  • 부정 조작(예: 말도 안 되는 데미지 등)을 감지해 차단 가능
UFUNCTION(Server, Reliable, WithValidation)
void ServerHeal(int32 HealAmount);

bool AMyCharacter::ServerHeal_Validate(int32 HealAmount)
{
    if (HealAmount < 0 || HealAmount > 100) // 예시 조건
        return false;
    return true;
}

void AMyCharacter::ServerHeal_Implementation(int32 HealAmount)
{
    Health = FMath::Clamp(Health + HealAmount, 0.f, MaxHealth);
    OnRep_Health();
}

Owner·Role 검사 및 서버 측 논리 검증

  • Owning Connection: 클라이언트가 소유하지 않은 Actor에 Server RPC를 호출하면 엔진에서 무시
  • 게임 로직에서도 HasAuthority()나 적절한 Role 검사를 통해 보안 이중화
  • 서버는 항상 “클라이언트 입력은 의심스러운 것”으로 보고, 논리 검증을 거쳐 상태를 확정


5. 네트워크 디버깅 & 프로파일링

Stat Net

  • 콘솔에서 stat net 입력 시 현재 송수신 바이트/패킷 수, 채널 정보, 대기열 등을 실시간 확인
  • 대역폭 사용량이 언제 급증하는지 모니터링 가능

Network Profiler & Unreal Insights

  • Network Profiler
    • net.StartNetworkProfiler 명령어로 프로파일링 시작
    • 세션 종료 후 .nprof 파일 분석: 어떤 Actor/변수가 얼마나 트래픽을 차지했는지 확인
  • Unreal Insights
    • UE 4.23+부터 도입
    • Window -> Developer Tools -> Unreal Insights에서 Networking 관련 데이터를 시각적으로 확인
    • RPC 호출 타이밍, Replication 트래픽 분포 등을 확인 가능

패킷 손실·지연 시뮬레이션

  • Net PktLoss=X, Net PktLag=Y 같은 콘솔 명령으로 인위적인 손실/지연 적용
  • 클라이언트 예측, 서버 보정이 잘 동작하는지, Unreliable 패킷이 유실돼도 큰 문제가 없는지 테스트 가능

로그와 경고 확인

  • “No owning connection for actor” 같은 경고 로그는 RPC가 잘못 호출된 상황 (Actor 소유권 문제)
  • LogNet 카테고리를 Verbose 이상으로 설정해 네트워크 관련 로그를 자세히 모니터링 가능


6. 결론

  • RPC함수 호출을 통해 이벤트나 요청을 전달하고, Replication상태 동기화를 자동화하는 메커니즘입니다.
  • 멀티플레이어 시스템에서는 “서버 권위”가 핵심이며, 클라이언트는 반드시 서버를 통해 중요한 로직을 처리해야 합니다.
  • RPC/Replication 사용 시에는 빈도·신뢰성·조건 등을 고려하여 최적화하고, 적절한 보안 기법(Validation, 권한 검사)을 적용해야 합니다.
  • stat net, Network Profiler, Unreal Insights 등을 활용해 실시간 트래픽을 모니터링하고, 병목을 조정해나가면 보다 안정적인 멀티플레이어 경험을 제공할 수 있습니다.


7. 참고 자료

위 문서는 UE5 C++ 멀티플레이어 개발에서 RPC와 리플리케이션을 종합적으로 다룬 가이드입니다.  
안정적이고 확장성 높은 네트워크 구조를 설계하는 데 참고하시기 바랍니다.

+ Recent posts