[IGC2017] 구승모 교수 "게임 서버 제작과 운영, 아마존을 통해 쉽게 해결한다"

게임뉴스 | 이두현 기자 | 댓글: 5개 |


▲ 구승모 교수

[인벤게임컨퍼런스(IGC) 발표자 소개] 구승모 교수는 아마존에서 게임 서비스 관련 프로그래밍과 프로덕션 운영을 맡고있다. 또한 NHN NEXT에서 게임 개발 교수, 블루홀에서 테라의 서버 아키텍쳐 시니어 소프트웨어 엔지니어, NCSOFT에서 L3 소프트 엔지니어로 근무했다.

재밌는 게임을 기획부터 개발까지 잘 완료했다 하더라도, 유저에게 선보이기 위해서는 서버와 운영 문제를 고민하지 않을 수 없다. 그리고 국내를 넘어 해외 시장을 바라보게 된다면 글로벌 서비스는 어떻게 할 것인지, 해외 서버는 어떻게 준비해야 할지 막막한 경우가 있다.

이에 구승모 교수는 아마존을 해답으로 제시한다. 구승모 교수가 IGC 2017에서 소개한 아마존의 GameLift는 서버 개발과 운영을 처음부터 시작하지 않도록 도와준다. 또한 수준 높은 세션형 게임 지원과 매치 메이킹, 최적화된 인터넷 환경으로 게임을 유저에게 선보일 수 있게 된다. 구승모 교수가 소개하는 아마존의 게임 서비스를 IGC 2017에서 들을 수 있었다.

※본 강연 기사는 주제 특성 상 강연자의 시점에서 서술했습니다.


■ 강연주제: 게임 클라이언트 엔진으로 게임 서버 제작, 서비스 운영까지 한번에

십여 년 전 WoW에 빠져 살다가 게임 업계로 넘어온 구승모입니다. NCSOFT와 블루홀에서 일했고, 현재는 아마존에서 게임 쪽 엔지니어로 있습니다. 과거에 몇 번 인벤에서 인터뷰하거나 기사가 나간 적이 있습니다.

모아보기
ㄴ[IGC2016] 빠르게 진화하는 게임 엔진! 아마존의 비밀 무기 '럼버야드'
ㄴ[NDC2014] NHN NEXT 구승모 교수의 '사설 서버를 막기 위한 가이드'
ㄴ[인터뷰] "테라의 논타겟팅, 이렇게 코딩했습니다", 서버 프로그래머 구승모 교수

오늘의 발표 순서는 첫 번째로 멀티 플레이어 게임에 대한 개요를 설명하고, 두 번째로는 데디케이티드 게임 서버에 대한 개념 설명을 하도록 하겠습니다. 마지막으로 GameLift라는 클라우드 서비스를 통해 서버 운영까지 하는 방법에 대해 말씀드리겠습니다.

본 강의에 앞서, 지금 근무하는 회사 소개를 잠깐 하겠습니다. 다들 아시는 아마존이고, 세계에서 시가총액 4~5위 하는 회사입니다. 시가총액이 가장 높은 글로벌 회사라고 하면 애플, 구글, MS, 아마존, 페이스북을 꼽는데요. 그 아마존입니다.

아마존에서는 AWS라는 클라우드 서비스도 제공하는데, 전 세계적으로 점유율이 가장 높고, 특히 게임 회사에서 많이 사용하고 있습니다.




물론, 아마존은 게임 특화 서비스도 제공하고 있습니다. 대표적으로 무료 AAA 게임 엔진인 럼버야드, 게임 백엔드를 돌릴 수 있는 AWS 클라우드, 다들 잘 아시는 게임 커뮤니티 및 스트리밍 서비스인 트위치까지 아마존이 운영하고 있습니다. 마지막으로, amazon.com을 통한 마케팅과 프로모션을 하고 있습니다.

이 모든 것이 게임을 위한 서비스로, 개발자들에게 편리함 외에 여러 가치를 제공하고 있습니다.

이제, 본격적으로 강연을 시작하겠습니다.


멀티플레이어 게임 개요

다들 잘 아시겠지만, 기본적인 멀티플레이어 게임의 구조 및 동기화 방법에 대한 설명을 간략히 하겠습니다.




싱글 플레이어 게임의 처리 과정부터 설명하겠습니다. 게임은 거대한 루프입니다. 마우스나 키보드를 통해 입력된 것을 바탕으로 상태를 시뮬레이션하고 결과를 화면에 보여주는 것이라 할 수 있습니다. 즉, 시간에 따라 상태가 변하는 기계입니다.




