Quick Reference
권위 서버(authoritative server)는 클라이언트가 보낸 입력을 믿고 상태를 받아 적는 서버가 아니라, 게임 규칙을 적용해 최종 상태를 확정하는 서버입니다. 클라이언트는 의도와 입력을 보내고, 서버는 가능한 행동인지 검증한 뒤 결과를 다른 클라이언트에 알립니다.
| 정보·행동 | 클라이언트가 해도 되는 일 | 서버가 최종 결정할 일 |
|---|---|---|
| 이동 | 방향·버튼 입력을 즉시 화면에 반영 | 이동 가능 여부, 충돌, 최종 좌표 |
| 공격 | 발사 효과와 조준선을 먼저 표시 | 쿨다운, 탄약, 사거리, 피격·피해 |
| 아이템 | 획득 요청과 UI 갱신 | 소유권, 중복 지급, 재화 증감 |
| 화면 연출 | 카메라, 사운드, 파티클 | 보통 서버 검증 대상이 아님 |
function handleFire(session: Session, command: FireCommand) {
if (!session.acceptsNextSequence(command.sequence)) return;
if (!world.canFire(session.playerId, command.weaponId)) return;
const hit = world.raycastFromPlayer(session.playerId, command.aim);
world.applyAuthoritativeFire(session.playerId, command.weaponId, hit);
}요청과 결과를 나누기
클라이언트가 보내야 하는 것은 보통 앞으로 이동한다, 이 무기를 발사한다, 이 슬롯의 아이템을 사용한다처럼 의도 또는 입력입니다. 내 좌표는 여기다, 적에게 100 피해를 주었다, 재화가 늘었다처럼 결과를 보내고 서버가 그대로 반영하면 패킷 변조와 버그가 곧 게임 상태 변조가 됩니다.
서버는 요청을 받았을 때 최소한 세 가지를 확인합니다. 해당 세션의 플레이어가 맞는지, 현재 상태에서 행동할 수 있는지, 행동 결과가 서버가 가진 월드 상태와 충돌하지 않는지입니다. 이동은 속도와 충돌을, 공격은 쿨다운·탄약·사거리·피격 판정을, 보상은 중복 요청과 소유권을 함께 봅니다.
클라이언트 예측은 이 원칙의 예외가 아닙니다. 로컬 화면이 먼저 움직여도 서버 스냅샷이 확정값이며, 예측값이 다르면 클라이언트가 보정합니다. 예측과 보정의 순서는 클라이언트 예측과 서버 보정에서 다룹니다.
서버가 소유할 상태
공정성이나 경제에 영향을 주는 상태는 서버가 소유합니다. 체력, 위치, 인벤토리, 퀘스트 진행, 매치 결과, 랭크 점수, 드롭 판정이 여기에 속합니다. 서버가 모든 표현 상태까지 보내야 한다는 뜻은 아닙니다. 카메라 흔들림, 로컬 메뉴 열림, 이미 확정된 타격의 파티클 재생은 클라이언트가 독립적으로 처리해도 됩니다.
서버 소유 상태는 하나의 simulation tick 안에서 읽고 쓴다는 계약을 정해 두는 편이 좋습니다. 네트워크 수신 callback이 임의로 월드 상태를 바꾸면, 같은 입력도 수신 순서에 따라 다른 결과를 낼 수 있습니다. callback은 command queue에 넣고, tick이 검증·적용·스냅샷 생성을 소유하면 판정 지점을 추적하기 쉬워집니다.
신뢰 경계가 무너지면
클라이언트가 hitTargetId나 damage를 확정해 보내게 하면, 정상 클라이언트 코드가 아니라 전송 패킷만 바꿔도 불가능한 피격과 피해를 만들 수 있습니다. 반대로 모든 입력을 의심한다며 화면 표시까지 서버 왕복을 기다리면 조작감이 크게 나빠집니다. 권위 서버의 목적은 모든 일을 서버에서 하는 것이 아니라, 승패와 공유 상태를 바꾸는 결론을 서버에서 확인하는 것입니다.
세션 ID만으로 권한을 판단하는 것도 부족합니다. 로그인한 플레이어인지, 해당 room 또는 match에 속하는지, 요청 sequence가 이미 처리되지 않았는지, 요청 속도가 규칙상 가능한지까지 확인해야 합니다. 연결이 끊겼다가 재접속한 세션에는 이전 입력 queue와 새 세션의 경계도 분명히 둡니다.
클라이언트가 보낸 값은 입력 후보이지 게임의 사실이 아닙니다. 서버가 검증할 수 없는 결과를 클라이언트 메시지 형식에 넣지 않는 것이 가장 단순한 방어입니다.
참고 링크
2 sources