본문 바로가기
유니티/개념정리

멀티플레이 게임(Photon 포함)에서 중요한 CS 네트워크 개념

by 유니티세상 2025. 12. 11.
반응형

전체 네트워크 CS 구조

멀티플레이 게임(Photon 포함)에서 중요한 CS 네트워크 개념을 크게 나누면

  1. 인터넷의 기본 요소
    • IP, 포트, 패킷, 라우팅
  2. 통신 방식
    • TCP vs UDP, 소켓(Socket)
  3. 게임 서버 아키텍처
    • 클라이언트–서버, 마스터 서버, 게임 서버, 라우팅
  4. 세션 / 룸 / 플레이어
    • 방(Room), 세션(Session), 매치메이킹, MaxPlayers 개념
  5. 메시지와 동기화 방식
    • RPC / 이벤트 / 상태 동기화(State Sync) / 명령(Command)
  6. 동기화 이슈와 해결 개념
    • 지연(Latency), 보간(Interpolation), 예측(Prediction), 권한(Authority), 소유권(Ownership)
  7. Photon이 제공하는 추상화 계층
    • PhotonNetwork, PhotonView, Transform/AnimatorView, 콜백 구조

인터넷의 기본 요소 – IP, 포트, 패킷, 라우팅

1-1. IP (집 주소)

  • IP 주소 = 인터넷 상의 “집 주소”
  • 어느 컴퓨터(서버, 클라이언트)로 데이터를 보내야 하는지 구분해주는 번호

1-2. 포트 (집 안의 방 번호)

  • 하나의 컴퓨터에서 여러 프로그램이 네트워크를 쓸 수 있음
  • 포트 번호 = “어떤 프로그램한테 줄지” 구분하는 방 번호 같은 것
    • 예: 5055, 7777 이런 숫자들

1-3. 패킷 (택배 박스)

  • 네트워크에서 실제로 왔다 갔다 하는 데이터 조각
  • 안에:
    • 출발지 IP/포트
    • 목적지 IP/포트
    • 실제 내용(위치 정보, 채팅, 명령 등)

1-4. 라우팅 (길 안내)

  • 패킷을 “어느 길로 보내야 목적지에 도착할지” 결정하는 과정
  • 인터넷 중간 중간의 라우터(길 안내자) 들이
    • “이 패킷은 여기로 가면 되겠다” 하고 넘겨줌
  • 게임 서버 세계에서는
    • “어느 게임 서버로 이 유저를 붙일까?” 결정하는 것도 넓은 의미의 라우팅

2. 통신 방식 – TCP, UDP, 소켓

2-1. 소켓(Socket)

  • 프로그램이 네트워크를 통해 이야기할 수 있게 OS가 제공하는 통로
  • C#에서 직접 네트워크를 하면:
    • Socket, TcpClient, UdpClient 같은 걸 쓰게 됨
  • Photon에서는 → 소켓 다루는 부분을 PhotonNetwork 안에 숨겨둠

2-2. TCP

  • 전화 통화처럼:
    • 연결 확실히 맺고
    • 순서 보장
    • 중간에 빠지면 재전송
  • 장점: 신뢰성 좋음
  • 단점: 느리고 오버헤드 큼 → 실시간 게임에만 쓰기엔 무겁기도 함

2-3. UDP

  • 택배 던져놓고 확인 안 하는 느낌:
    • 연결 개념 약함
    • 순서 보장 X
    • 유실될 수도 있음 (재전송은 애플리케이션 레벨에서 직접 구현해야)
  • 장점: 빠름, 실시간에 유리
  • 단점: 신뢰성 낮음

게임은 보통

  • 로그인, 로비, 매치메이킹 → TCP 성격
  • 실시간 위치, 입력 → UDP 많이 사용

Photon은 이걸 알아서 적절하게 처리하는 네트워크 엔진이라고 보면 됨.


3. 게임 서버 아키텍처 – 클라/서버, 마스터 서버, 게임 서버

3-1. 클라이언트–서버 구조

  • 클라이언트(Unity 게임) : 화면, 입력, 연출 담당
  • 서버 : 방, 플레이어, 게임 규칙, 검증, 동기화 담당

3-2. Master Server

  • 역할:
    • 방 목록 관리
    • 매치메이킹(어느 방에 넣을지 결정)
    • 어떤 Game Server가 어떤 방을 관리하는지 관리
  • 유저가 처음 접속할 때는 → 마스터 서버에 먼저 붙음
    → 그다음 어느 게임 서버로 가야 하는지 라우팅