멀티 플레이어 게임은 싱글 플레이어 게임과 구조가 비슷하지만, 로직 처리를 하는 부분이 다릅니다. 붉은 점선 부분이 내 컴퓨터에 있지 않고, 인터넷상 다른 컴퓨터나 서버에 있는 거라고 볼 수 있습니다. 이 로직 처리가 어떻게 구성되냐에 따라 P2P, CS, Web 방식인지 분류됩니다.




보통 멀티 플레이어 게임은 세 가지 형태로 나눌 수 있습니다. 도탑전기나 COC 같은 비동기 형 게임, MMORPG와 같이 세계가 지속되는 형태, 매칭을 통하거나 직접 방을 만들어서 게임의 시작과 끝이 명확한 형태인 세션이 있습니다.




팜빌(farmville)과 같은 소셜 게임류가 대표적인 비동기형 게임입니다. 잘 보면 웹 상에서 게임하는 것과 모바일에서 하는 것이 크게 다르지 않습니다.



▲ 웹 방식

웹 방식은 웹 페이지 탐색하는 것과 같은 처리 과정을 거칩니다. 즉, 내가 어떤 요청을 하고 응답을 받는 request-response 방식이기 때문에 게임의 특징이 항상 플레이어의 액션에 응답을 주는 형태로 제작됩니다.

사실, 이 방식의 게임 클라이언트는 웹 브라우저를 다르게 포장하는 것과 같습니다.



▲ 웹 서비스 기반 동기화 방식


웹 기반 게임은 동기화 방식도 간단합니다. 클라이언트에서 요청하면 서버에서 처리하고 결과를 알려주는 방식으로 진행됩니다. 예로 사과를 수확한다고 하면, 탭을 해서 요청하고-서버의 DB에서 처리하고-그 결과를 클라이언트에게 알려주면-클라이언트가 사과 수확 후의 장면을 그리게 됩니다.

단, 이런 방식의 동기화는 간단하지만 웹 페이지와 비슷한 문제를 담고 있습니다. 웹 서핑을 하다 보면 화면 갱신이 늦어서 무효화 되는 경우가 있습니다.

예를 들어, 웹에서 1개 남은 기차표를 예약하려 클릭했는데, 갑자기 느려지고서 확인해보니 이미 누군가 사간 경험을 겪어보셨을 겁니다. 나의 요청이 아닌 누군가의 요청으로 서버에서 변경이 일어나면, DB에서는 실제로 동기화 처리가 되었지만, 내 화면에는 바로 반영이 안 됩니다.

그래서 누군가의 요청에 의해 서버가 먼저 행동한 것에 대해서는 보여주기가 어렵습니다.



▲ 서버리스 아키텍쳐

클라우드 전용 서비스를 활용하면 서버를 직접 구축하지 않고도 웹 기반의 백엔드를 만들 수 있습니다. 이것을 서버리스 아키텍쳐라고 합니다.

게임 클라이언트는 코그니토를 통해 인증을 받은 다음, 게임 콘텐츠나 패치를 다운 받고, 플레이합니다. 플레이 중 발생한 백엔드 서비스 호출은 API GW를 통해서 할 수 있고, 로직의 처리는 람다에서, 게임 데이터 저장은 DynamoDB에 할 수 있는 구조입니다.



▲ 지속형 게임인 WoW

다음으로 지속형 게임의 아키텍처에 대한 설명을 드리겠습니다. 게임 월드가 지속적으로 유지되는 형태의 게임을 말합니다. 대표적인 장르로는 MMORPG, 게임으로는 WoW가 있습니다.

지속형 게임의 경우, 접속자가 1명만 있어도 서버를 내릴 수 없습니다. 즉, 게임 서버가 상태를 항상 유지해야 하므로, Scale-out 및 비용 절감이 쉽지 않고, 이는 비용 효율화가 아주 어렵다는 것을 의미합니다.

지속형 게임의 성능 요구 조건도 까다로운 편입니다. 복잡한 쿼리 및 Transaction(게임 내 거래)이 빈번하기 때문에 RDBMS가 필요합니다. RDBMS의 경우 Scale-out이 쉽지 않기에 DB가 병목되는 경우가 많습니다. 병목 현상을 피하려 1섭, 2섭... 와우로 치면, 아즈샤라 서버와 세나리우스 서버 등으로 나뉘게 되는 것입니다. 또한, 플레이어 간 패킷 방송이 많아서 고성능 네트워크 인터페이스가 필요합니다.



▲ 지속형 게임

지속형 게임은 서버에서 상태를 항상 보관하고 관리해야 하기 때문에, CS 구조를 사용합니다. 클라이언트는 서버를 통해 간접 연결하는 구조고, 중요한 로직은 서버에서 직접 하기 때문에 클라이언트 해킹으로부터 비교적 안전합니다. 단, 서버 구현에 들어가는 난이도와 유지 보수 비용이 높은 편입니다.



