실시간 온라인 대전, 서버 없이 물리 동기화하기
알까기와 오목의 친구 대전 기능을 만들 때 세운 목표는, 서버 비용을 거의 0에 가깝게 유지하면서도 실시간 대결이 매끄럽게 동작하게 만드는 것이었습니다. 결론부터 말하면, 서버는 게임 규칙을 전혀 몰라도 됩니다.
서버는 그저 우체부일 뿐
서버가 하는 일은 딱 하나, 방 코드로 두 사람을 연결하고 한쪽이 보낸 메시지를 다른 쪽에 그대로 전달하는 것뿐입니다. 누가 이겼는지, 돌이 어디로 움직였는지, 그런 판단은 서버가 전혀 하지 않습니다. 이런 구조를 쓰면 서버 로직이 극도로 단순해져서 유지보수가 쉽고, 게임 종류가 늘어나도 서버 코드를 거의 건드릴 필요가 없습니다.
양쪽이 각자 계산해도 똑같이 나오려면
문제는 여기서 시작됩니다. 서버가 판단을 안 해준다면, 알까기처럼 물리 연산이 필요한 게임은 두 플레이어의 기기가 각자 독립적으로 같은 물리 시뮬레이션을 돌려야 하는데, 이게 한 치의 오차도 없이 똑같이 나와야 합니다. 이를 위해 몇 가지 원칙을 지켰습니다. 첫째, 프레임 속도에 따라 흔들리지 않도록 물리 계산 간격을 고정 시간 단위로 누적시키는 고정 타임스텝을 씁니다. 둘째, 돌의 초기 배치처럼 무작위성이 필요한 부분은 진짜 난수 대신 방 코드에서 만든 시드값으로 결정되는 의사난수를 씁니다. 같은 시드는 항상 같은 배치를 만들어내니까요. 셋째, 발사 속도 같은 사용자 입력값은 소수점 자릿수를 정해서 절사한 뒤 전송하고, 받는 쪽도 똑같이 그 값을 그대로 적용합니다.
오목이 더 쉬운 이유
반면 오목은 물리 연산이 필요 없는 격자 게임이라 훨씬 단순합니다. 좌표 하나만 전달하면 되고, 화면 크기와도 무관하니 동기화 문제 자체가 애초에 생기지 않습니다. 같은 실시간 대전이라도 게임의 성격에 따라 필요한 기술적 설계가 이렇게 완전히 달라진다는 게, 이 두 게임을 나란히 만들면서 얻은 흥미로운 발견이었습니다.
서버가 없어야 오히려 튼튼하다
이런 구조를 택한 실용적인 이유도 있습니다. 서버가 게임 로직을 전혀 모르니, 한쪽이 실수로 이상한 값을 보내도 서버가 판정을 잘못 내릴 여지 자체가 없습니다. 잘못된 판정의 책임은 온전히 각 클라이언트의 계산 로직에만 있고, 서버는 그저 메시지를 전달하는 역할만 하니 장애 지점이 훨씬 적어집니다. 게다가 서버 코드가 게임 종류에 관계없이 거의 똑같기 때문에, 새로운 대전 게임을 추가할 때마다 서버를 새로 짤 필요 없이 기존 중계 서버를 그대로 재사용할 수 있었습니다. 단순한 구조가 결국 가장 안정적인 구조였습니다.