3-3. Game Server

  • 실제 방(Room)이 존재하고
    플레이어들이 붙어서 게임하는 서버
  • 위치/애니메이션/아이템 드랍 등 실제 게임 메시지가 오가는 곳

Photon에서도:

  • 마스터 서버에 먼저 연결 (OnConnectedToMaster)
  • 방에 Join하면 → 내부적으로 해당 방이 있는 Game Server로 라우팅됨
  • 우리는 JoinRandomRoom(), CreateRoom() 같은 함수만 쓰면 됨

4. 세션 / 룸 / 플레이어 개념

4-1. 세션(Session)/룸(Room)

  • 특정 게임 플레이를 위한 임시 공간
  • 예: 롤 한 판, 배그 한 판 = 하나의 세션/방
  • Photon에서:
    • Room = 하나의 세션
    • RoomOptions.MaxPlayers = 이 방에 몇 명까지 받을지

4-2. 플레이어(Player)

  • 각 클라이언트에 대응되는 서버 쪽 개체
  • Photon에서는:
    • Photon.Realtime.Player
    • PhotonNetwork.LocalPlayer → 나 자신
    • PhotonNetwork.PlayerList → 현재 방에 있는 모두

4-3. 매치메이킹

  • “어떤 방에 이 유저를 붙일까?” 결정하는 과정
  • Photon에서:
    • JoinRandomRoom() → 조건 맞는 방 찾기
    • 실패 시 OnJoinRandomFailed() 에서 CreateRoom() 호출 → 새 방 생성

5. 메시지와 동기화 – RPC, 이벤트, 상태 동기화

5-1. 메시지의 두 가지 큰 축

  1. State Sync (상태 동기화)
    • “내 현재 상태는 이거야”를 계속 보내줌
    • 예: 위치, 체력, 애니메이션 상태
  2. Command / RPC (명령, 이벤트)
    • “이 행동을 했으니 처리해줘”
    • 예: 공격 버튼 눌렀다, 총을 발사했다, 문을 열었다

5-2. RPC (원격 프로시저 호출)

  • 내 코드의 함수를 “원격에 있는 다른 클라이언트에서 실행시키는” 개념
  • Photon에서는:
    • PUN RPC, RaiseEvent 등 사용

5-3. 이벤트 기반 콜백

  • 네트워크에서 상태가 바뀌었을 때,
    계속 검사(polling)하지 않고
    “변화가 생기면 알려줘” 방식으로 받는 패턴
  • Photon:
    • MonoBehaviourPunCallbacks 상속 후
      • OnConnectedToMaster()
      • OnJoinedRoom()
      • OnPlayerEnteredRoom()
        같은 콜백으로 이벤트 처리

6. 동기화 이슈 & 해결 개념

6-1. 지연(Latency) & 패킷 유실

  • 네트워크는 항상 느리고, 가끔씩 끊기고, 패킷이 사라질 수 있음
  • 그래서 화면이 “뚝뚝 끊기거나”, “위치 튀고”, “총 맞았는데 뒤늦게 죽거나” 발생

6-2. 보간(Interpolation)

  • 일정 간격으로 받은 위치들을 중간값으로 부드럽게 이어서 보여주는 것
  • 예: 0초에 (0,0), 0.1초에 (1,0) 받으면
    중간 프레임에서 (0.1,0), (0.2,0)… 로 자연스럽게 움직임

6-3. 예측(Prediction)

  • “다음 프레임에서 어디 있을지 미리 예측해서 먼저 보여주고,
    나중에 서버 값과 맞추는” 기법
  • FPS, 빠른 액션 게임에 많이 필요

6-4. 권한(Authority) & 소유권(Ownership)

  • 어느 쪽 말을 진짜로 믿을 것인가?
  1. 서버 권한(Server Authority)
    • 서버가 최종 진실
    • 치트 방어에 유리, 안정적
  2. 클라이언트 권한(Client Authority)
    • 각 클라이언트가 자기 캐릭터는 직접 제어
    • 입력 반응이 빠름

Photon에서는:

  • 기본적으로 클라이언트 권한 구조 많이 사용
  • PhotonView.IsMine :
    • 이 오브젝트의 소유자(Owner)가 나냐? 를 알려주는 값
    • IsMine == true 일 때만 입력 처리, 위치 전송