▲ 클라이언트 서버 방식의 처리 구조

CS 방식의 로직 처리 과정은, 클라이언트로부터 입력을 받으면 무조건 서버에 보냅니다. 그럼 서버가 입력에 대한 처리를 하고, 시뮬레이션을 하고 결과를 다시 클라이언트에게 방송합니다. 클라이언트는 그제야 서버에서 보내준 결과를 토대로 화면에 렌더링하는 구조입니다.



▲ 클라이언트 서버 동기화 방식

CS 구조에서 주로 쓰는 것은 서버 동기화 방식입니다. 서버 동기화 방식은 먼저, 클라이언트에서 발생하는 이벤트는 무조건 서버로 먼저 보내고, 서버 측에서 로직 계산을 한 다음, 결과를 클라이언트로 방송합니다. 물론, 특정 이벤트는 클라이언트가 아니라 서버가 직접 생성할 수도 있습니다. 특히, 몬스터가 하는 행동은 클라이언트에 소속된 것이 아니기 때문에 서버에서 직접 처리합니다. 위의 이미지는 클라이언트가 서버에 붙어서 게임을 하는 예입니다.

문제는 클라이언트가 서버의 응답을 늦게 받으면, 튕기는 현상이 발생합니다. 이게 흔히 말하는 랙(Lag)입니다. 그러다 어느 순간 죽게 되죠. 이런 경우는 서버 회선이 안 좋을 겁니다.




위 그림은 일반적인 MMOG 아키텍처입니다. 우리나라 개발사들이 가장 잘 만드는 게임 백엔드입니다. 이런 종류의 게임은 하나의 서버 머신에서 동시 접속을 최대로 받는 것이 이슈인데요, 보통은 DB가 병목되기 때문에, 1서버 2서버 3서버 형태로 쪼갭니다. 이런 서버 단위를 렐름(Realm)이라고 합니다.

즉, 렐름별로 각 DB를 유지하기 때문에 다른 렐름에 있는 친구와 게임하려면 해당 렐름에 캐릭터를 새로 만들어야 하는 경우가 일반적입니다. 현재 인기 있는 MMOG는 이 구조가 가장 많습니다.

물론, 고성능 DB를 사용하면 렐름을 특정 군으로 묶을 수 있습니다. 이러면 통합 전장 또는 서버 간 콘텐츠를 즐길 수 있습니다.



▲ 스타크래프트2

다음으로 세션형 게임입니다. 위의 스타크래프트2가 대표적인 세션형 게임입니다. 세션형 게임은 쉽게 말해 방을 만들어 게임하는 형태입니다. 방을 플레이어가 직접 만드는 경우도 해당하고 매치메이킹을 통해 자동으로 만드는 경우도 세션형에 속합니다. 즉, 시작과 끝이 명확한 게임을 의미합니다. 주로 MOBA, FPS, RTS 등 실시간 멀티 플레이어 게임이 세션형 게임입니다.

세션형 게임은 연결 구조에 따라 P2P, 플레이어 클라이언트 중 하나가 서버 역할을 하는 호스트 방식, 전용 서버를 따로 두는 데디케이티드 서버 형태로 나뉩니다.

동기화 방식 또한 이전에 설명한 서버 동기화 방식을 많이 쓰지만, RTS 장르의 경우, Lock-Step 계열의 동기화를 많이 사용합니다.



▲ P2P 방식

P2P 방식은 서로 간 직접 연결하기 때문에, 따로 서버가 필요 없고 반응성이 빠릅니다. 과거 FPS나 RTS 게임류가 많이 사용한 방식입니다. 다만, 각각의 클라이언트의 정보가 다른 모든 클라이언트에게 공유하는 방식이기 때문에 맵핵, 헬퍼와 같은 해킹에 취약합니다. 게임에 참여하는 클라이언트 수가 늘어날수록 연결 수가 제곱으로 늘기 때문에 확장성에도 제약이 있습니다.



▲ 호스트 방식

호스트 방식은 플레이어 클라이언트 중 하나가 서버 역할까지 하는 방식입니다. 이러한 서버를 수퍼피어라고도 합니다. 보통 방장 역할을 하는 플레이어 컴퓨터가 호스트가 되는 것이 일반적입니다. 때문에, 방장의 컴퓨터 성능이 게임에 영향을 미칩니다.



▲ 데디케이티드 방식

