해당 내용은 게임 서버 프로그래밍교과서의 내용을 참고했습니다.
해당 설명에 사용된 코드는 모두 의사 코드입니다.

패키지 게임에서 게임 서버

과거 패키지게임에서는 인터넷 연결이 안 되는 컴퓨터가 많았고, 속도도 느렸다. 따라서 대부분의 패키지 게임에서는 컴퓨터 네트워크를 사용하지 않았다.

하지만 인터넷이 보급되면서, 세 명 이상 인터넷을 통해 플레이하는 방식의 패키지게임이 등장했다. 세 명 이상이 게임을 하려면 플레이 상태를 저장하는 곳이 있어야 했고, 플레이어 중 한명의 컴퓨터가 모든 플레이어들의 게임 플레이 상태를 취합해서 유지하는 역할을 했다.

서버의 역할

컴퓨터 한 대만 가지고 게임을 즐기는 싱글플레이에서는 모든 입출력과 연산 처리가 컴퓨터 한 대 안에서 일어난다. 따라서 네트워크 자체가 쓰일 일이 없다.
따라서 싱글 플레이에서는

  • 입력받기 : 키보드, 마우스 등으로 컴퓨터가 정보를 획득하는 과정
  • 게임 로직 처리하기 : 게임 플레이 판정, 플레이어가 어느 캐릭터에 대미지를 주었는지 등을 처리
  • 랜더링 : 변화된 상태를 화면에 표현

온라인 게임에서는 여러 사람의 상호작용을 어디선가 중재해야 한다.
여기서 게임 서버와 클라이언트의 개념이 나오는데, 클라이언트는 플레이어가 직접 만지는 컴퓨터를 의미한다.
대부분의 온라인 게임에서는 클라이언트에서 게임 로직을 처리하는 역할 일부를 떼어 서버로 옮긴다. 그리고 클라이언트와 서버 간에는 컴퓨터 네트워크를 통해 서로 데이터를 주고받는다.

게임 클라이언트와 서버의 상호 작용

게임 클라이언트와 서버의 상호 작용은 크게 네 가지로 구분된다.

  1. 연결
  2. 요청-응답
  3. 능동적 통보
  4. 연결 해제

연결

연결이란 클라이언트가 서버와 데이터를 주고받을 준비를 하는 것.
클라이언트에서 서버에 연결을 요청하면 서버는 이를 수락하여 클라이언트와 연결을 맺는 것이다. TCP연결이 이 과정 중 하나다.

요청-응답

요청-응답이란 클라이언트는 서버에 메시지를 보내고, 서버는 이를 처리한 후 결과를 응답해 준다.
TCP연결을 했다고 해서 바로 모든 행동이 허락될 수는 없는데, 이는 클라이언트가 해커일 수도 있기 때문이다.
일단 클라이언트가 서버와 연결되었으면 클라이언트는 서버에 내가 누구인지 알려준다. 그리고 서버는 클라이언트의 신원을 확인하여 연결을 계속할지 아니면 추방할지를 판단한다.

능동적 통보

능동적 통보란 클라이언트가 서버에게, 서버가 클라이언트에게 통보를 하지만, 그 통보에 대해 반응을 받지 않아도 될 경우를 의미한다.
게임 서버의 세션은 시간이 지나면서 변화하므로, 이 변화를 클라이언트에 일정 시간마다 통보해야 할 때가 있다. 이는 능동적 통보의 대표적인 예시이다.

게임 서버가 하는 일

  1. 여러 사용자와 상호작용
  2. 클라이언트에서 해킹 당하면 안 되는 처리
  3. 플레이어의 상태 보관

게임 로직을 클라이언트에서 처리하면 해킹의 위험이 있다. 하지만 모든 로직을 서버에서 실행하면, 클라이언트에서 마우스 클릭이나 키보드 방향키를 누르는 모든 입력들이 네트워크를 통해 서버로 전달된다. 서버는 이를 처리하고 결과를 클라이언트로 보내주는데, 네트워크의 지연율, 레이턴시 때문에 게임 진행에 지장이 생기고 렉이 걸리는 현상이 발생한다.

따라서 현실적으로는 서버에서 모든 게임 플레이 판정을 할 수 있게 만들되, 쾌적한 품질과 타협하기 위해 일부 게임 처리를 클라이언트 쪽에 맡긴다.

게임 서버의 품질

  1. 안정성
  2. 확장성
  3. 성능
  4. 관리 편의성

안정성

게임 서버가 얼마나 죽지 않는가를 의미한다. 게임 서버는 24시간 365일 켜져 있는 서버인데, 프로그램의 버그 때문에 서버 프로세스가 중간에 비정상 종료되는 경우가 있을 수 있다.
안정성에는 게임 서버가 얼마나 오작동을 하지 않는가도 포함한다.
따라서 서버의 안정성을 위해 다음의 노력들이 필요하다.

1. 치밀한 개발과 유닛 테스트

