brunch

You can make anything
by writing

C.S.Lewis

by 이기곤 May 11. 2019

0x02. 게임 서버 프레임워크 기능 명세 2

시스템 명세 다루기

지난 글에서 게임 관련 명세를 다뤘다면, 이번에는 이 명세들을 구현하는 데 있어서 필수적인 시스템 관련 명세를 다뤄보려 한다. 마찬가지로 우선 순위는 이번 글에서 결정하지 않으려고 한다. 일단 명세들을 쭉 나열한 후 우선 순위를 결정할 것이다.


1. 컴포넌트 기능(ECS, Entity Component System)


수 많은 클라이언트와 서버 게임 엔진은 모두 컴포넌트 패턴을 기반으로 동작한다. 여기서 컴포넌트란 하나의 기능을 수행하는 작은 모듈이라고 보면 된다. 클라이언트 엔진을 예로 들면 렌더링을 담당하는 컴포넌트, 렌더링에 필요한 값들을 계산하는 컴포넌트, 유저의 입력을 받는 컴포넌트 등으로 엔진의 기능들을 분리한다.


컴포넌트 패턴은 모듈들을 하나의 프로세스 안에서 관리하는 구조다. 즉, 컴포넌트의 설치, 시작, 중단,  종료 등을 프로세스가 관리하는 것이다. 라이프사이클을 관리해야 하는 이유는 컴포넌트를 개발할 때 다시 이야기할 것이다.