마지막은 데디케이티드 방식입니다. P2P나 호스트 방식은 온라인 게임 형태로 서비스하기 위해 변형된 형태로, 실제 클라이언트 중 하나를 게임 서버로 사용하는 개념입니다. 물론, 호스트 방식처럼 실제 플레이어가 사용하는 클라이언트가 아니라 렌더링 기능이 빠진 클라이언트를 게임 회사의 IDC나 클라우드에 올려서 서버로 사용하는 겁니다. 일반적으로, 몬스터와 같은 NPC 처리도 데디케이티드 서버에서 처리합니다. 게임 상태의 전달은 데디케이티드 서버를 통해 전달하는 구조를 많이 사용합니다.

이 방식은 LoL이나 오버워치 같은 대부분의 세션형 온라인 게임에서 사용하는 방식입니다.

위의 이미지는 하나의 세션에 4명이 플레이하는 경우입니다. 보이지 않는 플레이어가 데이터 센터에서 서버 역할을 하고 있다고 이해하실 수 있습니다. 예를 들어오버워치의 경우 12명이 한 방에서 게임을 즐기는데, 보이지 않는 플레이어가 하나 더 있어서, 이 플레이어가 데이터 서버에서 동작한다고 볼 수 있습니다.



▲ Lock-Step 방식

위 이미지는 Lock-Step 방식의 동기화입니다. 보통 네트워크 게임에서 동기화 방식은 크게 2가지 방식을 쓰는데, 하나는 Lock-Step과 이전에 설명한 서버 동기화입니다. Lock-Step 방식의 경우, 상대로부터 주기적으로 이벤트를 받고, 이벤트가 다 모이면 처리하는 방식입니다. 각각의 클라이언트는 모두 위와 같은 형태의 큐를 유지합니다.

만일, 해당 라운드에 이벤트가 없더라도 Beacon 신호는 라운드 단위로 교환해야 상대 클라이언트를 기다리지 않고 진행할 수 있습니다.



▲ 드디어 문제의 원인을 정확히 알았다

위의 상황처럼 게임 도중 상대방을 기다리는 상황이 나오면 Lock-Step 류의 동기화를 쓰는 게임이라고 볼 수 있습니다. 다 같이 동기화를 쓰는 방식이기 때문에, 하나라도 늦으면 기다려야 합니다. 즉, 가장 처리가 느린 클라이언트 기준으로 동기화가 이루어집니다.


데디케이티드 서버

그럼, 세션형 게임에서 가장 많이 쓰이는 데디케이티드 서버에 대해 설명하겠습니다.




위 이미지에서 눈치챘겠지만, 데디케이티드 서버 방식은 사실상 CS 구조입니다. 물론 P2P에서 많이 쓰이던 Lock-Step 방식의 동기화가 가능하지만, 요즘은 안전하게 서버에서 로직을 돌리는 서버 동기화 방식을 주로 사용합니다.

데디케이티드 방식과 그냥 게임 서버를 따로 만드는 것은 무엇이 다를까요?

바로, 게임 서버를 직접 구현하지 않고 프로그램을 서버 모드로 사용하는 점이 다릅니다. 하나의 클라이언트 프로세스가 그대로 데디케이티드 서버로 동작하는 것이고, 이 데디케이티드 서버는 하나의 게임 세션을 처리하게 됩니다.

이 방식의 장점은 클라이언트의 지형, 애니메이션, 물리 정보를 그대로 사용할 수 있기 때문에 화면에 렌더링만 하지 않을 뿐, 클라이언트 엔진 레벨의 정교한 시뮬레이션이 가능합니다.




배틀그라운드에서 프라이팬에 총알을 튕길 수준의 정교한 동기화는 클라이언트 직접 서버로 사용하는 데디케이티드 서버 방식이기 때문에 가능합니다.


데디케이티드 서버를 어떻게 만들 수 있는가?

클라이언트에서 렌더링만 끄고 그대로 서버로 쓰는 개념이기 때문에, 대부분의 상용 엔진에서 쉽게 만들 수 있는 방법을 제공합니다. 게임 엔진 내 복제 프레임워크(Replication framework)라고 불리는 네트워크 동기화 라이브러리를 통해서 만들 수 있습니다.

복제 프레임워크가 해주는 대표적인 기능은, 특정 객체의 멤버 변수를 네트워크 상에서 자동으로 동기화하거나, 네트워크상의 다른 클라이언트의 함수를 호출해주는 RPC 같은 것입니다.

럼버야드 그리드메이트(Lumberyard GridMate)는 원격 복제를 지원하는 크로스플랫폼 네트워크 라이브러리입니다. 특징으로는 네트워크상에서 원격지 복제본(replicas)을 지원하고, 하나의 노드가 복제본을 소유하고 나머지 노드는 프록시를 보는 구조입니다. 복제본은 데이터를 포함하고 RPC를 수행할 수 있습니다.