프로그래밍한 결과를 한 줄 한 줄을 검수하고 가이드라인을 따른다.
개발된 프로그램의 각 부분은 반드시 자동화된 자가 검증, 유닛 테스트를 만들어야 한다.

2. 80:20 법칙

모든 프로그램 성능의 80%는 20%의 코드에서 나온다. 성능에 지대한 영향을 주는 일부분의 소스 코드에서만 프로그램 구조가 복잡해지더라도 성능을 최적화해서 개발하고, 나머지 부분은 성능보다 유지보수하기 쉬운 단순한 구조로 개발한다.

3. 1인 이상의 코드 리뷰

모든 개발 결과물은 동료의 검토를 받아야 한다. 다른 사람의 관점에서 몰랐던 버그를 발견하는 경우가 종종 있다. 또한 상대방 코드를 이해하면서 서로 지식을 공유하는 효과도 있다.

4. 가정하지 말고 검증하라

유닛 테스트만으로는 서버 안정성을 확신할 수 없다. 실제 사람을 모아 테스트하기는 쉽지 않으므로, 사람 대신 컴퓨터를 이용하여 자동으로 작동하는 게임 클라이언트를 대량으로 실행시켜 테스트를 하기도 한다.
이는 정해진 동작만 하기 때문에 모든 상황에 대한 테스트를 하는 것에 한계가 있지만, 대부분의 문제를 해결할 수 있다.

하지만 이러한 노력에도 업데이트가 계속되다 보면 프로그램 복잡도가 증가하고, 프로그램 구조가 훼손되기 시작한다.
따라서 서버가 죽더라도 최대한 빨리 다시 살아나게 하거나, 서버는 죽더라도 최대한 적은 서비스만 죽게 하거나, 오작동에 대해 기록을 남기는 방법이 있다.

확장성

서버를 얼마나 많이 설치할 수 있느냐를 의미한다.
사용자 수가 늘어나더라도 서비스 품질이 떨어지지 않고 유지되는지를 의미한다.
서버 확장성을 올리는 방법에는 수직적 확장, 수평적 확장이 있다.
수직적 확장은 서버의 하드웨어를 더 좋은 것으로 바꾸는 것이고, 수평적 확장은 서버 컴퓨터의 개수를 늘리는 것이다.

수직적 확장

단지 하드웨어를 더 좋은 것으로 바꾸는 방법. 소프트웨어에 변화를 줄 필요가 없고, 소프트웨어 설계에 시간이 덜 든다.

비용이 많이 든다. 컴퓨터는 고사양일 수록 가격이 기하급수적으로 상승하며, 한계가 있다.

수평적 확장

서버 프로그램의 구조가 복잡해진다. 설계에 드는 시간 비용이 높아진다.

성능을 200배 좋게 하려면 컴퓨터를 200대 놓으면 된다.
하지만 수평적 확장은 특정 데이터 처리를 여러 서버에 걸쳐서 작동해야 하는데, 일반적으로 컴퓨터 한대 안에서 발생하는 처리 속도보다 여러 컴퓨터에 걸쳐서 처리하는 속도가 훨씬 느리다. 따라서 서버 간 상호 작용하는 처리에서 성능 하락이나 예전 데이터를 다루는 에러 현상이 발생하기도 한다.

또한 하나의 메시지를 처리하기 위해 여러 서버를 거쳐야 하므로 처리 시간에 서버끼리 통신하는 시간까지 포함된다.

정리

결국 수평적 확장은 수직적 확장보다는 느리다. 하지만 확장성이 뛰어나다.
대부분의 서버에서는 개발의 경제성과 성능을 위해 수평적 확장과 수직적 확장을 혼합해서 사용한다.

성능

확장성은 사용자가 많아지더라도 처리 속도가 하락하지 않게 하는 것이다.
성능은 기본적으로 얼마나 빨리 처리하는지를 의미한다.

게임 종류마다 요구되는 성능이 다르다. 턴제 게임은 처리 속도가 제법 느려도 상관이 없지만, FPS게임 등은 처리 속도가 중요하다.

성능을 좋게 하는 방법으로는

  1. 코드 최적화나 알고리즘 최적화를 통해 프로그램이 더 빠르게 실행될 수 있게 한다.
  2. 더 빠른 속도로 실행되는 프로그래밍 언어를 사용한다.
  3. 서버의 과부하 영역을 분산한다.
  4. 네트워크 프로토콜을 최적화한다.
  5. 서버를 통신하지 않고 클라리언트 끼리 직접 통신하게 한다.

관리 편의성

일반적인 게임 서버는 콘솔 프로그램 형태로 작동한다. 서버 프로그램은 키 입력을 받는 기능조차 없이 작동되는 것들도 많으며, 사용자가 하는 거의 유일한 직접 상호작용은 프로그램을 끄는 것일 수 있다.

아무것도 입력받지 않고 아무런 정보도 보여주지 않는 서버의 관리는 어렵다.

가장 많이 하는 방법으로는 서버 프로그램을 원격으로 관리하는 프로그램을 따로 이용하는 것이다.
서버 프로그램과 네트워크 통신을 하는 별도 프로그램을 두고, 그 프로그램은 GUI같은 필요한 사항들을 자유롭게 갖춘다. 이를 관리 도구 혹은 운영 도구라고 한다.

관리 도구에서 필수적으로 하는 일은 다음과 같다.

  • 서버 켜기, 끄기
  • 동시접속자 수 보기
  • CPU,RAM 사용량 보기

관리 도구는 게임을 운영하는 조직이 클 수록 역할이 분담된다.
예를 들어 GM은 서버를 끄거나 켜는 권한이 없는 대신 불량 행동을 하는 플레이어를 강제로 추방하는 등의 권한을 갖고, 서버 관리자는 GM의 일을 할 수 없는 대신 서버를 끄거나 켜는 등 업무를 할 수 있는 관리 도구를 쓴다.

플레이어 정보의 저장

보통 싱글플레이 게임의 플레이어 정보는 게임을 구동하는 컴퓨터 자체의 디스크에 저장된다. 온라인 게임에서 플레이어 정보는 클라이언트에 잘 저장하지 않는다.

이는 해킹에 취약하고, 같은 사용자가 다른 기기를 사용할 때 문제가 된다.
그래서 대다수 온라인 게임은 플레이어 정보를 서버에 저장한다.
플레이어의 데이터는 게임 서버에 직접 저장하는 경우가 있지만, 게임 서비스의 규모가 큰 경우 별도의 데이터베이스를 이용하기도 한다.

데이터베이스를 이용하는 이유는 다음과 같다

  1. 데이터 관리와 분석을 빠르게 할 수 있다. 특정 조건을 만족하는 데이터들을 빠른 속도로 찾아낼 수 있다.
  2. 강력한 데이터 복원 기능이 있다. 데이터 변경 사항을 1개 이상의 사본 저장소에 보관할 수 있으며, 고장에 대응하기가 쉽다.
  3. '전부 아니면 전무'로 데이터를 변경할 수 있다. 송금 업무의 중간에 에러가 나더라도 돈이 없어지거나 많아지는 일 없이 복구할 수 있다.
  4. 데이터 일관성을 유지시켜 준다. 제약 사항을 미리 데이터베이스에 넣을 수 있다.
  5. 처리가 2개 이상 동시에 실행될 때 한 데이터가 동시에 여러 데이터에 색세스 하면서 이상한 결과가 나오는 문제를 막아 주는 기능이 있다. 이를 lock기능이라 한다.
  6. 장애에 대한 내성이 강하다. 데이터베이스는 데이터를 기록하기 전에 로그버퍼 라는 버퍼에 할 일을 기억해 둔다. 데이터베이스가 중간에 죽어버린 후 재시작을 해도 로그 버퍼에서 아직 하지 않은 일을 찾아 복원한다.

서버 구동 환경

온프림 서버

  1. 인터넷 품질이 신뢰성이 있어야 한다. 인터넷이 중간에 끊어지거나 느려지지 않아야 한다.
  2. 서버가 쉽게 고장 나지 않아야 한다. 컴퓨터를 24시간 265일 계속 켜 놓는 상황에서 장기간 고장이 없어야 한다.
  3. 도난이나 침입 사고에서 안전해야 한다.

이러한 이유 때문에 규모가 큰 서버에서는 데이터센터라는 특별한 건물에 설치된다.
이렇게 데이터센터에서 직접 서버 하드웨어와 운영체제를 설치해서 관리하는 것을 자체 서버 혹은 온프리미스(on-premise), 줄여서 온프림(on-prem) 서버라고 한다.
초기 비용이 많이 들어가고 서비스 이용자 수가 급격히 늘거나 줄어들 때 빨리 대응하지 못한다는 단점이 있다.

클라우드 서버

리얼 머신 위에 구동하는 가상 머신의 집합, 서버 컴퓨터만 가상 머신으로 구동하는 것이 아니라 네트워크 장치나 외장 디스크 드라이브도 모두 가상화되어 있다.
서버 증설이 간편하고 빠르다.
클라우드 서버의 사용 단가는 온프림 서버보다 비싸지만, 필요한 만큼 쉽게 개수를 늘렸다 줄였다 할 수 있다. 또한 전 세계 어디든지 즉시 서버를 늘리고 줄일 수 있다. 따라서 갑자기 폭증하는 사용자 수를 대응하기가 쉽다.

하지만 클라우드 서버는 물리적 서버와 달리 처리 속도가 균일하지 않다는 단점이 있다. 가상 머신에서 동작하기 때문에 성능이 떨어지고, 리얼머신 한 대에서 서로 모르는 사람이 구축한 서버가 동작하여 간헐적인 지연 시간이 발생할 수 있다.

profile
코린이

1개의 댓글

comment-user-thumbnail
2022년 8월 15일

진짜 정리 잘 한 것 같습니다!

답글 달기