Version: 5.6
레거시 네트워크 레퍼런스 가이드
Unity의 네트워킹 요소(레거시)

네트워킹 개념의 개요(레거시)

(새 프로젝트에는 5.1에서 도입된 새로운 네트워킹 시스템을 사용해야 합니다. 다음 내용은 이전 네트워킹 시스템을 사용하는 레거시 프로젝트를 대상으로 작성되었습니다.)

이 섹션에서는 Unity 네트워킹 아키텍처에서 게임 개발을 하는 데 있어 알아야 할 일반적인 네트워킹 개념에 대해 다루고 있습니다.

네트워킹이란?

네트워킹은 2대 이상의 컴퓨터 사이에서 이루어지는 커뮤니케이션입니다. 기본적인 개념은 클라이언트(정보를 요청하는 컴퓨터)와 __ 서버__(정보 요청에 응답하는 컴퓨터)의 관계입니다. 서버는 모든 클라이언트에서 사용되는 호스트 컴퓨터 또는 단순히 플레이어가 게임을 실행하는 컴퓨터(클라이언트)가 또 다른 플레이어의 서버 역할을 하는 경우의 두 가지가 있습니다. 서버가 구축되고 클라이언트가 연결되면 두 컴퓨터는 게임플레이에 따라 데이터를 교환할 수 있습니다.

네트워크 게임을 만들려면 매우 구체적인 세부 사항까지 신경을 써야 합니다. Unity에서 네트워크 작업을 설계하고 작성하는 일은 쉽지만 복잡한 작업입니다. Unity의 주요 설계 방침은 네트워킹을 최대한 견고하고 유연성을 가지도록 하기 위한 것입니다. 즉 게임 크리에이터는 다른 게임 엔진에서 자동화는 되어 있지만 견고함은 부족한 것들에 대해서도 신경을 써야 합니다. 게임 크리에이터의 판단은 게임 디자인에 큰 영향을 미치므로 설계 초기 단계에 결정을 내리는 일이 매우 중요합니다. 네트워킹의 개념을 이해하는 것은 게임 디자인 계획을개선하고 게임 구현시 발생하는 문제를 해결하는 데 도움이 됩니다.

네트워크 접근법

네트워크 게임의 구축에 있어서 잘 알려져 있으며 검증도 충분히 행해지고 있는 두 가지 방법으로 권한 서버(Authoritative Server)비권한 서버(Non-Authoritative Server) 방식이 있습니다. 두 가지 방법은 서버의 클라이언트 연결의 확립과 상호 데이터 전달을 전제로 하고 있습니다. 둘다 최종 사용자끼리 직접 연결되거나 서로에게 IP 주소가 노출되게 하진 않기에 최종 사용자에게 프라이버시를 보장합니다.

권한 서버

권한 서버에 접근하려면 서버가 게임 월드의 시뮬레이션, 게임 규칙 적용 및 플레이어 클라이언트로부터 입력 처리를 수행해야 합니다. 각 클라이언트는 입력(키 입력이나 요청된 액션의 형태로)을 서버에 보내고 지속적으로 게임의 현재 상태를 서버에서 가져옵니다. 클라이언트는 게임 상태에 직접 변경하지 않습니다. 대신 서버에 하고 싶은 사항을 요청하고 서버가 요청을 처리하며 클라이언트에 결과로써 무엇이 일어났는가를 응답합니다.

기본적으로 플레이어가 원하는 것과 실제로 일어나는 일은 분리되어 있습니다. 이로 인해 서버가 모든 클라이언트의 요청을 듣고 게임 상태를 어떻게 업데이트해야 하는지 판단할 수 있습니다.

이 방법의 장점은 클라이언트의 부정행위가 더 어려워진다는 점입니다. 예를 들어, 클라이언트가 서버에(또한 다른 클라이언트에게) 적을 죽인 것을 전달할 때 스스로 판단을 낼 수 없기 때문에 부정행위가 일어날 수 없습니다. 서버에 무기를 발사한 것을 전하고 거기서 적이 죽었는지 여부는 서버가 결정합니다.

권한 서버의 또 다른 예는 물리에 의존하는 멀티 플레이어 게임입니다. 각 클라이언트가 각각의 물리 시뮬레이션을 기반으로 실행되면 클라이언트 사이에 약간의 차이가 나서 점차 동기화되지 않는 상황을 발생시킬 수 있습니다. 그러나 모든 물리 오브젝트의 시뮬레이션이 중앙 서버에서 처리되면 업데이트 상태가 클라이언트에게 다시 보내질 수 있으며 무결성이 보장됩니다.

권한 서버의 잠재적인 단점은 네트워크에서 메시지가 전달되는 데 시간이 걸린다는 점입니다. 플레이어가 캐릭터를 앞으로 움직이고 네트워크 서버에서 리스폰스가 0.1초 걸린다고 하면 플레이어가 리스폰스 지연을 인식합니다. 하나의 해결책은 클라이언트측 예측(client-side prediction) 을 수행하는 것입니다. 이 기술의 본질은 클라이언트가 게임 상태의 로컬 버전을 업데이트할 수 있지만 필요하다면 서버의 권한 버전으로부터 수정을 받습니다. 일반적으로 간단한 게임 액션에서만 사용되어야 하며 게임 상태에서 중요한 정보의 업데이트에서는 사용하지 않습니다. 예를 들어 플레이어에게 적이 죽은 정보를 보고한 다음 서버가 이 결정을 뒤집어서는 안 됩니다.

클라이언트 측의 예측은 고급 주제이므로 가이드에서 다루지 않지만 교재나 웹 리소스를 통해 자세한 내용을 확인할 수 있습니다.

권한 서버는 비권한 서버에 비해 처리하는 오버헤드가 높습니다. 서버가 모든 게임 상태에 대한 변경사항을 처리하지 않아도 될 경우 이 로드의 많은 부분을 클라이언트에게 분산시킬 수 있습니다.

비권한 서버

비권한 서버는 모든 사용자 입력 결과를 제어하지 않습니다. 클라이언트 자신이 사용자 입력과 게임 로직을 로컬로 처리하여 정해진 액션을 서버로 전송합니다. 서버는 모든 액션을 게임 월드의 상태와 동기화합니다. 이를 통해 설계의 관점에서 구현하기 쉽고 서버는 단순히 클라이언트 간의 메시지 릴레이를 할 뿐이며 클라이언트가 수행하는 이상의 추가 처리가 없습니다.

클라이언트가 모든 물리와 이벤트를 스스로 다루게 되면 예측 방법은 필요하지 않게 되며 발생한 내용을 서버에 릴레이합니다. 이 경우 클라이언트는 오브젝트에 대한 소유자 가 되며 네트워크에서 유일하게 오브젝트의 수정을 허가 받은 에이전트가 됩니다.

네트워크 커뮤니케이션 방법

네트워크 게임의 기본 아키텍처를 설명했으므로 클라이언트와 서버가 더 낮은 단계에서 서로 통신하는 방법을 살펴보겠습니다.

커뮤니케이션 방법에는 원격 프로시저 호출(Remote Procedure Calls)상태 동기화(State Synchronization) 의 두 가지가 있습니다. 여러 게임의 다양한 상황에서 두 방법 모두 공통적으로 사용됩니다.

원격 프로시저 호출

원격 프로시저 호출은 네트워크의 다른 컴퓨터의 함수를 호출하기 위해 사용되며 여기에서 “네트워크”는 클라이언트와 서버가 동일한 컴퓨터에서 실행되는 경우에는 그 사이의 메시지 채널을 의미합니다. 클라이언트는 서버에 RPC를 보낼 수 있으며 서버는 RPC를 하나 이상의 클라이언트에 보낼 수 있습니다. 일반적으로 빈번하지 않은 액션에 사용됩니다. 예를 들어, 클라이언트가 문을 열기 위하여 스위치를 플립하는 경우 서버에 RPC를 전송하여 문이 열려 있음을 알립니다. 서버는 다른 RPC를 모든 클라이언트에 전송하여 각각의 로컬 함수를 실행하여 같은 문을 열 수 있게 합니다. 이를 개별 이벤트의 관리 및 실행에 사용합니다.

상태 동기화

