▲ 에이스프로젝트 안현석 디렉터

[인벤게임컨퍼런스(IGC) 발표자 소개] 에이스프로젝트 개발팀 디렉터로 재직 중인 안현석 디렉터는 과거 넥슨에서 팀장, 실장을 거치며 10년 가량 개발팀에서 클라이언트 프로그래머로 게임을 제작한 실력자다. 넥슨에서는 모바일 메이플스토리 시리즈를 개발했으며, 현재는 에이스프로젝트에서 신규 프로젝트에 매진 중이다.

모바일 게임 개발에 있어서 유니티 엔진은 뭐든 가능한 만능툴로 인식되곤 한다. 어지간한 기능들은 유니티 엔진으로 만들 수 있을 정도다. 하지만 대부분 클라이언트의 영역으로만 알고 있다. 서버의 경우는 얘기가 다르다. 이미 검증된 서버 엔진을 이용하는 게 대부분이다.

그러나 오늘 IGC 강연에 나선 에이스프로젝트의 안현석 디렉터는 달랐다. 유니티 엔진이 지원하는 기능만으로도 서버를 만든 것이다. 강연에 나선 안현석 디렉터는 왜 유니티로 서버를 만들었는지 그 궁금증에 답하는 한편, 어떻게 서버를 구현했는지 그간의 성과를 소개했다.



■ 강연주제: 유니티로 실시간 멀티플레이 게임서버를 만들 수 있을까?

⊙ 나는 클라이언트 프로그래머

우선 안현석 디렉터는 자신이 클라이언트 프로그래머라는 걸 분명히 했다. 피쳐폰 시절부터 모바일 게임을 개발했지만, 그는 줄곧 클라이언트 프로그래머였다. 서버 개발 경험이 없던 건 아니었지만, 단순한 웹기반 랭킹서버였고 VB(비쥬얼베이직)이나 C#으로 소켓 서버를 개발한 정도였다.

클라이언트와 서버. 프로그래머라는 큰 틀에서는 같지만, 세부적으로는 큰 차이가 있다. 그럴진대 그는 왜 유니티로 서버를 만든 걸까?


⊙ 실시간 게임서버를 만들어야 한다


서버를 만들게 된 이유 첫 번째는 바로 회사에서 필요했기 때문이다. 5월 에이스프로젝트의 신작이 KOCCA 지원사업에 선정됐다. 가위바위보를 기반으로 한 실시간 카드배틀 야구게임인데 1:1 실시간 대전을 기반으로 하는 만큼, 서버가 필요했다.

두 번째는 당시 서버 팀의 인력이 부족한 상황이었기 때문이라고 안현석 디렉터는 말했다. 결국 누군가는 나서야 했는데 관심도 있다보니 그가 지원했다는 것이다. 결국, 이런 이유로 인해 클라이언트 프로그래머였던 안현석 디렉터가 서버를 만들게 됐다.

본격적인 개발에 앞서 안현석 디렉터는 우선 어떤 게임인지, 어떤 점에서 서버가 필요한지를 명확히 했다. 에이스프로젝트의 신작은 '클래시로얄'과 유사한 실시간 매칭을 통한 1:1 PvP 대전 게임으로 가위바위보를 기반으로 한 야구게임이다. 기본적으로는 턴 베이스 기반으로 실시간 관전과 리플레이 기능을 구현해야 했다.

어떤 부분에서 서버가 필요한지 파악하자 다음으로는 어떤 서버 엔진을 쓸지에 대한 고민이 있었다. 보통 시장에는 두 가지 형태로 서버 엔진이 나뉜다. GBaaS(Game Backend-as-a-Service)와 설치형 게임서버 엔진인데 두 방식의 장단점은 명확했기에 더욱 고민이 컸다고 안현석 디렉터는 밝혔다.