또한 Windows, Xbox One, PS4, Android, iOS 등 다양한 플랫폼을 지원합니다. 또한 변수 동기화, 변수 변경 시 자동 콜백 호출 등 다양한 동기화 기능을 제공하고 네트워크 테스트 및 시뮬레이션을 위한 RTT 변경, 패킷 손실 등의 다양한 기능을 제공합니다.

사용 코드는 아래와 같습니다. 코드 부분을 보시면 알겠지만, ReplicaChunk라는 클래스를 상속받은 후에 멤버 변수로 DataSet 타입을 사용하면, 이 멤버 변수는 네트워크상에서 자동으로 동기화됩니다.





Unreal Engine Dedicated Server




당연히 언리얼 엔진도 Actor 간 상태 동기화를 쉽게 제공합니다. 그리고 이런 기능을 쉽게 데디케이티드 서버로 빌드해서 사용할 수 있습니다. 언리얼 엔진에서 가이드를 제공하니 참고하시면 좋습니다.

Dedicated Server Guide (바로 가기)
Actor Replication (바로 가기)


Unity UNET

유니티의 경우도 5.1 버전부터 도입된 UNET 라이브러리가 복제 프레임워크 역할을 합니다. UNET은 크게 2가지 방식, High Level API와 Transport API 방식을 지원하는데, HLAPI가 복제 프레임워크 기능에 해당되고 Transport API가 네트워크 라이브러리 기능에 해당한다고 보면 됩니다.

HLAPI는 NetworkManager 콤포넌트를 통해 객체의 상태 복제를 지원하는데, 클라이언트의 플레이어가 서버의 함수를 호출하는 커맨드 기능과 서버가 클라이언트 함수를 호출하는 RPC 기능을 통해 손쉽게 네트워크 이벤트를 주고받을 수 있습니다.

물론 Transport API를 통해 기존의 익숙한 방식인 연결을 맺고 데이터를 직접 주고 받은 식으로 사용할 수도 있습니다. 참고로 내부적으로는 Reliable UDP 기반으로 동작합니다.


복제 프레임워크 사용 방법

게임 엔진마다 복제 프레임워크 사용 방법은 엔진 별로 다 다릅니다만, 개념적으로 거의 비슷하니 가장 쉬운 엔진인 Unity를 기준으로 설명하겠습니다.




UNET도 이전 설명한 호스팅 모드와 정확히 같은 개념을 사용합니다. 즉, 네트워크 매니저 콤포넌트를 이해하기 위해서는 호스트와 서버의 차이에 대한 개념부터 알아야 합니다.

호스팅 모드에서 호스트는 플레이어 클라이언트가 서버 역할을 한다고 했습니다. 구체적으로 살펴보면 플레이어 클라이언트의 호스트 프로세스 안에 서버 역할을 하는 콤포넌트가 따로 있는 겁니다.

즉, 호스트 안에 있는 클라이언트를 로컬 클라이언트라 하고 서버와는 내부적으로 메시지 큐를 통해 통신합니다. 호스트 밖에 있는 클라이언트를 리모트 클라이언트라 하고 네트워크를 통한 통신을 합니다. 로컬-리모트 클라이언트는 코드 레벨에서는 같습니다.



▲ Player Authority

복제 프레임워크를 사용하기 위해서는 객체에 대한 오너십을 누가 갖고 있는지 정하는 게 중요합니다. Authority는 실제 그 개체의 상태를 변경할 수 있는 권한을 의미합니다.

플레이어 객체에 대한 권한은 각 클라이언트가 갖고 있습니다. 즉, 클라이언트 별로 내 캐릭터, 내 플레이어라는 개념이 있습니다. isLocalPlayer가 세팅된 경우만 내가 명령을 내려서 객체의 상태를 변경할 수 있습니다. 이 명령은 서버를 통해 다른 클라이언트에게 전달되는 구조입니다.



▲ NPC Authority

반면, NPC는 서버가 권한을 갖고 있습니다. 서버가 해당 NPC에 대한 권한을 갖고 제어해, 그 결과를 클라이언트에게 방송하는 구조입니다. 유니티5.2부터는 클라이언트도 이런 NPC에 대한 권한을 갖는 방법이 생겼지만, 잘 쓰지는 않습니다.

복제 프레임워크에서는 내가 서버인지 클라이언트인지 아는 것이 중요합니다. 유니티의 경우 isServer, isClient, isLocalPlayer와 같은 프로퍼티를 제공하기 때문에 내가 어떤 역할인지 코드 레벨에서 바로 파악할 수 있습니다. 코드는 다음과 같습니다.

isServer - true if the object is on a server (or host) and has been spawned.
isClient - true if the object is on a client, and was created by the server.
isLocalPlayer - true if the object is a player object for this client.
hasAuthority - true if the object is owned by the local process


UNET HLAPI 사용 방법

