전체 네트워크 CS 구조
멀티플레이 게임(Photon 포함)에서 중요한 CS 네트워크 개념을 크게 나누면
- 인터넷의 기본 요소
- IP, 포트, 패킷, 라우팅
- 통신 방식
- TCP vs UDP, 소켓(Socket)
- 게임 서버 아키텍처
- 클라이언트–서버, 마스터 서버, 게임 서버, 라우팅
- 세션 / 룸 / 플레이어
- 방(Room), 세션(Session), 매치메이킹, MaxPlayers 개념
- 메시지와 동기화 방식
- RPC / 이벤트 / 상태 동기화(State Sync) / 명령(Command)
- 동기화 이슈와 해결 개념
- 지연(Latency), 보간(Interpolation), 예측(Prediction), 권한(Authority), 소유권(Ownership)
- 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. 메시지의 두 가지 큰 축
- State Sync (상태 동기화)
- “내 현재 상태는 이거야”를 계속 보내줌
- 예: 위치, 체력, 애니메이션 상태
- Command / RPC (명령, 이벤트)
- “이 행동을 했으니 처리해줘”
- 예: 공격 버튼 눌렀다, 총을 발사했다, 문을 열었다
5-2. RPC (원격 프로시저 호출)
- 내 코드의 함수를 “원격에 있는 다른 클라이언트에서 실행시키는” 개념
- Photon에서는:
- PUN RPC, RaiseEvent 등 사용
5-3. 이벤트 기반 콜백
- 네트워크에서 상태가 바뀌었을 때,
계속 검사(polling)하지 않고
“변화가 생기면 알려줘” 방식으로 받는 패턴 - Photon:
- MonoBehaviourPunCallbacks 상속 후
- OnConnectedToMaster()
- OnJoinedRoom()
- OnPlayerEnteredRoom()
같은 콜백으로 이벤트 처리
- MonoBehaviourPunCallbacks 상속 후
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)
- 어느 쪽 말을 진짜로 믿을 것인가?
- 서버 권한(Server Authority)
- 서버가 최종 진실
- 치트 방어에 유리, 안정적
- 클라이언트 권한(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 – 신뢰성 중심
특징
- 연결 지향(Connection-oriented)
- 패킷 유실 시 자동 재전송
- 순서 보장
- 대신 오버헤드가 크고 상대적으로 느림
게임에서 쓰이는 곳
- 로그인 / 회원 정보 / 인벤토리 / 상점 / 결제
- 채팅
- “절대 사라지면 안 되는 데이터”에 적합
정리
“한 번만 정확히 오면 되는 요청/응답” → TCP 스타일이 어울림
8-3. UDP – 속도 중심
특징
- 비연결(Connectionless)
- 유실/순서 보장 없음
- 오버헤드 작고 빠름
- “조금 손해 봐도 되지만, 자주/빨리 보내야 하는 데이터”에 적합
게임에서 쓰이는 곳
- 실시간 위치, 회전, 속도
- 이동 입력
- 총구 방향, 카메라 방향
정리
“매 프레임 새 값이 오니까, 하나쯤 날아가도 상관 없는 정보” → 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. 세 가지 통신 축 다시 정리
- State Sync (상태 동기화)
- 계속 바뀌는 “현재 값” 공유
- 예: 위치, 회전, 속도, 애니메이션 파라미터 등
- Photon: PhotonTransformView, OnPhotonSerializeView
- RPC / Event (명령/사건)
- 특정 시점에 “이 행동을 했다” 전달
- 예: 총 발사, 문 열기, 스킬 사용, 폭발
- Photon: PunRPC, PhotonView.RPC, PhotonNetwork.RaiseEvent
- 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 기준 “채팅방”:
- 서버는 Photon Cloud가 이미 있음
- 클라는 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번을 채팅으로 사용)
- 보낼 내용(문자열)
정도만 신경 쓰면 됨.
'유니티 > 개념정리' 카테고리의 다른 글
| 자료구조 총정리 (이것이 취업을 위한 컴퓨터 과학이다 with CS 기술 면접) (0) | 2025.12.11 |
|---|---|
| 아두이노 개념 정리, 시리얼 통신 (2) | 2025.07.09 |
| XR Interaction Toolkit 의 AR Starter Assets VS AR Foundation 차이 (1) | 2025.06.25 |
| [unity] AsyncOperation과 코루틴 (0) | 2025.06.24 |
| [unity] Time.time vsTime.deltaTime (0) | 2025.06.24 |