상태 동기화는 지속적으로 변경이 일어날 데이터를 공유하는 데 사용합니다. 가장 좋은 예로는 액션 게임의 플레이어 포지션입니다. 플레이어는 지속적으로 이동, 달리기, 점프하는 등 여러 다양한 액션을 취합니다. 네트워크의 다른 모든 플레이어, 로컬에서 플레이어를 제어하지 않는 경우도 포함하여 어디에 위치하는지와 무엇을 하고 있는지 알 필요가 있습니다. 이 플레이어 포지션 데이터를 지속적으로 릴레이하는 것으로 게임이 정확하게 그 위치를 다른 플레이어에게 제공할 수 있습니다.

이러한 데이터는 네트워크를 통해 정기적으로 빈번하게 전송됩니다. 이 데이터는 시간에 민감하게 대응하며 하나의 컴퓨터에서 다음 컴퓨터에 통과할 때까지 시간이 걸리기 때문에 가능한 한 전송 데이터의 양을 줄이는 것이 중요합니다. 더 간단하게 말하면 상태 동기화는 자연스럽게 많은 대역폭을 필요로 하기 때문에 가능한 한 최소의 대역폭을 사용하도록 해야 합니다.

서버와 클라이언트 연결

서버와 클라이언트를 연결하는 것은 복잡한 프로세스가 될 수 있습니다. 컴퓨터는 프라이빗, 공용 IP 주소를 가지며 로컬 및 외부 방화벽이 액세스를 차단할 수 있습니다. Unity 네트워킹은 가능한 한 많은 상황을 처리하지만 절대적인 해결책은 없습니다.

사설 주소는 인터넷에서 직접 액세스할 수 없는 IP 주소입니다(구현하기 위해 사용되는 방법에 따라 네트워크 주소 이동 또는 NAT 주소라고도 합니다). 쉽게 설명하면 사설 주소는 로컬 라우터를 통과하고 거기서 주소가 공용 주소로 변환됩니다. 이렇게 하면 많은 사설 주소를 가진 컴퓨터는 하나의 공용 주소를 사용하여 인터넷에서 통신할 수 있습니다. 이것은 누군가 인터넷에서 다른 사설 주소와 통신하고 싶은 경우가 아니라면 아무런 문제가 없습니다. 커뮤니케이션은 라우터의 공용 주소를 통해 발생해야 하며 그 다음에 사설 주소로 메시지를 전달해야 합니다. NAT 펀치스루 기술을 사용하여 퍼실리테이터 로 알려진 공유 서버를 중개로 사설 주소를 공용 주소에서 도달할 수 있도록 합니다. 사설 주소가 먼저 퍼실리테이터에 연결하고 거기에서 로컬 라우터에 “구멍을 내어” 접근하는 것으로 실현됩니다. 그 다음 퍼실리테이터는 사설 주소가 사용하는 공용 IP 주소와 포트를 볼 수 있습니다. 이 정보를 활용하여 인터넷에 연결된 모든 컴퓨터가 보통의 경우 접근할 수 없는 사설 주소를 통해 직접 연결할 수 있습니다(NAT 펀치스루에 대한 자세한 내용은 실제로 더 복잡한 것임에 유의하시기 바랍니다).

공용 주소는 더 직관적입니다. 여기에서 주요 문제는 연결이 내부 또는 외부 방화벽에 의해 차단될 수 있다는 점입니다(내부 방화벽은 지키려고 하는 컴퓨터에서 로컬로 실행되고 있습니다). 내부 방화벽에서는 사용자가 특정 포트를 개방하여 게임 서버를 액세스 가능하게 하도록 해야 합니다. 외부 방화벽은 반대로 사용자의 관리하에 없습니다. Unity는 NAT 펀치스루를 사용하여 외부 방화벽을 통해 액세스 시도는 할 수 있지만 성공은 보장되지 않습니다. 우리가 해온 테스트 결과에 따르면 일반적으로 테스트에서는 작동하지만 발견이 옳다고 증명해주는 공식적인 연구 결과는 없습니다.

