내일배움캠프_언리얼9기

내일배움캠프 71일차 TIL 2026/08/03

begin-play 2026. 8. 3. 20:54

📝 TIL: 언리얼 엔진 멀티플레이어 핵심 원리 및 구현 (숫자야구 게임)

📌 1. 멀티플레이어 개요 & 프레임워크 역할 분담

언리얼 엔진의 멀티플레이어는 데디케이티드/리스닝 서버(Server) - 클라이언트(Client) 구조로 동작하며, 보안과 네트워크 효율을 위해 클래스별 역할이 철저히 분리되어 있다.

  • AGameModeBase (서버 전용)
    • 오직 서버에만 존재하며 클라이언트에는 존재하지 않는다.
    • 정답 생성(GenerateSecretNumber), 승패 판정(JudgeGame), 게임 리셋(ResetGame) 등 핵심 게임 규칙과 보안이 중요한 데이터를 관리한다. (클라이언트가 정답을 훔쳐볼 수 없음)
  • APlayerController (서버 + 해당 로컬 클라이언트)
    • 유저와 게임 세상을 연결하는 통로.
    • IsLocalController() 검사를 통해 로컬 플레이어 전용 UI(채팅창, 알림)를 생성한다.
    • 클라이언트의 요청을 서버로 보내거나(Server RPC), 서버의 응답을 클라이언트로 전달(Client RPC)하는 네트워크 통신 창구 역할을 한다.
  • APlayerState (서버 + 모든 클라이언트)
    • 플레이어 개개인의 상태 정보(이름, 시도 횟수 CurrentGuessCount, 최대 기회 MaxGuessCount 등)를 저장한다.
    • 네트워크 복제(Replication)되어 접속한 모든 플레이어가 서로의 정보를 확인할 수 있다.
  • AGameStateBase (서버 + 모든 클라이언트)
    • 모든 플레이어가 공유하는 전체 게임 세션 상태를 다룬다.
    • 새로운 유저가 접속했을 때 모든 클라이언트에게 알림을 방송(NetMulticast)하는 등에 활용된다.

🌐 2. 네트워크 통신 기법 (RPC & Replication)

① RPC (Remote Procedure Call, 원격 프로시저 호출)

다른 단말기(서버 또는 특정 클라이언트)에서 함수를 실행시키기 위한 네트워크 명령.

  • Server RPC (UFUNCTION(Server, Reliable))
    • 호출: 클라이언트 -> 실행: 서버
    • 클라이언트가 서버에게 상태 변경이나 판정을 요청할 때 사용. (예: ServerRPCPrintChatMessageString)
    • 조건: 호출하는 클라이언트가 소유(Owned)한 액터(PlayerController 등)여야 함.
  • Client RPC (UFUNCTION(Client, Reliable))
    • 호출: 서버 -> 실행: 특정 클라이언트
    • 서버에서 처리된 결과를 해당 클라이언트의 화면에 띄우거나 UI를 업데이트할 때 사용. (예: ClientRPCPrintChatMessageString)
  • NetMulticast RPC (UFUNCTION(NetMulticast, Reliable))
    • 호출: 서버 -> 실행: 서버 + 연결된 모든 클라이언트
    • 접속 알림 메시지처럼 모든 유저에게 일괄 전달해야 하는 상황에 사용. (예: MulticastRPCBroadcastLoginMessage)
  • Reliable 키워드
    • 패킷 손실이 발생하더라도 재전송하여 반드시 도착하도록 보장함. (채팅, 판정 등 중요한 통신에 필수)

② 변수 복제 (Replication)

서버에서 변수 값을 변경하면, 클라이언트에 해당 변수 값이 자동으로 동기화되는 기능.

C++
 
// 1. 헤더 파일 선언
UPROPERTY(Replicated)
int32 CurrentGuessCount;

// 2. CPP 파일 등록
void AMyPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(ThisClass, CurrentGuessCount);
}

🔄 3. 전체 데이터 흐름 (숫자야구 입력 ~ 결과 출력)

  1. [UI] UChatInputWidget: 플레이어가 입력창에 숫자를 치고 엔터를 누르면 OnTextCommitted 이벤트가 감지되어 컨트롤러의 SetChatMessageString() 호출.
  2. [Client PC] AMyPlayerController: PlayerState에서 작성자 정보(Player1(0/3))를 가져와 문장을 만든 뒤, ServerRPCPrintChatMessageString (Server RPC)을 통해 서버로 발송.
  3. [Server] AMyGameModeBase:
    • 서버가 RPC를 수신하여 입력받은 문자열 검증 (IsGuessNumberString).
    • PlayerState 내 시도 횟수 차감 및 정답 판정 (JudgeResult).
    • 승패/무승부 여부 확인 (JudgeGame).
    • ClientRPCPrintChatMessageString (Client RPC)으로 판정 결과를 클라이언트들에게 전파.
  4. [Client 화면] NBC_CH6FunctionLibrary: 클라이언트가 Client RPC를 받아 GEngine->AddOnScreenDebugMessage 또는 UE_LOG로 채팅 및 야구 결과를 출력.

💡 4. 핵심 요약 및 느낀 점 (Key Takeaways)

  • 권한 분리 (Authority): 정답과 승패 판단 같은 보안 로직은 반드시 서버(GameMode)에 두고, 유저는 서버에게 RPC로 요청만 해야 핵/어뷰징을 막을 수 있다.
  • 복제 대상 구분:
    • PlayerController는 나만 조종하는 로컬 중심 액터.
    • PlayerState는 남들에게도 보여야 하는 내 정보를 담는 복제 액터.