대표적으로 GBaaS는 기본적인 기능을 전부 제공하기에 서버를 구현하기 쉬운 반면, 새로운 기능을 추가하는 등의 커스터마이징이 어렵고 사용량 또는 CCU에 따라 이용금액이 달라진다는 단점이 있다. 설치형 서버 엔진은 반대로 일일이 다 만들어야 하지만 언제든 원하는 기능을 추가할 수 있으며, 엔진 라이센스 비용만 지불하면 된다는 점이 장점이었다.

고민 끝에 안현석 디렉터는 설치형 게임서버 엔진으로 서버를 구현하기로 결정했다. 하지만 설치형 게임서버 엔진이라고 해도 한 두개가 아니었다. 포톤, 프라우드넷, 아이펀 등 다양한 서버 엔진이 존재했기 때문이다. 여기에 각각 하나씩 아쉬움이 있는 게 발목을 잡았다.

그러던 중 유니티로 서버를 만들 수 있단 소식을 듣게 됐다. 어차피 클라이언트도 유니티인데 서버도 유니티로 만들면 더 좋지 않을까 하는 생각과 익숙한 C#을 사용한다는 점과 맥, 윈도우, 리눅스를 가리지 않고 서버 배포가 가능하단 점, 유니티에 포함돼있어서 무료란 점 등으로 인해 유니티 UNet을 이용해 서버를 만들기로 결정했다.


⊙ 유니티로 게임서버를 만들어보자


유니티로 게임서버를 만들기에 앞서 유니티에서 제공하는 네트워킹 API를 알 필요가 있다고 안현석 디렉터는 밝혔다.

다행히 유니티는 HLAPI(하이레벨 API)와 LLAPI(로우레벨 API)를 둘 다 제공해 고수준에서 저수준까지 거의 모든 네트워킹 API가 사용 가능했다. 여기에 각종 네트워킹 컴포넌트로 에디터에서 멀티플레이 게임을 손쉽게 개발할 수 있으며, 추가로 제공하는 멀티플레이어 서비스를 통해 매치 메이킹, 릴레이 서버를 클라우드 방식으로도 쓸수 있었다. 유료였지만, 그만큼 지원하는 서비스가 다양한 점은 여러모로 장점이었다.

하지만 에이스프로젝트의 신작은 턴 베이스 카드 배틀 게임이었기에 굳이 실시간 P2P 방식일 필요도, 실시간일 필요도 없었다.매치 메이커, 방, 턴 베이스 게임 플레이가 가능해야 하며, 여기에 AI 봇 플레이어, 실시간 관전, 리플레이 기능만 있어도 충분했다.


그 결과 신작에는 LLAPI를 사용해 클라이언트 - 서버 방식의 멀티플레이가 가능하도록 기획, 개발했다. 로우레벨 NetworkServer와 NetworkClient 클래스를 직접 사용하는 방식으로 원하는 형태로 클라이언트 접속 관리가 가능했는데 Transport Layer API도 쓸 수 있어서 꽤 많은 부분을 로우레벨로 개발할 수 있었다.

시작은 서버를 만드는 일이었다. 유니티에서는 NetworkServer.Listen (serverPort)로 서버포트를 만드는 등 몇 줄의 코드만으로도 서버를 열 수 있다. 이렇게 서버를 만들면 NetworkClient에서 서버에 접속해야 한다. 해당 서버주소와 포트로 서버에 접속 요청을 하는 식으로 이 역시 몇 줄의 코드만으로 가능했다.

서버를 만들고 접속하는 작업이 끝나자 다음으로는 메세지를 전송하는 테스트가 이뤄졌다. UNet에서는 사용자 정의 메세지를 날리기 위해선 메세지 클래스를 작성해야 한다. 이때 클래스 자체를 새로 생성해 메세지를 날리면 자동으로 Serialize 함수를 생성하는데, 컬렉션이나 복잡한 클래스 등에서는 자동으로 Serialize 함수를 생성하지 않아 오버라이드해서 수동으로 해야 했다.