메인씬에서 네트워크 콤포넌트를 부착하고 접속 받을 포트 번호, 플레이어에 해당하는 프리펩, 서버에서 스폰해줘야 할 프리펩에 관련된 것을 설정합니다. 그리고, 네트워크 동기화가 필요한 엔티티에는 Network Identity 및 Network Transform 콤포넌트를 부착합니다.

마지막으로 서버인지 클라이언트인지 역할에 따라 해당 시작 함수를 호출하면 됩니다. 시작 함수는 startHost(), startServer(), startClient()입니다.




위 이미지 중 왼쪽은 네트워크 매니저 콤포넌트 설정 화면 예시입니다. 포트 번호, 플레이어에 해당하는 프리펩 등을 설정합니다. 그리고 동기화가 필요한 엔티티마다 오른쪽에 보이는 network identity와 transform을 부착해야 합니다.



▲ 상태 복제

코드 레벨에서 상태 복제는 대상이 되는 스크립트를 MonoBehavior가 아닌 NetworkBehavior로부터 상속받아야 합니다. 그래야 멤버 변수에 대한 상태 복제 및 원격지의 멤버 함수 호출이 가능합니다.

멤버 변수 위에 SyncVar 속성을 부여하면 해당 변수는 네트워크 상에서 자동으로 동기화 됩니다. 오른쪽 코드 예시처럼 해당 변숫값이 바뀔 때마다 특정 함수가 호출되도록 설정할 수도 있습니다.



▲ Command

클라이언트가 데디케이티드 서버의 함수를 호출하고 싶을 때는 Command 속성을 부여하면 됩니다. 위의 코드에서 CmdFire() 멤버 함수는 클라이언트에서 호출하지만 실제로 실행은 서버에서 됩니다.



▲ ClientRPC


반대로, 데디케이티드 서버가 클라이언트의 멤버 함수를 호출할 때는 ClientRpc 속성을 부여하면 됩니다. 위의 예시 코드에서 RpcSpawn() 함수를 호출할 경우 클라이언트에서 실행됩니다.




지금까지 설명 드린 내용을 전체적인 그림으로 살펴보면 다음과 같습니다. 보시다시피 클라이언트와 서버상의 Objects가 서로 복제되어 동기화되는 구조입니다.

플레이어의 클라이언트가 서버에 접속하여 명령을 내리면 서버상의 플레이어 객체에 반영이 되고, 서버상의 플레이어 객체가 NPC와 같은 다른 객체의 상태를 변경하게 되면, 그것이 연결된 모든 클라이언트의 복제된 객체에게 반영되는 구조입니다.

즉, 따로 서버를 구현할 필요 없이 클라이언트 구조 그대로 서버로 활용 가능하게끔 하는 것이 UNET과 같은 복제 프레임워크의 가장 큰 역할입니다.


Unity로 데디케이티드 서버 만들기 바로 시작

UNET을 사용한 초 간단 슈팅 게임 튜토리얼(바로 가기)을 바로 만들어 볼 수 있습니다. 단, 이렇게 만들어진 클라이언트 중 하나를 데디케이티드 서버로 사용하기 위해서는 실행 시 batchmode 옵션을 주어 headless 모드로 동작되도록 해야 합니다. headless 모드란 input/output을 끄는 기능으로, 어떠한 입력도 받지 않고 렌더링을 꺼버려서 디스플레이로 출력도 하지 않고 백그라운드 프로세스로 동작하도록 하는 모드입니다.


GameLift를 통한 DevOps!




DevOps는 문자 그대로 개발과 운영의 통합을 의미합니다. 위 그림은 개발부터 운영까지의 사이클을 나타냅니다. 계획-개발-테스트-배포-운영-모니터링을 하고, 모니터링 결과로부터 수정 계획을 하고 다시 개발하는 반복의 과정입니다.

게임의 경우, 플레이어들로부터 피드백 반영 주기가 빠르면 업데이트와 혁신도 빠르게 이루어집니다. 그래서 개발과 운영을 따로 분리하지 않고 통합함으로써 이 혁신을 이룰 수 있습니다.

이런 DevOps 환경을 위해서는 클라우드가 필수입니다. 이유는 예전처럼 일일이 서버 머신을 확보하고 패치, 업데이트하는 방식으로는 피드백 반영 주기가 길어지고, 그만큼 혁신의 속도가 느려지면 플레이어에게 외면당하게 됩니다.

이것은 시장에서 살아남기 힘들어짐을 의미합니다. 클라우드는 클릭 몇 번으로 새로운 서버를 확보-배포-운영하고 모니터링 할 수 있는 방법을 제공합니다.


Amazon GameLift?