오늘날 수 많은 서비스들은 컴포넌트 패턴 대신 [마이크로 서비스 아키텍처(Microservice Architecture) 패턴을 사용한다. 이 패턴은 각 모듈들을 독립적인 프로세스/서버로 분리한 후, Restful API로 서로가 통신하는 구조다. 쉽게 서비스를 확장할 수 있고, 코드 유지보수도 모듈 단위로 하기 때문에 관리가 쉽다.

그림 1 - 마이크로 서비스 아키텍처 (출처: https://microservices.io/)


게임 서버도 마이크로 서비스 패턴을 사용하지만, 게임 서버 내부에서 독립적으로 수행하는 작업들이 많기 때문에 컴포넌트 패턴은 반드시 필요하다. 참고로 1편에서 설명했던 메시지 처리, 리더보드, 계정 관리, 로직 관리 기능들은 모두 독립적인 컴포넌트로 동작한다.



2. 작업 관리 컴포넌트(스레드 풀 및 요청 작업 관리)


게임 서버는 CPU 자원을 효율적으로 사용해야 한다. 수 많은 유저들의 요청을 동시에 처리해야 하고, 동시에 처리할 수 있는 유저 수는 게임 서버가 CPU 자원을 효율적으로 쓸 수록 증가하기 때문이다.


작업 관리 컴포넌트는 컴포넌트 설치 시점에 고정된 개수의 스레드를 생성한 다음, 서로 다른 스레드로부터 오는 요청을 순차적으로 큐에 넣어 유휴 상태에 있는 스레드에게 할당하는 기능을 맡을 것이다.


그림 2 - 스레드 풀 모델 (출처: https://en.wikipedia.org/wiki/Thread_pool)


3. 타이머 컴포넌트


게임 서버에서는 주기적으로 실행하거나 특정 시간, 또는 약간의 시간 간격을 둔 후 작업을 실행해야 할 때가 있다. 예를 들어 MMO 와 같은 부류의 게임 서버는 타이머를 이용해 짧은 주기마다 맵 또는 맵의 구역을 업데이트하는데, 이 때 타이머 컴포넌트를 사용한다.


시스템 수준에서는 API 재시도 등을 사용할 때 좋고, 게임 수준에서는 일일, 주간, 월간 퀘스트 초기화나 랭킹 정산 작업 등을 할 때도 타이머를 유용하게 사용할 수 있다. 리셋과 관련해서는 뒷부분에서 다시 살펴볼 것이다.


참고로 시간과 관련된 작업들은 최대한 다른 작업에 영향을 받아서는 안되므로, 타이머를 위한 스레드가 반드시 1개 필요하다.



4. 리셋/충전 컴포넌트


리셋 컴포넌트는 타이머 컴포넌트와 밀접하게 동작하는 기능으로, 특정 날짜 또는 특정 요청으로부터 동작하며, 수 많은 유저들이 가지고 있는 아이템, 재화의 제한/충전을 관리한다. 예를 들면 다음과 같은 것들이 있다.


- 투기장 입장권 충전 (X 분마다 +1)

- 일일, 주간 구매 제한 리셋

- 컨텐츠 소비에 필요한 에너지 충전 (X 분마다 + Y 씩)

- 컨텐츠 소비에 필요한 토큰 리셋 (매일 또는 매 주)


많은 유저들의 리셋/충전 정보를 어떻게 관리할지는 컴포넌트를 만들 때 고민해볼 것이다.



5. 리소스 스냅샷 컴포넌트


리소스 스냅샷 컴포넌트는 운영을 위한 용도로 특정 시점의 서버 상태를 기록하는 기능을 수행한다. 상태로 정의할 수 있는 것들은 CPU, 메모리, 디스크 정보가 있을 것고 엔진 수준에서 제공하는 여러 정보들도 있을 것이다. 예를 들면 이 서버에 접속한 사용자 수, 요청 받은 메시지/처리한 메시지 수, 실패한 외부 API 호출 수 등이 있다.



6. 운영 컴포넌트


운영 컴포넌트는 크게 운영 API 관리와 점검 여부 판단 기능이 있다. 운영 API는 사후 처리/환불 등 운영에 필요한 외부 API 들을 뜻한다.


점검 여부 판단 기능은 서버가 복구 불가능한 상태에 빠져 데이터 동기화를 더 이상 보장할 수 없을 경우 외부 요청을 모두 차단하고 처리 중인 요청을 모두 무효(Abort)하는 기능이다.



7. 서비스 발견(Service Discovery) 컴포넌트


서비스 발견은 리소스 스냅샷, 운영 컴포넌트와 밀접하게 붙어 있으며 크게 2가지 기능을 한다.


1. 자신의 주소(IP:PORT)와 스냅샷, 운영 컴포넌트가 가진 정보들을 서비스 발견 프로그램으로 전달한다.

2. 서비스 발견 프로그램으로부터 다른 모든 서버의 정보들을 가져와 업데이트한다.


이렇게 해서 각 서버는 같은 서버 군에 속한 모든 서버들을 볼 수 있고, 이 서버와 통신할 수 있다.



8. 설정 컴포넌트


설정 컴포넌트는 모든 컴포넌트에서 사용할 설정을 관리하는 컴포넌트다. 로그 레벨, 기능 on/off 기능 등 여러가지가 있을 것이다. 설정 정의는 YAML 포멧을 사용하려 한다. 익숙해지면 JSON보다 사용하기 쉽고, 주석도 사용할 수 있기 때문이다.



9. Redis / MySQL 연결 컴포넌트


많은 컴포넌트들이 Redis / MySQL을 사용할 것이다. 두 서비스와 통신할 별도 스레드가 필요하므로, 각각 컴포넌트로 만들 필요가 있다.


컴포넌트는 아니지만 필수적인 기능들


컴포넌트는 이정도면 충분하고, 더 필요한 것들은 그때가서 만들 것이다. 이제 컴포넌트는 아니지만, 반드시 필요한 기능들을 살펴보자.



1. JSON / YAML 파싱 유틸리티


JSON은 기획 데이터 관리, YAML은 설정 관리를 위해 반드시 필요하다. XML은 필요할 때 만들 것이고, 당장 필요하진 않을 것이다.



2. 오브젝트 정의 기능


오브젝트는 캐릭터가 될 수도 있고, 아이템, NPC 등 여러 형태일 수 있다. 그리고 모두 다음과 같은 기능들이 필요할 것이므로 이를 정의할 수 있는 유틸리티 기능들을 만들면 도움이 될 것이다.


 - 강화 기능 (강화도 종류가 다양하다)

 - 레벨 정의 기능 (마찬가지로 하나 이상의 레벨이 존재할 수 있다)

 - 게시판 (평가를 위한)

 - 분해 가능 여부 (오브젝트를 파괴하고 새로운 여러 오브젝트를 얻는 기능)

 - 판매 가능 여부 (오브젝트를 재화로 바꾸는 기능)



3. 다국어 지원 기능


프로그램 코드와 별개로 다국어를 관리할 수 있는 기능이 있어야 한다. 우선 순위는 낮지만, 대부분의 프로그래밍 언어(또는 gettext)에서 쉽게 구현이 가능하다.


다음 글에서 다룰 것들

이제 거의 모든 명세를 나열한 것 같다. 다음 글에서는 게임/시스템 명세를 가지고 개발 우선 순위를 정하고, 첫 번째 커밋(initial commit)을 하지 않을까 생각한다. 어쩌면 글보다 그림이 더 많을 수도 있다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari