복제(Replication) 시스템 이해
bReplicates·DOREPLIFETIME·OnRep를 사용해 액터 생성과 중요 변수의 서버 상태를 복제합니다.
이전 절에서는 언리얼 엔진 네트워킹의 기본 개념, 클라이언트-서버 모델, 복제(Replication)의 개요를 살펴보았습니다.
이번 절에서는 서버와 클라이언트 간 게임 상태를 동기화하는 복제(Replication) 시스템을 더 자세히 다룹니다.
이번 절에서는 액터 복제, 변수 복제, RPC(원격 프로시저 호출) 등 복제 시스템의 다양한 구성 요소를 심층적으로 분석하고, 각 요소가 어떻게 작동하는지 구체적인 예시와 함께 알아보겠습니다.
핵심 흐름: 서버 권한에서 상태를 확정하고 DOREPLIFETIME 규칙으로 복제 대상을 선별한 뒤, 클라이언트에서 OnRep_/RPC로 반응을 분리합니다.
서버가 액터의 존재와 상태를 확정하고, 클라이언트는 전달받은 값에 맞춰 화면·사운드·UI 반응을 수행합니다.
- 서버 권한 상태가 클라이언트에 도착하는 경로
상태 변경은 서버가 확정하고, 소유 클라이언트의 입력만 Server RPC 요청으로 받습니다. 2 서버에서 스폰·파괴된 `bReplicates` 액터가 클라이언트 월드에도 생성·제거됩니다. 3 `UPROPERTY(Replicated)`와 `DOREPLIFETIME`으로 서버 상태 중 전파할 값만 명시합니다. 4 새 값이 도착하면 `OnRep_`에서 이펙트·사운드·UI를 갱신하고, 서버 권한 값과 화면 표현을 분리합니다.
- 권한·소유권 확인
상태 변경은 서버가 확정하고, 소유 클라이언트의 입력만 Server RPC 요청으로 받습니다.
- 액터 존재 동기화
서버에서 스폰·파괴된 `bReplicates` 액터가 클라이언트 월드에도 생성·제거됩니다.
- 변수 복제 등록
`UPROPERTY(Replicated)`와 `DOREPLIFETIME`으로 서버 상태 중 전파할 값만 명시합니다.
- 클라이언트 반응 분리
새 값이 도착하면 `OnRep_`에서 이펙트·사운드·UI를 갱신하고, 서버 권한 값과 화면 표현을 분리합니다.
- 복제 누락 디버그 지점
위치·회전·스케일처럼 이동 컴포넌트가 관리하는 값은 별도 활성화가 필요합니다. 클라이언트에 새 값이 도착했을 때 `OnRep_` 함수로 표현 로직을 분리합니다. 받을 대상을 좁혀 불필요한 네트워크 전송을 줄입니다.
액터 복제 (Actor Replication)
언리얼 엔진에서 네트워크를 통해 게임 월드의 객체를 동기화하는 가장 기본적인 단위는 액터(Actor)입니다.
액터 복제는 서버에 존재하는 액터의 인스턴스를 네트워크를 통해 클라이언트에 생성하고, 해당 액터의 중요한 상태를 지속적으로 동기화하는 과정입니다.
bReplicates와 SetReplicates(true)
액터가 복제되려면, 해당 액터 클래스의 생성자에서 bReplicates 속성을 true로 설정하거나 런타임에 SetReplicates(true)를 호출해야 합니다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyReplicatedActor.generated.h"
UCLASS()
class MYPROJECT_API AMyReplicatedActor : public AActor
{
GENERATED_BODY()
public:
AMyReplicatedActor();
};#include "MyReplicatedActor.h"
AMyReplicatedActor::AMyReplicatedActor()
{
// 중요: 이 액터가 네트워크를 통해 복제될 것임을 명시
bReplicates = true; // 액터의 움직임(위치, 회전, 스케일)도 복제될 것임을 명시
// UMovementComponent가 액터에 있어야 효과가 있습니다.
SetReplicatesMovement(true); }bReplicates = true;: 이 액터의 인스턴스가 서버에 생성되면, 언리얼 엔진은 자동으로 네트워크를 통해 연결된 모든 클라이언트에 이 액터의 사본을 생성하고 동기화하기 시작합니다.SetReplicatesMovement(true);: 이 함수를 호출하면, 액터의 위치, 회전, 스케일 정보가 네트워크를 통해 자동으로 복제됩니다.UMovementComponent를 사용하는 캐릭터나 폰의 경우 이 속성이 매우 중요합니다.
액터의 생성 및 파괴 복제
- 생성: 서버에서
GetWorld()->SpawnActor<AMyActor>(...)를 통해 액터가 생성되면,bReplicates가true인 경우 해당 액터는 모든 클라이언트에도 자동으로 스폰됩니다. - 파괴: 서버에서
Destroy()가 호출되어 액터가 파괴되면, 모든 클라이언트에서도 해당 액터의 사본이 파괴됩니다.
변수 복제 (Variable Replication)
액터 자체의 존재 여부 외에도, 액터 내부의 특정 변수들의 값도 서버와 클라이언트 간에 동기화되어야 합니다.
이는 UPROPERTY() 매크로와 GetLifetimeReplicatedProps() 함수를 통해 관리됩니다.
DOREPLIFETIME 매크로
변수를 복제하려면 UPROPERTY()에 Replicated 지정자를 추가하고, 액터 클래스의 GetLifetimeReplicatedProps() 함수 내에서 DOREPLIFETIME() 매크로를 사용해야 합니다.
#pragma once
// ...
#include "GameFramework/Actor.h"
#include "MyReplicatedActor.generated.h"
UCLASS()
class MYPROJECT_API AMyReplicatedActor : public AActor
{
GENERATED_BODY()
public:
AMyReplicatedActor();
protected:
// 서버에서만 값을 변경하고 클라이언트로 복제할 변수
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Replication")
int32 ReplicatedInteger;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};#include "MyReplicatedActor.h"
#include "Net/UnrealNetwork.h" // DOREPLIFETIME 매크로를 위해 반드시 포함
AMyReplicatedActor::AMyReplicatedActor()
{
bReplicates = true;
SetReplicatesMovement(true);
ReplicatedInteger = 0; // 초기값 설정
}
void AMyReplicatedActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyReplicatedActor, ReplicatedInteger); }UPROPERTY(Replicated): 이 변수가 네트워크를 통해 복제될 것임을 언리얼 엔진에 알립니다.GetLifetimeReplicatedProps(): 이 함수를 오버라이드하여DOREPLIFETIME매크로를 사용함으로써, 언리얼 엔진의 네트워크 코드가 어떤 변수를 복제해야 하는지 알게 됩니다.
OnRep_ 노티파이 함수 (ReplicatedUsing)
서버 값 변경, 수명 등록, 조건 필터, 클라이언트 수신, OnRep 실행을 한 프레임처럼 착각하지 않는다.
| 순서 | 엔진에서 일어나는 일 | 확인할 코드 | 흔한 오해 |
|---|---|---|---|
| 01 서버 권한 값 변경 | Authority가 있는 서버 인스턴스에서 복제 변수를 바꾼다. | HasAuthority 분기와 실제 값 변경 위치 | 클라이언트 로컬 변경은 기준값이 아니다. |
| 02 수명 복제 등록 | GetLifetimeReplicatedProps가 대상 필드를 등록한다. | DOREPLIFETIME 설정과 조건 | UPROPERTY만으로 항상 전송되지는 않는다. |
| 03 조건 대상 필터 | Owner Only, Skip Owner 등이 클라이언트별 전송 여부를 정한다. | 복제 조건과 소유자 지정 | 모든 플레이어가 같은 값을 받는다고 보지 않는다. |
| 04 수신 로컬 반영 | 클라이언트가 패킷을 받아 변수 값을 갱신한다. | 초기값과 이전 값 비교 | 같은 값이면 기대한 알림이 없을 수 있다. |
| 05 반응 OnRep 실행 | UI, 이펙트, 캐시 갱신을 RepNotify 함수에서 처리한다. | OnRep 내부의 표시 전용 로직 | 서버 게임 규칙 실행 위치로 쓰지 않는다. |
특정 변수가 클라이언트로 복제되어 값이 변경될 때, 클라이언트에서 특정 로직을 실행하고 싶을 때 ReplicatedUsing 지정자를 사용합니다.
이는 주로 시각적 효과, 사운드 재생, UI 업데이트 등 클라이언트에서만 필요한 처리에 사용됩니다.
#pragma once
// ...
UCLASS()
class MYPROJECT_API AMyReplicatedActor : public AActor
{
GENERATED_BODY()
public:
// ...
protected:
// 값이 변경될 때마다 클라이언트에서 OnRep_ReplicatedFloat 함수를 호출할 변수
UPROPERTY(ReplicatedUsing = OnRep_ReplicatedFloat, BlueprintReadOnly, Category = "Replication")
float ReplicatedFloat;
// OnRep_ReplicatedFloat 함수 선언 (UFUNCTION으로 선언되어야 함)
UFUNCTION()
void OnRep_ReplicatedFloat();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
public:
// 서버에서 호출하여 복제된 변수들을 업데이트하는 함수
UFUNCTION(BlueprintCallable, Category = "Replication")
void Server_SetReplicatedFloat(float NewFloat);
};#include "MyReplicatedActor.h"
#include "Net/UnrealNetwork.h"
// ...
void AMyReplicatedActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyReplicatedActor, ReplicatedInteger); // 기존
DOREPLIFETIME(AMyReplicatedActor, ReplicatedFloat); // OnRep_ 함수와 연결된 변수 복제
}
void AMyReplicatedActor::OnRep_ReplicatedFloat()
{
// 이 함수는 ReplicatedFloat 변수의 값이 클라이언트로 복제될 때 클라이언트에서 자동으로 호출됩니다.
// 서버에서는 직접 값을 변경하므로 호출되지 않습니다.
UE_LOG(LogTemp, Warning, TEXT("Client: ReplicatedFloat changed to: %f (OnRep_ called)"), ReplicatedFloat);
// 여기에 이펙트 재생, UI 업데이트, 애니메이션 전환 등 클라이언트에서만 필요한 로직을 구현합니다.
}
void AMyReplicatedActor::Server_SetReplicatedFloat(float NewFloat)
{
if (HasAuthority()) // 서버에서만 실행
{
ReplicatedFloat = NewFloat; // 이 값을 변경하면 클라이언트로 복제되고 OnRep_ReplicatedFloat가 호출됩니다.
UE_LOG(LogTemp, Warning, TEXT("Server: ReplicatedFloat set to: %f"), ReplicatedFloat);
// 서버에서도 동일한 로직을 실행하고 싶다면 OnRep_ReplicatedFloat()를 직접 호출할 수 있습니다.
// OnRep_ReplicatedFloat(); // 서버에서도 강제로 OnRep_ 함수 호출
}
}UPROPERTY(ReplicatedUsing = FunctionName): 이 변수의 값이 복제되어 클라이언트에서 변경될 때, 지정된FunctionName이 호출됩니다. 함수는 반드시UFUNCTION()으로 선언되어야 하며,OnRep_접두사를 사용하는 것이 관례입니다.
조건부 복제 (DOREPLIFETIME_COND)
모든 클라이언트에게 특정 변수를 복제할 필요가 없거나, 특정 조건에서만 복제하고 싶을 때 DOREPLIFETIME_COND 매크로를 사용합니다.
#pragma once
// ...
UCLASS()
class MYPROJECT_API AMyReplicatedActor : public AActor
{
GENERATED_BODY()
public:
// ...
protected:
// 소유자에게만 복제될 변수 (예: 인벤토리)
UPROPERTY(Replicated, Category = "Replication")
TArray<FString> PlayerInventory;
// 비소유자에게만 복제될 변수 (예: 공개된 상태 정보)
// UPROPERTY(Replicated, Category = "Replication")
// int32 PublicStateValue;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};#include "MyReplicatedActor.h"
#include "Net/UnrealNetwork.h"
// ...
void AMyReplicatedActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// COND_OwnerOnly: 이 변수는 액터의 소유자에게만 복제됩니다.
DOREPLIFETIME_COND(AMyReplicatedActor, PlayerInventory, COND_OwnerOnly); // COND_SkipOwner: 이 변수는 소유자를 제외한 모든 클라이언트에게 복제됩니다.
// DOREPLIFETIME_COND(AMyReplicatedActor, PublicStateValue, COND_SkipOwner);
}COND_None: 항상 복제 (기본값)COND_InitialOnly: 액터가 처음 스폰될 때 한 번만 복제COND_OwnerOnly: 액터의 소유자에게만 복제COND_SkipOwner: 소유자를 제외한 모든 클라이언트에게 복제COND_SimulatedOnly: 서버 시뮬레이션 클라이언트(Non-Owner)에게만 복제COND_AutonomousOnly: 자율 프록시(Owner)에게만 복제- 그 외 다양한 조건들이 있습니다.
RPC (Remote Procedure Call)
RPC는 함수 지정자만 붙인다고 어디서나 실행되지 않는다. 호출 위치, 소유권, Net Mode, Actor 복제 여부가 함께 맞아야 한다.
- 클라이언트가 소유한 Actor에서 호출
Server RPC 클라이언트가 소유한 Actor에서 호출 소유권이 없으면 서버에 도착하지 않거나 무시될 수 있다. Owner 확인
- 서버가 특정 소유자에게 결과 전달
Client RPC 서버가 특정 소유자에게 결과 전달 PlayerController나 소유 Pawn처럼 대상 연결이 분명해야 한다. owning connection
- 서버 호출이 관련 클라이언트에 전파
NetMulticast 서버 호출이 관련 클라이언트에 전파 장기 상태보다 순간 효과, 소리, 이펙트에 적합하다. relevancy
- 중요 이벤트에 제한적으로 사용
Reliable 중요 이벤트에 제한적으로 사용 남발하면 네트워크 큐가 밀려 실시간성이 떨어질 수 있다. 필수 이벤트만
RPC는 네트워크를 통해 함수 호출을 전송하여 서버 또는 클라이언트에서 특정 로직을 실행하도록 하는 메커니즘입니다.
변수 복제로는 즉각적인 액션이나 이벤트 처리가 어렵기 때문에 RPC가 사용됩니다.
RPC 지정자
UFUNCTION() 매크로에 다음과 같은 지정자를 사용하여 RPC를 선언합니다.
Server: 클라이언트가 서버로 함수 실행을 요청할 때 사용. 서버에서만 실행됩니다.Reliable/Unreliable: 전송 신뢰성.Reliable은 전송을 보장하지만 오버헤드가 크고,Unreliable은 빠르지만 패킷 손실 가능성이 있습니다.WithValidation:ServerRPC에만 사용되며, 클라이언트가 보낸 인자가 서버에서 유효한지 검증하는_Validate함수를 강제합니다. 치트 방지에 필수적입니다.Client: 서버가 특정 클라이언트(해당 액터의 소유자)로 함수 실행을 요청할 때 사용. 해당 클라이언트에서만 실행됩니다.NetMulticast: 서버가 모든 클라이언트(서버 자신 포함)로 함수 실행을 요청할 때 사용. 모든 클라이언트에서 실행됩니다.
#pragma once
// ...
UCLASS()
class MYPROJECT_API AMyReplicatedActor : public AActor
{
GENERATED_BODY()
public:
// ...
protected:
// 1. 클라이언트가 서버로 요청하는 RPC
UFUNCTION(Server, Reliable, WithValidation)
void Server_FireWeapon(FVector Location, FRotator Direction);
// _Validate 함수 (선언만)
bool Server_FireWeapon_Validate(FVector Location, FRotator Direction);
// _Implementation 함수 (선언만)
void Server_FireWeapon_Implementation(FVector Location, FRotator Direction);
// 2. 서버가 특정 클라이언트에게 보내는 RPC
UFUNCTION(Client, Reliable)
void Client_ReceiveChatMessage(const FString& SenderName, const FString& Message);
// 3. 서버가 모든 클라이언트에게 보내는 RPC
UFUNCTION(NetMulticast, Unreliable) // 애니메이션 재생 등 비중요, 빈번한 업데이트
void Multicast_PlayParticleEffect(FVector EffectLocation);
};#include "MyReplicatedActor.h"
// ...
// 1. Server RPC 구현: 클라이언트가 서버로 무기 발사 요청
bool AMyReplicatedActor::Server_FireWeapon_Validate(FVector Location, FRotator Direction)
{
// 여기서 클라이언트가 보낸 데이터가 유효한지 검사합니다.
// 예: Location이 너무 멀리 떨어져 있거나, 부정확한 값이 아닌지 등
// 치트 방지에 매우 중요합니다.
return true;
}
void AMyReplicatedActor::Server_FireWeapon_Implementation(FVector Location, FRotator Direction)
{
// 이 코드는 서버에서만 실행됩니다.
UE_LOG(LogTemp, Warning, TEXT("Server: Processing weapon fire from client at %s, direction %s"), *Location.ToString(), *Direction.ToString());
// 실제 발사 로직 (충돌 감지, 데미지 적용, 탄약 감소 등)을 여기서 처리합니다.
// 모든 클라이언트에게 발사 효과를 복제 (NetMulticast 호출)
Multicast_PlayParticleEffect(Location);
}
void AMyReplicatedActor::Client_ReceiveChatMessage_Implementation(const FString& SenderName, const FString& Message)
{
// 이 코드는 해당 액터를 소유한 클라이언트에서만 실행됩니다.
// HasAuthority()는 false를 반환합니다.
if (!HasAuthority())
{
UE_LOG(LogTemp, Warning, TEXT("Client: Chat Message from %s: %s"), *SenderName, *Message);
// 여기에 UI에 메시지를 표시하는 로직을 구현합니다.
}
}
void AMyReplicatedActor::Multicast_PlayParticleEffect_Implementation(FVector EffectLocation)
{
// 이 코드는 서버를 포함하여 모든 연결된 클라이언트에서 실행됩니다.
UE_LOG(LogTemp, Warning, TEXT("All: Playing particle effect at %s"), *EffectLocation.ToString());
// 여기에 파티클 시스템 생성, 사운드 재생 등 시각적/청각적 효과를 구현합니다.
}ServerRPC: 클라이언트에서만 호출할 수 있습니다. 서버에서Server_FireWeapon()을 호출하면 아무 일도 일어나지 않습니다.Client/NetMulticastRPC: 서버에서만 호출할 수 있습니다. 클라이언트에서 이들을 호출하면 아무 일도 일어나지 않습니다._Validate함수:WithValidation이 붙은ServerRPC는 반드시_Validate함수를 구현해야 합니다. 이 함수가false를 반환하면_Implementation함수는 실행되지 않고, 해당 클라이언트는 서버에서 연결이 끊어질 수 있습니다._Implementation함수: RPC가 실행될 실제 로직을 담는 함수입니다.
오래 유지되는 게임 상태는 복제 변수로, 순간적인 요청이나 연출은 RPC로 나누면 서버 권한과 클라이언트 표현이 섞이지 않습니다.
- 공유 상태
체력, 점수, 인벤토리처럼 새로 접속한 클라이언트도 알아야 하는 값은 `UPROPERTY(Replicated)`로 둡니다. DOREPLIFETIME COND_OwnerOnly
- 값 변화 후처리
복제된 값이 도착한 순간 UI, 사운드, 애니메이션을 갱신해야 한다면 `ReplicatedUsing`으로 `OnRep_`를 연결합니다. RepNotify Client visual
- 클라이언트 요청
발사, 상호작용, 구매처럼 입력에서 시작되는 행동은 Server RPC로 보내고 서버의 `_Validate`와 권한 로직에서 확정합니다. Server RPC WithValidation
- 단발 연출
파티클, 피격음, 채팅 표시처럼 상태로 오래 보관할 필요가 없는 이벤트는 Client 또는 NetMulticast RPC로 보냅니다. Client RPC NetMulticast
권한/소유권 판정 순서 (UE5 기준)
네트워크 로직은 먼저 서버 권한을 확인하고, 다음으로 소유 클라이언트인지 판단한 뒤 RPC 방향을 정해야 복제 버그가 줄어듭니다.
- HasAuthority
현재 실행 주체가 서버 권한을 가진지 봅니다.
- IsLocallyControlled
Pawn이 로컬 입력 주체인지 확인합니다.
- Owner
Actor의 소유 클라이언트를 기준으로 RPC 가능성을 봅니다.
- Server RPC
클라이언트 요청을 서버 검증 로직으로 보냅니다.
- Client RPC
서버가 특정 소유자에게 결과를 전달합니다.
복제 버그를 줄이려면 로직 시작점에서 아래 순서를 고정하는 것이 좋습니다.
- 권한 판단: 상태 변경 로직은
HasAuthority()블록에서만 실행합니다. - 소유권 판단: 입력 기반 요청은
IsLocallyControlled()또는GetOwner()를 함께 확인합니다. - 전파 방식 선택: 상태값은
Replicated변수, 단발 이벤트는 RPC로 분리합니다. - 클라이언트 반영: 시각/UI 후처리는
OnRep_또는Client RPC에서 처리합니다.
컴포넌트 복제 (Component Replication)
액터뿐만 아니라 특정 컴포넌트도 복제될 수 있습니다.
UActorComponent를 상속받는 컴포넌트가 복제되려면:
컴포넌트의 생성자에서 bReplicates = true;로 설정합니다.
액터의 GetLifetimeReplicatedProps()에서 해당 컴포넌트의 변수를 DOREPLIFETIME으로 선언합니다.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "MyReplicatedComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class MYPROJECT_API UMyReplicatedComponent : public UActorComponent
{
GENERATED_BODY()
public:
UMyReplicatedComponent();
protected:
virtual void BeginPlay() override;
UPROPERTY(Replicated)
int32 ComponentSpecificValue;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
};#include "MyReplicatedComponent.h"
#include "Net/UnrealNetwork.h"
UMyReplicatedComponent::UMyReplicatedComponent()
{
bReplicates = true; // 컴포넌트 복제 활성화
ComponentSpecificValue = 0;
}
void UMyReplicatedComponent::BeginPlay()
{
Super::BeginPlay();
}
void UMyReplicatedComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UMyReplicatedComponent, ComponentSpecificValue);
}- 컴포넌트 자체는
DOREPLIFETIME을 사용하지 않습니다. 컴포넌트를 소유한 액터가 복제되면 컴포넌트도 함께 복제됩니다.bReplicates는 컴포넌트 내의 변수들이 복제될 수 있도록 설정하는 역할을 합니다.
컴포넌트 복제는 액터의 생명주기와 함께 움직이므로, 존재 생성과 상태 등록 책임을 분리해 보면 누락 지점이 빨리 보입니다.
변수에 Replicated를 붙이는 것만으로 끝나지 않고 어떤 액터가 복제되며 누구에게 언제 보내는지가 함께 정해져야 합니다.
- Actor Replication
SetReplicates와 NetUpdateFrequency로 액터가 네트워크 대상인지와 갱신 빈도를 정합니다.
- DOREPLIFETIME 복제할 UPROPERTY
명시적으로 등록해야 서버 값이 클라이언트로 전달됩니다.
- Condition OwnerOnly
SkipOwner 같은 조건으로 실제 전송 대상을 좁혀 대역폭을 아낍니다.
- OnRep ReplicatedUsing 함수
새 값을 받은 클라이언트의 표시 상태를 갱신합니다.
GetLocalRole()과 HasAuthority()
네트워크 환경에서 액터의 현재 상태를 파악하는 데 중요한 두 가지 속성이 있습니다.
GetLocalRole(): 액터의 현재 인스턴스가 네트워크상에서 어떤 권한(Authority)을 가지는지 확인합니다.ROLE_Authority: 이 액터의 인스턴스가 서버(Server)에서 실행되고 있음을 의미합니다. 이 액터의 모든 변수와 로직은 진실된 상태를 가지고 있습니다.ROLE_AutonomousProxy: 이 액터의 인스턴스가 클라이언트에서 실행되고 있으며, 해당 클라이언트가 이 액터의 소유자(Owner) 임을 의미합니다 (예: 현재 플레이어가 조작하는 캐릭터).ROLE_SimulatedProxy: 이 액터의 인스턴스가 클라이언트에서 실행되고 있으며, 해당 클라이언트가 이 액터의 비소유자임을 의미합니다 (예: 다른 플레이어의 캐릭터).HasAuthority(): 서버 권한 여부를 가장 직접적으로 판정하는 표준 헬퍼입니다.
#include "MyReplicatedActor.h"
void AMyReplicatedActor::BeginPlay()
{
Super::BeginPlay();
if (HasAuthority()) // Role == ROLE_Authority
{
UE_LOG(LogTemp, Warning, TEXT("AMyReplicatedActor: This is the Server (Authority)!"));
}
else if (GetLocalRole() == ENetRole::ROLE_AutonomousProxy)
{
UE_LOG(LogTemp, Warning, TEXT("AMyReplicatedActor: This is the Owner Client (Autonomous Proxy)!"));
}
else if (GetLocalRole() == ENetRole::ROLE_SimulatedProxy)
{
UE_LOG(LogTemp, Warning, TEXT("AMyReplicatedActor: This is a Non-Owner Client (Simulated Proxy)!"));
}
}HasAuthority():GetLocalRole() == ENetRole::ROLE_Authority의 축약형으로, 현재 코드가 서버에서 실행되고 있는지 확인하는 데 가장 일반적으로 사용됩니다. 상태를 바꾸는 로직은 기본적으로HasAuthority()내부에 두고, 클라이언트는 요청(RPC)과 표현(UI/이펙트)에 집중시키는 구조가 안전합니다.
복제 시스템을 디버깅할 때는 액터 생성, 변수 등록, OnRep 반응, RPC 방향을 한 번에 점검하는 루틴이 필요합니다.
값이 안 보일 때는 코드 한 줄보다 네트워크 파이프라인 어느 단계에서 끊겼는지를 먼저 찾습니다.
- 1Actor 복제
spawn Actor 복제 bReplicates와 서버 Spawn 여부를 확인합니다.
- 2변수 등록
props 변수 등록 UPROPERTY와 DOREPLIFETIME 누락을 봅니다.
- 3클라이언트 반응
onrep 클라이언트 반응 UI, 사운드, 이펙트는 OnRep에서 분리합니다.
- 4이벤트 호출
rpc 이벤트 호출 Server/Client/Multicast 실행 위치를 확인합니다.
- 5소유자만 보이는 값
COND_OwnerOnly를 썼다면 다른 클라이언트에서 보이지 않는 것이 정상입니다.
- 6서버 OnRep 기대
서버는 값을 직접 바꾸므로 클라이언트와 같은 OnRep 흐름을 기대하면 안 됩니다.
- 7컴포넌트 변수 누락
컴포넌트 자체 복제와 컴포넌트 내부 변수 등록을 따로 점검합니다.
언리얼 엔진의 복제 시스템은 멀티플레이어 게임의 동기화를 위한 견고한 기반을 제공합니다.
bReplicates를 통한 액터 복제, UPROPERTY(Replicated) 및 DOREPLIFETIME을 통한 변수 복제, ReplicatedUsing을 통한 OnRep_ 함수 활용, 그리고 Server, Client, NetMulticast RPC를 통한 함수 호출 복제는 멀티플레이어 게임의 핵심 구현 요소입니다.
또한, Role 및 HasAuthority()를 이해하여 네트워크 권한을 올바르게 관리하는 것은 버그 없는 효율적인 멀티플레이어 게임을 개발하는 데 매우 중요합니다.