GameLift는 세션형 멀티 플레이어 게임을 쉽게 DevOps 할 수 있도록 도와주는 클라우드 서비스입니다. GameLift의 가장 중요한 가치는 자동화된 서버 관리와 스케일링으로 비용의 최적화, 글로벌 플레이어를 대상으로 매치 메이킹 제공, DDoS 방어 등이 있습니다.

즉, 게임 서버 바이너리만 만들면 그 아래의 일들은 거의 신경 쓰지 않아도 되는 서비스라고 보시면 됩니다.


GameLift를 사용하는 가장 큰 이유: 스케일링




보통 멀티 플레이어 게임의 경우, 주말이나 저녁 시간 때는 많은 플레이어가 게임을 하고 새벽이나 아침에는 그 반대입니다.




그런데, 이런 멀티 플레이어 게임을 위해 서버를 중간 정도로 확보하면, 피크 시간대의 사용자들을 커버하지 못하게 됩니다. 이것은 대기열로 이어지고, 곧 수익 감소를 의미합니다.




서버를 너무 충분하게 확보하면, 플레이어들의 경험은 좋아질 수 있지만 회사 입장에서는 서버 인프라 비용 낭비가 심해지게 됩니다.




GameLift는 플레이어 접속 상황에 따라 서버가 탄력적으로 확장-축소되기 때문에 선투자 없이 사용량에 따라 비용 최적화가 가능합니다.


Fleet?




이게 어떻게 가능한지 인프라, 서버 머신 레벨에서 설명하겠습니다. GameLift에는 플릿(Fleet)이라는 개념이 있습니다. 이것은 데디케이티드 서버 바이너리가 배포된 EC2 인스턴스의 집합체라고 볼 수 있습니다.

즉, 게임 서버 빌드로부터 플릿상의 EC2 인스턴스에 게임 서버 프로세스를 올릴 수 있습니다. 그리고 플레이어는 플릿에 접속해서 게임을 하게 됩니다.




플릿의 구조를 좀 더 자세히 보겠습니다. 플릿의 인스턴스들이 데디케이티드 서버 프로세스들을 돌리는 구조고, 실제로 이 프로세스들은 게임 세션을 호스팅합니다. 플레이어가 플릿에 접속하면, 사용 가능한 게임 세션을 찾아서 플레이어 슬롯을 예약하고, 그 슬롯에 할당되는 구조입니다.

사용자는 EC2 인스턴스의 상태에 대해서 몰라도 됩니다. 플릿에 빈방이 있는지 요청하고 어느 게임방으로 들어가면 될지 요청만 하면 됩니다. 게임 세션이 더 필요하다면, GameLift가 하단의 인스턴스들을 자동으로 scale-out 하도록 설정하면 됩니다.


GameLift 사용법




GameLift는 사용법도 상당히 간단합니다. 게임 엔진으로 만든 데디케이티드 서버 바이너리를 올리고, 설정하고, 스케일링을 적용하면 바로 운영할 수 있습니다. 즉, 세션형 실시간 멀티플레이어 게임을 클라우드에서 손쉽게 배포-실행-운영할 수 있습니다.




구체적으로 살펴보면, 우선 데디케이티드 서버 빌드를 만듭니다. AWS CLI를 통해서 GameLift에 업로드 하면 위 화면처럼 업로드된 빌드를 확인할 수 있습니다. 배포 버전 관리를 할 수 있음을 의미합니다.




빌드로부터 플릿을 생성하기 위해서는 EC2 인스턴스 타입 지정, 실행 파일 이름, 명령 파라미터, 프로세스 수 지정, 포트, 프로토콜 종류, 허용 IP 대역 지정, Auto-Scaling 정책을 설정합니다.

다음으로 데디케이티드 게임 서버 실행 파일 이름과 실행 인자, 프로세스 수를 설정합니다. 예로 2개의 프로세스를 실행하도록 설정한다면, 하나의 인스턴스에 2개의 게임 세션을 호스팅하게 됩니다. 각자의 게임 서버 워크로드에 따라 다수의 프로세스를 실행하도록 해도 됩니다. 그리고 게임 클라이언트로부터 접속을 받을 포트 번호와 프로토콜, IP 대역을 지정해야 합니다.

마지막으로, 스케일링 정책을 설명하면 됩니다. 예를들어 플레이어 세션이 60분 동안 1 미만이면 인스턴스를 1대로, 활동중인 게임 세션이 20분간 3개 이상이면 인스턴스 하나 추가, 플레이어 세션이 30분 동안 10개 이하면 인스턴스 15% 감소 등 원하는 만큼 정책을 추가할 수 있습니다.

마쳤다면 이제 플릿이 생성됩니다. Active 상태인 플릿은 바로 사용 가능함을 의미합니다. 즉, 게임 서비스가 시작되었음을 의미합니다.