7. Photon이 제공하는 추상화 계층

이제 위 개념들이 Photon에서 어떤 식으로 추상화돼 있는지 한 번에 묶어보자.

7-1. PhotonNetwork – 네트워크 서브시스템 매니저

  • 서버 연결: ConnectUsingSettings()
  • 방: CreateRoom, JoinRandomRoom, CurrentRoom
  • 플레이어: LocalPlayer, PlayerList
  • 네트워크 객체 생성: Instantiate

 소켓/TCP/UDP/프로토콜 을 직접 만지지 않고
이 함수들만 호출하면 됨.

7-2. PhotonView – 네트워크 엔티티 ID

  • “이 GameObject는 네트워크에서 공유되는 대상이다” 표시
  • 각 PhotonView는 고유 ID를 가짐
  • IsMine : 이 오브젝트를 제어하는 주체가 나인지 확인

7-3. Transform/Animator 동기화

  • PhotonTransformView → 위치/회전/스케일 동기화 (State Sync)
  • PhotonAnimatorView → 애니메이션 파라미터 동기화

7-4. 콜백 구조

  • MonoBehaviourPunCallbacks 상속 → 이벤트 기반 네트워크 처리

예:

public class NetworkManager : MonoBehaviourPunCallbacks
{
    void Start()
    {
        PhotonNetwork.ConnectUsingSettings();
    }

    public override void OnConnectedToMaster()
    {
        PhotonNetwork.JoinRandomRoom();
    }

    public override void OnJoinRandomFailed(short code, string msg)
    {
        PhotonNetwork.CreateRoom(null, new RoomOptions { MaxPlayers = 2 });
    }

    public override void OnJoinedRoom()
    {
        PhotonNetwork.Instantiate("Player", Vector3.zero, Quaternion.identity);
    }
}

 

  • 서버 연결
  • 매치메이킹
  • 세션 생성
  • 라우팅 완료 후 플레이어 스폰
    까지 전체 네트워크 플로우가 들어있음.

8. TCP vs UDP 심화 – 실제 게임에서 어떻게 쓰는지

8-1. 비유

  • TCP
    • “카톡·문자처럼: 보낸 건 꼭 도착해야 하고, 순서도 지켜져야 하는 통신”
    • 중간에 빠지면 다시 보내고, 1 → 2 → 3 순서 보장.
  • UDP
    • “마이크로 ‘지금!’ 외치고 지나가는 방송 느낌”
    • 못 들으면 끝, 다시 안 보내줌. 순서도 보장 X.

8-2. TCP – 신뢰성 중심

특징

  1. 연결 지향(Connection-oriented)
  2. 패킷 유실 시 자동 재전송
  3. 순서 보장
  4. 대신 오버헤드가 크고 상대적으로 느림

게임에서 쓰이는 곳

  • 로그인 / 회원 정보 / 인벤토리 / 상점 / 결제
  • 채팅
  • “절대 사라지면 안 되는 데이터”에 적합

정리

 “한 번만 정확히 오면 되는 요청/응답” → TCP 스타일이 어울림


8-3. UDP – 속도 중심

특징

  1. 비연결(Connectionless)
  2. 유실/순서 보장 없음
  3. 오버헤드 작고 빠름
  4. “조금 손해 봐도 되지만, 자주/빨리 보내야 하는 데이터”에 적합

게임에서 쓰이는 곳

  • 실시간 위치, 회전, 속도
  • 이동 입력
  • 총구 방향, 카메라 방향

정리

 “매 프레임 새 값이 오니까, 하나쯤 날아가도 상관 없는 정보” → UDP 스타일


8-4. 실제 멀티플레이 구조에서의 조합

현실 게임 서버는 보통 이렇게 섞어 쓴다

  • TCP 스타일(신뢰)
    • 로그인, 로비, 매치메이킹, 방 생성/삭제
    • 아이템 강화, 상점 구매, 랭킹 저장 등
  • UDP 스타일(속도)
    • 캐릭터/차량의 위치·속도·회전 동기화
    • 입력 정보(좌/우, 점프, 공격 버튼 등)

Photon / Mirror 같은 라이브러리들은 내부에서:

  • “이건 Reliable(재전송+순서)”
  • “이건 Unreliable(유실 허용)”

등을 플래그로 나눠서 처리해 주고, 우리는 “이 메시지가 사라지면 안 되는가?” 기준으로 선택만 하면 됨.