서버와 클라이언트를 만들고 접속 테스트 다음으로는 메세지 전송을 테스트했다. 메세지를 보내기 위해선 시스템에서 정의한 메세지 타입보다 큰 값을 정의할 필요가 있다. 이렇게 메세지 타입까지 정의하고 나면 메세지를 보낼 수 있는데 클라이언트에서 메세지를 보내면 Serialize를 자동으로 해서 서버 쪽으로 전달된다. 단, 이때 주의해야 할 점이 있다. 클라이언트 입장에서는 무조건 서버로 보내니까 누구에게 보내는지는 큰 상관이 없지만, 서버는 다르다. 어떤 클라이언트에게 보낼지 명확히 인지해야 한다. 이 때문에 서버에서 클라이언트로 보낼 때는 connetionId로 누구에게 보내는지 구분했다.

메세지를 보냈으니 다음으로는 받는 작업이 필요했다. 메세지를 받기 위해선 RegisterHandler를 등록해 해당 메세지를 받으면 등록된 함수가 호출되는 방식이었다.

이상으로 간단한 서버와 클라이언트 연동을 끝냈지만, 아직 더 작업할 부분이 남았다. 확장성이 필요했다. 신작에는 작전카드가 100여 종이 존재하는데 일일이 하드코딩하기엔 힘들고 시간도 오래걸렸다. 즉, 편하게 업데이트할 수 있도록 작전카드 제작을 위한 스크립트가 필요했다. 그래서 유니티용 자바스크립트 인터프리터인 Jint를 탑재하기로 했다. 그 결과, 각종 작전카드를 기획자가 스크립트화해서 작업할 수 있게 됐다.


현재 유니티로 만든 게임서버는 기본적인 1:1 대전 플레이가 가능하고 데이터베이스 연동은 자체 API 서버를 이용하고 있다. 이제는 서버 분산 작업에 착수할 단계에 까지 와 있다. 그래도 아직까지는 큰 문제 없이 개발 중이다.


⊙ UNet 소소한 팁

서버 구현에 대한 이야기에 이어서 안현석 디렉터는 그간 UNet을 직접 사용하면서 얻은 팁들을 공유했다. 첫 번째는 유니티가 클라이언트 엔진이기 때문에 발생하는 문제에 대한 거였다. 앱이 비활성화되면 멈추곤 했는데 Run In Background를 체크하면 이 같은 문제를 미연에 방지할 수 있다고 설명했다.


아울러 복잡한 클래스, 컬렉션은 Serialize 되지 않는 문제가 있다고 했는데 이를 해결하기 위한 방법이었다. 바로 JSON을 사용하면 된다. 메세지 클래스를 JSON으로 Serialize 하고 데이터를 전송하도록 한 거였다. 간단히 말하자면 네트워크와 서버 중간에 JSON을 집어넣은 것으로 패킷 용량은 커졌지만, 턴 베이스인 만큼 실시간으로 패킷을 쏘는 게 아니어서 큰 문제도 없었고 Serialize 문제를 해결할 수 있어서 이 방법을 지금도 사용 중이다.

동시 접속자가 10명 이상이면 에러가 발생하기도 했는데 이는 유니티 서버 자체가 P2P를 기반으로 했기에 발생한 문제였다. 이를 해결한 방법은 간단했는데 maxConnecions 숫자를 디폴트인 10에서 1000으로 임의로 변경해 해결할 수 있었다. 단, 이때 NetworkServer를 수정했으면 NetworkClient의 maxConnecions도 동일해야 한다.


끝으로 안현석 디렉터는 유니티 UNet으로 서버 개발을 고려하는 개발자들을 위한 조언을 남겼다. "유니티 UNet 서버는 원래부터 대규모 멀티플레이어 환경에 적합한 서버가 아니기에 네트워크 속도에 영향을 받지 않으며, 방 기반의 턴 베이스 게임에 적합하다"며, 자신이 개발하고자 하는 게임의 장르, 시스템에 따라 어떤 서버를 쓸 지 고려하는 게 좋다고 말하며 강연을 끝마쳤다.