[DAY66] Chat-System : RPC Pattern & Replication

베리투스·2025년 11월 19일

TIL: Today I Learned

목록 보기
56/93

데디케이티드 서버(Dedicated Server) 구조에서 클라이언트끼리는 직접 대화할 수 없다. 채팅 시스템을 구현하려면 '클라이언트 -> 서버 -> 모든 클라이언트'로 이어지는 릴레이 방식이 필요하다. 오늘은 RPC를 활용한 채팅 및 접속 알림 구현과, 변수 값을 자동으로 동기화해주는 프로퍼티 레플리케이션(Property Replication)의 기초를 정리했다.


📌 오늘의 목표

  • RPC를 활용하여 멀티플레이 채팅 시스템 구현하기
  • NetMulticastGameState를 이용해 접속 알림 띄우기
  • 프로퍼티 레플리케이션(Property Replication)의 작동 원리 이해

📚이론 및 원리

1. 채팅 구현의 핵심: "서버를 거쳐라"

지난 시간에 배운 것처럼 데디케이티드 서버 환경에서 클라이언트 A는 클라이언트 B의 존재를 직접적으로 알거나 통신할 수 없다. 따라서 채팅은 다음과 같은 3단계 릴레이로 구현해야 한다.

  1. 나(Client A): 채팅을 입력하고 Server RPC로 서버에 보낸다. ("서버야, 나 이 말 했어.")
  2. 서버(Server): 메시지를 받아서 현재 접속 중인 모든 플레이어 컨트롤러를 찾는다.
  3. 서버 \rightarrow 모두: 찾은 모든 컨트롤러의 Client RPC를 호출해 메시지를 띄운다. ("모두들 들어라, A가 이렇게 말했다.")

❓ 왜 PlayerController에서 NetMulticast를 안 쓸까?
NetMulticast는 "해당 액터가 존재하는 모든 단말"에 함수를 실행한다. 하지만 PlayerController는 보안상 서버와 내 PC(Local)에만 존재하고, 남의 PC(Remote)에는 존재하지 않는다.
따라서 PlayerController에서 멀티캐스트를 쏘면 나랑 서버에만 채팅이 뜨고, 다른 친구 화면에는 뜨지 않는 참사가 벌어진다. 그래서 번거롭더라도 서버가 반복문(Iterator)을 돌며 일일이 Client RPC를 쏴주는 방식을 사용했다.

2. 접속 알림: GameState와 NetMulticast

반면, "누군가 게임에 접속했습니다" 같은 알림은 모든 클라이언트에게 방송해야 한다. 이때는 NetMulticast가 아주 유용하다.

  • GameMode: 서버에만 존재한다. 플레이어가 로그인(OnPostLogin)하면 이를 감지한다.
  • GameState: 서버와 모든 클라이언트에 복제(Replication)되어 존재한다.

따라서 흐름은 다음과 같다.
1. GameMode가 접속을 감지 \rightarrow GameState의 함수 호출.
2. GameState는 모든 클라이언트가 다 가지고 있으므로, 여기서 NetMulticast 함수를 실행.
3. 모든 클라이언트의 화면에 접속 알림 출력.

3. 프로퍼티 레플리케이션 (Property Replication)

RPC가 "함수 실행"을 요청하는 것이라면, 레플리케이션은 "변수 값의 동기화"다.
서버에서 변수 값이 바뀌면, 연결된 클라이언트들의 해당 변수 값도 자동으로 갱신된다.

  • 방향: 오직 Server \rightarrow Client (단방향). 클라에서 값을 바꿔봤자 서버나 남들에겐 안 보인다.
  • 조건:
    1. 액터의 bReplicates = true여야 함.
    2. 변수에 UPROPERTY(Replicated) 지정.
    3. GetLifetimeReplicatedProps 함수에서 DOREPLIFETIME 매크로 설정.
// 서버가 받아서 -> 모든 클라이언트에게 뿌린다.
void ACXPlayerController::ServerRPCPrintChatMessageString_Implementation(const FString& InString)
{
    // 월드에 있는 모든 플레이어 컨트롤러를 순회한다.
    for (TActorIterator<ACXPlayerController> It(GetWorld()); It; ++It)
    {
        ACXPlayerController* PC = *It;
        if (IsValid(PC))
        {
            // 각 PC에게 Client RPC를 쏴준다. (귓속말을 모두에게 하는 느낌)
            PC->ClientRPCPrintChatMessageString(InString);
        }
    }
}

✅ 핵심 요약

개념설명비고
Server RPC클라이언트가 서버에게 요청 (채팅 전송)Validation 필요 (보안)
Client RPC서버가 특정 클라이언트에게 요청 (채팅 수신)소유권(Ownership)이 중요함
NetMulticast서버가 해당 액터를 가진 모든 단말에게 실행PlayerController에서는 사용 주의
Property Replication서버의 변수 변경을 클라이언트에 자동 동기화DOREPLIFETIME 설정 필요
채팅 로직Client A \rightarrow Server \rightarrow Loop All ClientsPC는 남의 컴퓨터에 없으므로 이 방식 사용
profile
Shin Ji Yong // Unreal Engine 5 공부중입니다~

0개의 댓글