8-5. Photon 관점 요약

Photon을 쓸 때 느끼는 것:

  • PhotonNetwork.CreateRoom, JoinRandomRoom → TCP적인 성격(신뢰 필수)
  • Transform 동기화(PhotonTransformView) → UDP적인 성격(유실 허용)

정확한 프로토콜은 Photon 내부 구현이지만, 개념적으로는:

“중요한 건 Reliable로, 자주 바뀌는 상태는 Unreliable로”
라고 나눠 쓰는 네트워크 엔진이라고 이해하면 된다.


9. 클라이언트 권한 vs 서버 권한 – Photon 기준으로 보기

9-1. 클라이언트 권한(Client Authority)

“각 클라이언트가 자기 캐릭터에 대한 진실이다.”

특징

  • 내 PC에서 입력 → 즉시 내 캐릭터 움직임
  • 서버/다른 유저에게 “내 위치는 이거야”라고 알려줌
  • 반응이 빠르고 구현이 쉽다
  • 하지만 치트에 취약

Photon PUN의 전형적인 예

void Update()
{
    if (photonView.IsMine == false)
    {
        return; // 내가 소유한 오브젝트가 아니면 입력 처리 안 함
    }

    // 여기서 이동/점프/마우스 회전 등 처리
}
 
  • 여기서 photonView.IsMine == true 인 클라이언트가
    자기 캐릭터의 움직임을 결정하는 주체 → 클라 권한 구조

9-2. 서버 권한(Server Authority)

“게임에서 진짜 정답은 항상 서버가 들고 있다.”

특징

  • 클라는 “이렇게 하고 싶다” 요청만 보냄
  • 서버가:
    • 가능한 행동인지 검증
    • 판정(공격 / 피격 / 사거리 등)
    • 결과를 모든 클라에게 반영
  • 치트 방어에 강하지만 구현 복잡 + 지연 보정 필요

  • 유저: “A를 때렸어요!” → 서버로 요청
  • 서버: “정말 사거리 안에 있고 시야 안에 있고 쿨타임 OK?” → 검증
  • 결과: “맞음, HP 몇 깎임” → 전파

9-3. Photon PUN에서 둘이 어떻게 섞여 있나?

Photon PUN은 현실적으로 혼합 구조에 가깝다:

  • 서버 권한 쪽
    • 방(Room) 생성/삭제
    • 플레이어 입장/퇴장 순서
    • 방 속성(RoomProperties)
    • 이벤트 순서 옵션
  • 클라이언트 권한 쪽
    • 자기 캐릭터 이동, 회전, 애니메이션
    • 스킬 발동, 이펙트 재생 등 대부분의 액션
      (보통 RPC/Event로 날리고, 다른 클라가 믿고 따라감)

그래서:

Photon PUN =
“세션/방/플레이어 리스트/기본 순서는 서버가 맡고,
실제 움직임·연출은 클라가 많이 맡는 구조”라고 보면 된다.


9-4. 왜 이걸 알아야 하냐?

이걸 알아야:

  • “이 로직은 서버가 검증해야 하나, 클라에 맡겨도 되나?”
  • “치트 위험이 크면 구조를 바꿔야 하는지?”

같은 판단을 할 수 있음.
PUN은 캐주얼/작은 규모의 게임엔 편하지만,
경쟁/랭크/치트 민감 게임
→ 서버 권한 구조(Fusion, Dedicated 서버, Custom Server 등)로 갈 필요가 있음.


10. RPC / Event / State Sync – 설계 기준 잡기

10-1. 세 가지 통신 축 다시 정리

  1. State Sync (상태 동기화)
    • 계속 바뀌는 “현재 값” 공유
    • 예: 위치, 회전, 속도, 애니메이션 파라미터 등
    • Photon: PhotonTransformView, OnPhotonSerializeView
  2. RPC / Event (명령/사건)
    • 특정 시점에 “이 행동을 했다” 전달
    • 예: 총 발사, 문 열기, 스킬 사용, 폭발
    • Photon: PunRPC, PhotonView.RPC, PhotonNetwork.RaiseEvent
  3. Room / Player Properties (전역 상태)
    • 방 전체에 공유되어야 하는 게임 상태
    • 예: 현재 라운드 수, 남은 시간, 팀별 점수, 룰 모드 등

10-2. State Sync를 쓰는 상황

