RPC 구현
Remote Procedure Calls로 네트워크 함수 호출
RPC 개요
원격 프로시저 호출의 이해
RPC(Remote Procedure Call)는 네트워크를 통해 원격으로 함수를 호출하는 메커니즘입니다. Property Replication이 "상태"를 동기화한다면, RPC는 "이벤트"를 전달합니다.
Server RPC
호출: 클라이언트
실행: 서버
플레이어 입력, 요청 처리에 사용
Client RPC
호출: 서버
실행: 소유 클라이언트
개인 알림, UI 업데이트에 사용
NetMulticast RPC
호출: 서버
실행: 모든 클라이언트 + 서버
이펙트, 사운드 재생에 사용
| RPC 유형 | Reliable | Unreliable |
|---|---|---|
| Server | 중요한 입력 (발사, 구매) | 빈번한 업데이트 (조준 방향) |
| Client | 중요한 알림 (사망, 보상) | 피드백 (히트마커) |
| NetMulticast | 중요한 이펙트 (사망) | 장식적 이펙트 (발자국) |
Server RPC 구현
클라이언트 → 서버 함수 호출
UCLASS()
class MYGAME_API ACombatCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Reliable + WithValidation (권장)
UFUNCTION(Server, Reliable, WithValidation)
void Server_Fire(FVector TargetLocation);
// Unreliable (빈번한 호출용)
UFUNCTION(Server, Unreliable)
void Server_UpdateAimRotation(FRotator AimRotation);
private:
UPROPERTY(Replicated)
int32 CurrentAmmo;
static const int32 MAX_FIRE_DISTANCE = 10000;
};
// Validate 함수: false 반환 시 클라이언트 연결 해제!
bool ACombatCharacter::Server_Fire_Validate(FVector TargetLocation)
{
// 거리 검증 - 비정상적으로 먼 타겟 차단
float Distance = FVector::Dist(GetActorLocation(), TargetLocation);
if (Distance > MAX_FIRE_DISTANCE)
{
UE_LOG(LogTemp, Warning,
TEXT("Invalid fire distance: %f"), Distance);
return false; // 치터 의심 → 연결 해제
}
// 탄약 검증
if (CurrentAmmo <= 0)
{
return false;
}
return true;
}
// Implementation 함수: 실제 서버 로직
void ACombatCharacter::Server_Fire_Implementation(FVector TargetLocation)
{
// 서버에서만 실행되는 코드
CurrentAmmo--;
// 히트 검사
FHitResult HitResult;
FVector Start = GetActorLocation();
if (GetWorld()->LineTraceSingleByChannel(
HitResult, Start, TargetLocation, ECC_Pawn))
{
if (AActor* HitActor = HitResult.GetActor())
{
float Damage = 25.0f;
UGameplayStatics::ApplyDamage(
HitActor, Damage, GetController(), this, nullptr);
// 히트 피드백을 요청한 클라이언트에게 전송
Client_PlayHitMarker();
}
}
// 모든 클라이언트에게 총구 이펙트 표시
Multicast_PlayMuzzleFlash();
}
Server RPC에는 반드시 WithValidation을 추가하세요. Validate에서 false를 반환하면 해당 클라이언트 연결이 해제되어 치터를 방지할 수 있습니다.
Client & Multicast RPC
서버에서 클라이언트로 함수 호출
// Client RPC - 서버 → 특정 클라이언트
UFUNCTION(Client, Reliable)
void Client_ShowDamageNumber(float DamageAmount, FVector WorldLocation);
UFUNCTION(Client, Unreliable)
void Client_PlayHitMarker();
// NetMulticast RPC - 서버 → 모든 클라이언트
UFUNCTION(NetMulticast, Reliable)
void Multicast_PlayDeathEffect();
UFUNCTION(NetMulticast, Unreliable)
void Multicast_PlayFootstepSound(FVector Location);
void ACombatCharacter::Client_ShowDamageNumber_Implementation(
float DamageAmount, FVector WorldLocation)
{
// 이 클라이언트에서만 실행
if (APlayerController* PC = Cast<APlayerController>(GetController()))
{
// 데미지 숫자 UI 위젯 생성 및 표시
// ...
}
}
void ACombatCharacter::Client_PlayHitMarker_Implementation()
{
// 히트마커 사운드 및 UI 피드백
// Unreliable이므로 누락될 수 있음 (괜찮음)
PlayHitMarkerSound();
}
void ACombatCharacter::Multicast_PlayDeathEffect_Implementation()
{
// 서버와 모든 클라이언트에서 실행
UGameplayStatics::SpawnEmitterAtLocation(
GetWorld(),
DeathParticle,
GetActorLocation()
);
}
void ACombatCharacter::Multicast_PlayFootstepSound_Implementation(
FVector Location)
{
// Unreliable - 누락되어도 게임플레이에 영향 없음
UGameplayStatics::PlaySoundAtLocation(
GetWorld(),
FootstepSound,
Location
);
}
Best Practices
RPC 사용 시 주의사항
나쁜 예시 1: Tick에서 Reliable 호출
// 절대 하지 마세요! 네트워크 버퍼 과부하!
void ACombatCharacter::Tick(float DeltaTime)
{
Server_UpdatePosition(GetActorLocation()); // Reliable
}
나쁜 예시 2: Validation 없는 Server RPC
// 치터가 악용할 수 있음!
UFUNCTION(Server, Reliable) // WithValidation 누락
void Server_SetHealth(float NewHealth); // 위험!
나쁜 예시 3: BeginPlay에서 RPC 호출
// 작동하지 않을 수 있음!
void ACombatCharacter::BeginPlay()
{
Super::BeginPlay();
Server_Initialize(); // 연결이 아직 안 됐을 수 있음
}
함수 이름에 RPC 유형을 접두사로 붙이면 코드 가독성이 향상됩니다:
Server_Attack(), Client_Notify(), Multicast_Effect()
핵심 요약
- Server RPC — 클라이언트 입력을 서버로 전달, WithValidation 필수
- Client RPC — 서버에서 특정 클라이언트에게 알림
- NetMulticast RPC — 모든 클라이언트에 이펙트/사운드 동기화
- Reliable vs Unreliable — 중요도에 따라 선택, Tick에서 Reliable 금지
- _Implementation — RPC 함수의 실제 로직은 이 접미사가 붙은 함수에 작성
도전 과제
배운 내용을 직접 실습해보세요
UFUNCTION(Server, Reliable)로 ServerRequestAttack()을 구현하세요. 클라이언트에서 공격 입력 시 서버에 RPC를 보내고, 서버에서 데미지를 계산한 후 결과를 Multicast로 전파하세요.
UFUNCTION(Client, Reliable)로 ClientReceiveDamage()를 구현하여, 서버가 특정 클라이언트에게 피격 알림을 보내세요. 화면 흔들림, 피격 사운드 등 로컬 피드백을 처리하세요.
중요한 게임플레이 이벤트(아이템 획득, 퀘스트 완료)는 Reliable로, 빈번한 시각 효과(발자국, 파티클)는 Unreliable+NetMulticast로 구현하세요. 네트워크 대역폭 사용량을 비교하세요.