GameLift SDK 연동(DEV)

게임 엔진으로 만든 데디케이티드 게임 서버가 GameLift와 통합하기 위해서는 어떻게 해야 할까요? 바로, 게임 엔진 별 전용 플러그인 또는 GameLift Server SDK를 사용해서 GameLift와 연동해야 합니다.

GameLift Server SDK는 게임 서버와 GameLift 서비스의 통신을 위해 제공되는 전용 SDK입니다. GameLift 클라이언트 API는 AWS SDK를 그대로 쓰면 됩니다.




파란 원은 클라이언트가 호출하는 API, 붉은 원은 게임 서버가 호출하는 API, 녹색 원은 GameLift가 게임 서버로 콜백하는 부분입니다. 세로축을 보면, 서버 초기화의 경우 게임 서버에서 ProcessReady를 호출하면 됩니다. 게임 시작을 위해서는 클라이언트에서 게임 세션 생성을 요청하고, GameLift가 콜백으로 게임 서버에 방 생성 요청을 하게 됩니다.

그러면 게임 서버는 ActivateGameSession API 호출을 통해 게임 세션이 준비되었음을 알리면 됩니다.




위 이미지는 같은 방식으로 플레이어가 추가, 삭제 및 게임 종료에 따라 어떤 식으로 API 호출을 하면 되는지를 보여줍니다. 즉, 몇가지 API 호출만 적절히 하면, 여러분의 게임과 GameLift가 쉽게 연동됩니다. 이렇게 연동된 게임 서버 빌드를 앞서 설명한 방법대로 GameLift 서비스에 올려서 바로 게임 서비스를 시작할 수 있습니다.


GameLift를 통한 운영(Ops)

운영 모니터링 도구를 구현하는 것도 상당히 귀찮은 일이 많습니다. GameLift의 경우 웬만한 정보는 웹상에서 바로 모니터링 할 수 있는 체계를 지원합니다. 물론, GameLift API 콜을 통해서도 각종 상태 확인 및 제어할 수 있습니다.




웹을 통해서도 스케일링 상태, 게임 세션이나 플레이어 동시 접속 수, 하드웨어 사용 상황 등 각종 세부 정보를 그래프 형태로 확인 가능합니다. 또한, 게임 세션 내 플레이어에 대한 정보도 구체적으로 확인할 수 있습니다. 언제 접속하고 끝냈는지, 얼마나 게임을 했는지, 기타 플레이어 정보를 커스터마이징 해서 확인할 수 있는 방법을 제공합니다.

그리고 GameLift는 최근 전용 매치메이킹 기능도 제공하기 시작했습니다. 즉, 매치메이킹 기능을 따로 구현할 필요가 없습니다. 무료로 제공되는 FlexMatch 기능을 사용하면, 지정한 여러 조건(ELO 점수 범위 등 지정)에 맞는 플레이어들을 자동으로 찾아 게임 세션 생성은 물론 배치까지 시켜줍니다.

전 세계에서 요청이 들어올 경우, 가까이 있는 사람을 모아, 가까이 있는 서버에 연결해 매치 시켜줍니다.




GameLift의 가장 큰 장점 중 하나는 AWS 글로벌 인프라를 쉽게 활용할 수 있다는 점입니다. 원 빌드 글로벌 서비스(one build global service)가 클릭 몇 번으로 가능합니다.




지금 당장 구현된 기능을 포함해서, 앞서 말한 핵심적인 가치(스케일링 자동화, 글로벌 저 지연 경험 제공, DDoS 방어 등)와 앞으로 나올 기능이 함께 갈만하다고 여기시면, 당장 비용이 들더라도 통합하기를 강력히 추천합니다.

GameLift도 무료로 테스트해볼 수 있는 프리티어를 제공합니다. 게임을 따로 만들 필요 없이 바로 테스트해 볼 수 있도록 8인용 물리 기반 축구 게임을 제공합니다. GameLift 콘솔에 가셔서 샘플 게임 항목을 선택하면, 바로 해볼 수 있습니다.

c3.large 인스턴스 타입으로 빠르게 테스트할 수 있는 샘플 게임 제공
GameLift 코드 레벨 사용법 및 데모 더미 플레이어 테스트용
Full-Stack 게임 샘플 (실제 세션형 게임 서비스와 기술 기능적으로 동일)




또한, 개발자 입장에서 GameLift를 사용한 게임 구현을 어떻게 하는지 확인할 수 있도록 풀스택 게임 샘플도 제공합니다. 물론, 운영자 입장에서 이를 통해 GameLift를 사용하는 게임의 백엔드 시스템을 새로 구축해볼 수 있는 문서도 제공합니다.

댓글

새로고침
새로고침

기사 목록

1 2 3 4 5