조건

  • 값이 계속 바뀐다.
  • 과거 값보다 “지금 값”이 중요하다.
  • 약간의 유실·딜레이는 허용된다.

예시

  • 캐릭터/차량의 위치, 회전
  • 카메라 방향
  • 애니메이션에서 “달리는 중 vs 서 있는 중”

Photon 예

  • PhotonTransformView
  • PhotonAnimatorView
  • 또는 직접 OnPhotonSerializeView 구현

10-3. RPC / Event를 쓰는 상황

조건

  • 한 번 일어나는 “사건”이다.
  • 타이밍이 의미가 있다.
  • 경우에 따라 과거에 일어난 사실도 중요할 수 있다.

예시

  • 총 발사 순간
  • 폭발 이펙트
  • 문 열림/닫힘
  • 스킬 사용, 피격 판정

Photon PUN 예시

[PunRPC]
void Fire(int bulletId)
{
    // bulletId 기반으로 총알 생성 처리
}

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space) && photonView.IsMine)
    {
        photonView.RPC("Fire", RpcTarget.All, nextBulletId);
    }
}
 

10-4. Room/Player Properties를 쓰는 상황

조건

  • “방 전체에서 공유해야 하는 상태”
  • 클라가 접속을 나중에 해도, 현재 값만 알면 되는 정보

예시

  • 현재 라운드 수
  • 게임 진행 상태(대기, 시작, 종료 등)
  • 팀 점수
  • 맵/모드 이름

Photon 사용 느낌

  • PhotonNetwork.CurrentRoom.CustomProperties["Score"]
  • PhotonNetwork.LocalPlayer.CustomProperties["Team"]

10-5. 결정 규칙 한 줄 정리

  • 계속 변하는 값, 최신 값만 중요 → State Sync
  • 한 순간의 행동/사건, 타이밍 중요 → RPC/Event
  • 방 전체가 공유해야 하는 고정/상대적으로 느린 상태 → Room/Player Properties

이 기준으로만 나눠도 설계가 훨씬 깔끔해진다.


11. 직접 구현 vs Photon – TcpClient 채팅 예시로 보는 추상화

11-1. 직접 C# TcpClient로 만들면 해야 하는 일들

직접 네트워크 채팅 서버를 만든다고 치면:

  • 서버:
    • TcpListener로 접속 받기
    • 접속한 TcpClient들을 List로 관리
    • 각 클라마다 스레드/비동기로 Read 루프 돌리기
    • 수신한 메시지를 다른 모든 클라에게 브로드캐스트
    • 끊어진 클라 제거, 예외 처리
  • 클라이언트:
    • TcpClient.Connect(ip, port)로 연결
    • 입력을 읽어서 메시지 전송
    • 다른 스레드에서 서버 메시지 계속 읽어서 출력

= 소켓, 스레드, 예외, 브로드캐스트, 접속 관리까지 전부 직접 구현해야 함.


11-2. Photon으로 같은 걸 한다면?

Photon PUN 기준 “채팅방”:

  1. 서버는 Photon Cloud가 이미 있음
  2. 클라는 Unity에서:
     
     
public class SimpleChat : MonoBehaviourPunCallbacks
{
    void Start()
    {
        PhotonNetwork.ConnectUsingSettings();
    }

    public override void OnConnectedToMaster()
    {
        PhotonNetwork.JoinOrCreateRoom("ChatRoom",
            new RoomOptions { MaxPlayers = 20 }, null);
    }

    public override void OnJoinedRoom()
    {
        Debug.Log("Chat room joined");
    }

    public void SendChat(string message)
    {
        PhotonNetwork.RaiseEvent(
            0, message,
            new RaiseEventOptions { Receivers = ReceiverGroup.All },
            SendOptions.SendReliable
        );
    }

    public override void OnEvent(EventData photonEvent)
    {
        if (photonEvent.Code == 0)
        {
            string message = (string)photonEvent.CustomData;
            Debug.Log("[Chat] " + message);
        }
    }
}

Photon이 대신 해주는 것:

  • 서버 인프라 운영
  • 접속/끊김 관리
  • 방 생성/삭제/입장
  • 클라 리스트 관리
  • 메시지 브로드캐스트
  • 재전송/순서 보장/신뢰성 처리
  • Region / 라우팅 / 확장성 등

우리는:

  • 방 이름
  • 이벤트 코드(0번을 채팅으로 사용)
  • 보낼 내용(문자열)
    정도만 신경 쓰면 됨.

 

반응형