이미 언급했던 연결 문제는 서버와 클라이언트에 각각 다른 영향을 줍니다. 클라이언트 요청은 발신 트래픽뿐이기 때문에 비교적 직관적입니다. 클라이언트가 공용 주소를 가지는 경우 거의 항상 성공하는데 그 이유는 발신 트래픽까지 차단하는 것은 일반적으로 기업 네트워크 등 매우 엄격한 액세스 제한을 걸고 있는 것에 한정되기 때문입니다. 클라이언트가 사설 주소를 가지는 경우 NAT 펀치스루할 수 없는 사설 주소를 가진 서버를 제외한 모든 서버와 연결할 수 있습니다(이에 대한 자세한 내용은 아래 참조). 서버 측은 더 복잡한데 그 이유는 서버는 알 수 없는 발신자로부터 수신할 필요가 있기 때문입니다. 공용 주소를 통해 서버는 인터넷에 게임 포트를 개방하고 있어야 하며(즉 방화벽에서 차단되지 않음) 그렇지 않으면 클라이언트에서 연결을 받아들이지 못하고 사용할 수 없습니다. 서버가 사설 주소를 가지는 경우 NAT 펀치스루를 사용하여 연결을 허용하고 클라이언트 또한 NAT 펀치스루를 허용하지 않으면 연결할 수 없습니다.

Unity는 이러한 다양한 연결 상태를 테스트하는 툴을 제공합니다. 연결을 설정할 수 있는 상황이 마련되면 두 가지 방법이 있습니다. 직접 연결(클라이언트가 서버의 DNS 이름이나 IP 주소를 알아야 함) 또는 마스터 서버를 통한 연결입니다. 마스터 서버는 특정 게임 서버의 정보에 대해 미리 알 필요가 없는 클라이언트에 서버가 존재를 나타낼 수 있습니다.

네트워크 대역폭 최소화

여러 클라이언트간에 상태 동기화를 할 경우 모든 상세 정보가 없더라도 오브젝트가 동기화되지 않는 것은 아닙니다. 예를 들어, 캐릭터의 아바타를 동기화시키는 경우 클라이언트 사이를 송신하는 것은 포지션과 회전 정보만으로 충분합니다. 캐릭터 자체가 더 복잡하고 심도 깊은 트랜스폼(Transform) 계층 구조를 가지고 있었다고 해도 전체 계층 구조에 대한 정보를 공유할 필요는 없습니다.

게임에 있어서 많은 정보는 사실상 정적이며 클라이언트는 그것을 처음부터 전송 또는 동기화할 필요가 없습니다. 빈번하지 않거나 일회성 RPC 호출만으로 충분히 실현할 수 있습니다. 모든 게임의 설치에 존재하는 데이터를 유효하게 활용하고 클라이언트는 가능한 한 스스로 작동하도록 합니다. 예를 들어 텍스처 및 메시와 같은 에셋은 모든 설치에 존재하며 일반적으로는 변경되지 않기 때문에 동기화될 필요가 없습니다. 이것은 간단한 예제이지만 클라이언트간에 공유되어야 하는 중요한 데이터가 무엇인지 생각하기에는 충분합니다. 데이터는 클라이언트간에 유일하게 공유할 데이터가 될 것입니다.

네트워크 게임을 처음 만들 때는 특히 구체적으로 무엇을 공유해야 하고 무엇을 공유해서는 안되는지 여부를 판단하기 어렵습니다. 레벨 이름을 붙인 한번의 RPC 호출에서 모든 클라이언트가 지정된 레벨 전체를 로드할 수 있으며 자동으로 네트워크 요소를 제공할 수 있음을 유의하시기 바랍니다. 게임에서 클라이언트가 최대한 자급자족할 수 있도록 구성하여 대역폭을 최소화할 수 있습니다.

멀티플레이어 게임 성능

서버 자체의 물리적 위치와 성능은 서버에서 실행되는 게임 플레이 품질에 크게 영향을 줍니다. 서버와 다른 대륙에 있는 클라이언트는 엄청난 네트워크 지연을 겪을 수 있습니다. 이것은 인터넷의 물리적 한계이며 실제 해결 방법은 서버와 사용하는 클라이언트가 최대한 가깝게 위치하거나 적어도 같은 대륙에 있는 것입니다.

추가 리소스

다음은 네트워킹에 대해 학습할 수 있는 추가 리소스에 대한 링크 모음입니다.

  • http://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
  • http://developer.valvesoftware.com/wiki/Lag_Compensation
  • http://developer.valvesoftware.com/wiki/Working_With_Prediction
  • http://www.gamasutra.com/resource_guide/20020916/lambright_01.htm
레거시 네트워크 레퍼런스 가이드
Unity의 네트워킹 요소(레거시)