나만의 작은 도서관

[TIL][C++] 250708 MMO 서버 개발 53일차: 웹서버와 게임서버는 다르다.(웹서버와 게임서버의 특징들) 본문

Today I Learn

[TIL][C++] 250708 MMO 서버 개발 53일차: 웹서버와 게임서버는 다르다.(웹서버와 게임서버의 특징들)

pledge24 2025. 7. 9. 00:30
주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다. 

웹서버와 게임서버는 다르다.

  • 게임서버를 공부하다 보면 자료가 부족해서 웹서버 자료를 통해 이해해야 하는 경우가 많다. 그런데, 웹서버 지식을 이해하다면 약간씩 게임서버에 안 맞는 경우가 종종 발생한다. 오늘은 이에 대해서 알아보고, 어떠한 구조적 차이가 있는지 이해해 보기로 했다.

 

웹서버와 게임서버의 특징들(웹서버)

 

웹서버의 특징 1. 많은 수의 짧은 연결

  • 요즘에는 이것저것 많은 기술들이 붙은 웹서버이지만, 전통적인 웹서버 구조에서 통신은 https에 req-res 방식의 독립적이고 수동적인 통신을 한다. 그렇기에 각 요청에 대해 오랜 시간 연결되어 있지 않으며, 보통 수백 m s 이내에 연결이 끊긴다.

 

웹서버의 특징 2. 작업 처리 시간 중 대부분의 I/O 대기시간(I/O bound)

  • 사용자가 서버에게 작업을 요청한다면 어떤 작업을 요청할까? 대부분의 요청은 서버가 가지고 있는 정보를 달라고 하는 경우가 많다. 페이지 정보라던가, 랭킹 정보 등이 예시가 될 수 있다. 즉, 사용자의 요청은 READ/WRITE 중 READ에 해당하는 경우가 많다는 것이다.
  • 서버가 사용자에게 무언가 정보를 주기 위해선 DB를 들러야 한다. 게다가 요청에 의한 추가적인 작업들도 작업별 다른 서버들이 처리하기 때문에 더더욱 CPU를 사용할 일이 없다. 그래서 웹서버의 대부분의 요청은 I/O bound 작업이다.

 

웹서버의 특징 3. 용인되는 불규칙하고 느린 응답 속도

  • 웹 서비스를 이용하는 사용자는 100ms ~ 400ms를 오가는 응답 속도를 경험했을 때, 이에 대해서 크게 불편함을 느낄까? 그…럴 수도 있겠지만 생각보다 그리 불편하게 느끼는 사람은 없을 것이다. 웹 서비스는 느슨한 응답 속도에 대해 민감한 작업이 별로 없기 때문이다.
  • 페이지 요청, 게시물 등록 등 “사용자 행동 → 요청 → 결과”의 느슨한 흐름을 가지는 것이 대부분이다.

 

웹서버의 특징 4. 병렬성보단 동시성이 중요

  • 특징 3과 연관되는 이야기인데, **“응답 속도가 중요시되지 않는다”**는 말은 하나의 요청에 대해 병렬 작업을 해야 할 이유가 없다는 것을 의미하기도 한다. 즉, 사용자가 게시물 정보 조회 요청을 했을 때, 서버의 모든 코어를 동원해 N배 이상 빠르게 작업을 처리할 필요가 없다는 것이다. 그저 진득하게 하나의 코어에서 정확하게 처리만 하면 된다.
    • ⇒ 이는 서버 입장에서 작업의 병렬성은 중요시되지 않는다는 것을 의미한다.
  • 하지만 작업들의 동시 처리는 중요시된다. 작은 I/O bound 작업 요청이 많은 웹서버는 조금이라도 더 많은 양의 작업들을 빨리 처리하기 위해 동시에 진행해야 한다. 즉 하나의 코어에서 여러 작업에 대한 실행 역할을 맡으며, 스레드 수가 코어 개수의 4~5배 정도 되기도 한다.
    • ⇒ 이는 서버 입장에서 작업의 동시성은 매우 중요시된다는 것을 의미한다.

 

웹서버의 특징 5. 다른 사용자의 서버와의 통신은 관심이 없다.

  • 사용자는 그저 서버와 단 둘이 소통하고 싶을 뿐이다. 누군가가 서버와 통신하는 것에 대해선 전혀 관심 없다. 클라이언트끼리 상호작용하는 일은 전혀 없으며, 오히려 사용자가 줄어들수록 서버의 응답 속도가 올라가니 사용자가 없는 편이 좋다.
  • +) 어디까지나 전통적인 수동적 웹서버에서 이야기이다. 웹소켓, RTC 등을 써서 양방향 소통을 하는 경우는 해당되지 않는다.

 

웹서버와 게임서버의 특징들(게임서버)

게임서버는 장르마다 서버의 구조가 매우 다릅니다. 아래의 특징들이 전부 통용되지는 않음을 미리 알립니다.

 

게임서버의 특징 1. 지속되는 연결

  • TCP로 통신하는 게임서버의 경우, 유저와 지속적으로 연결된 상태를 유지하게 된다. 이는 양쪽의 호스트에게 연결의 품질을 유지시켜야 하는 책임을 지게 하기 때문에 양쪽 호스트는 추가적인 부하를 얻게 된다.

 

게임서버의 특징 2. 작업 처리 시간의 대부분이 연산 처리(CPU bound)

  • 게임서버는 굉장히 바쁘다. 웹서버와 달리 사용자의 대부분의 패킷이 READ/WRITE 중 WRITE를 해야 하는 작업이 많고, 이 WRITE 작업이 서버 내부에서 연산되어야 하는 일이 많기 때문이다. 게다가 많은 작업들이 Broadcast로 이어지기 때문에 하나의 패킷이 제곱의 작업량을 만들어내기까지 한다.
    • ex) 플레이어의 이동, 플레어이의 공격/피격 판정 등
  • 게다가 Tick 단위로 운영하는 맵을 갱신 해야 한다. Tick은 100ms~200ms 정도마다 반복되며, 매 틱마다 운영 중인 맵의 데이터를 전부 계산에서 갱신해야 한다.
    • ex) 몬스터 스폰, 몬스터 AI, 투사체 위치 갱신 등
  • 그래서 게임서버의 대부분 요청은 CPU bound 작업이며, 틱 작업 또한 CPU bound 작업이다.

 

게임서버의 특징 3. 요구되는 공평하고 낮은 응답 속도

  • 게임 서비스를 이용하는 사용자는 100ms ~ 400ms를 오가는 응답 속도를 경험했을 때, 이에 대해서 크게 불편함을 느낄까? 당연히 큰 불편함을 느낄 것이다. 게임을 플레이하는 플레이어들은 찰나의 순간에 게임의 판도가 뒤바뀌는 것을 몇 번이고 경험하게 된다. 거짓말 조금 보태 숙련된 플레이어들의 경쟁에서는 1ms 차이의 응답 속도(latency) 차이에 판도가 바뀌기도 한다.
  • 그렇기 때문에 전체적인 응답 속도가 느려서도, 유저마다 다른 응답 속도를 가져서도 안된다. 공평하고 쾌적한 게임 환경을 조성하기 위해선 낮고 일관성 있는 응답 속도를 최대한 제공해야 한다.

 

게임서버의 특징 4. 상태 동기화를 위한 Tick과 프레임 드롭

  • 서버는 나의 상태를 다른 유저들에게 보내고, 다른 유저들의 상태를 나에게 보냄으로써 상태를 동기화한다. 또한, 유저의 정보가 아니더라도 서버는 매 Tick마다 운영하는 맵들의 상태 정보를 맵에 존재하는 유저들에게 broadcast 한다. 여기서 두 번째 내용인 맵의 Tick에 대해서 조금 신경 써야 한다.
  • 서버는 최대한 한 틱 내에서 맵의 상태 정보를 갱신해야 한다. 즉, 틱이 100ms라면, 다음 상태 동기화 정보를 broadcast 하기 위해선 100ms 이내에 모든 계산을 끝내야 한다. 그런데 만약, 계산을 하지 못하면 어떻게 될까? 계산이 마무리되지 못했다면 부정확한 데이터를 broadcast 하거나, 아예 Tick을 건너뛰게 된다. 이로 인해 유저들은 일명 “프레임 드롭”을 겪게 되는데, 프레임 드롭의 발생 빈도가 높아지면 유저의 경험은 조금씩 박살이 난다.
  • 따라서, 게임서버는 이런 Tick 처리를 최대한 빨리 해야 하기 때문에 단일 코어의 성능이 보다 좋아야 한다.

 

결론

  • 게임서버를 웹서버처럼 스레드를 코어의 3~4배 정도로 많이 만들어두고, 각 요청에 대해 독립적으로 처리할 수 없는 이유는
    1. CPU bound: 웹서버와 달리 게임서버는 CPU bound 한 작업이 대부분이기 때문에, 각 스레드가 CPU 사용률이 높아 CPU 유휴 상태가 많지 않다. 따라서 병렬 처리 성능은 늘지 않으며 오히려 context switching만 잔뜩 발생해 성능이 떨어진다.
    2. 상태 동기화: 게임에선 서로 다른 유저가 같은 상태를 바라봐야 하기 때문에 모든 요청을 독립적으로 처리하면 안 된다.