내 스킬 하나가 상대 화면에 뜨기까지
플레이어가 스킬 버튼을 누른 순간부터 다른 플레이어 화면에 그 스킬이 보이기까지 패킷이 지나는 단계를 내 클라, 네트워크, 서버, 네트워크, 상대 클라 순서로 보여준다. 예시 조건(20Hz, RTT 50ms)에서 전체 합계는 약 221ms이고, 내 화면에서는 약 25ms 만에 보이는 스킬이 상대 화면에는 200ms 넘게 걸려 나타난다. 이벤트도 보간 시점에 맞춰 재생하는 구현이라면 가장 큰 몫은 상대 클라의 보간 버퍼, 네트워크 편도 두 번(내 업링크, 상대 다운링크), 서버 틱 대기다.
01전체 구간
내 클라 → 네트워크 → 서버 → 네트워크 → 상대 클라 · ▶ 이 장면 보기
화면에는 다섯 개 구간이 나란히 놓인다.
- 내 클라: 입력, 예측 실행, 패킷 생성,
send() - 네트워크: 공유기·와이파이, ISP, 인터넷 백본, 방화벽·LB
- 게임 서버: I/O 스레드, 존 메시지 큐, 틱에서 검증·판정, AOI·브로드캐스트
- 네트워크: 백본, 상대 ISP, 상대 공유기
- 상대 클라: recv, 보간 버퍼, 애니메이션·이펙트, 화면
패킷은 내 클라에서 UseSkillReq로 출발하고, 서버에서 처리된 뒤 SkillCastNoti로 바뀌어 상대 클라에 도착한다. 아래쪽에는 이 구간이 업링크, 서버 처리, 다운링크로 묶인다. 구간마다 시간이 조금씩 쌓이고, 그 합이 상대가 느끼는 지연이다.
02내 클라 안에서
누르는 순간 화면은 이미 움직이고, 패킷이 만들어진다 · ▶ 이 장면 보기
내 클라 안의 단계는 키 입력(~0ms), 게임 루프(0~16ms), 예측 실행(즉시), 패킷 생성(<1ms), 직렬화·암호화(<1ms), send()(<1ms), NIC(<1ms) 순서다. 키 입력은 OS 이벤트로 들어오고, 게임 루프가 다음 프레임에 읽기 때문에 최대 한 프레임(60fps 기준 16ms)을 기다릴 수 있다.
예측 실행 단계에서 클라이언트는 서버 승인을 기다리지 않고 스킬 애니메이션을 먼저 재생한다. 그래서 내 화면은 이미 스킬을 쓰는 중이다.
패킷은 game_packet.proto의 UseSkillReq 메시지로 만들어진다. 필드는 seq(입력 순번), skill_id, target_id, pos(내가 본 내 위치), client_ms(보낸 시각)다. seq와 client_ms는 나중에 위치 보정과 지연 측정에 쓰인다. 여기까지 1~20ms가 걸리고, 대부분은 다음 프레임을 기다리는 시간이다.
03네트워크 구간
집에서 데이터센터까지, 여러 장비를 거치는 편도 구간 · ▶ 이 장면 보기
패킷은 내 PC에서 출발해 공유기·와이파이, ISP 망, 인터넷 백본, IDC 방화벽·DDoS 필터, 로드밸런서를 지나 게임 서버에 닿는다. 각 지점 위의 누적 시간은 0, 2, 8, 16, 23, 24, 25ms이고, 편도 합계는 25ms다. 편도는 대략 RTT의 절반이며, 여기서는 RTT 50ms를 예시로 잡았다.
- 와이파이 구간: 지터와 손실이 가장 많이 생긴다.
- 거리: 광섬유 안의 빛도 1,000km를 가는 데 약 5ms가 걸린다.
- 방화벽·LB: 보통 1ms 미만이지만 트래픽이 몰리면 큐잉이 생긴다.
- 손실이 나면: TCP 재전송으로 수십 ms가 늘고, RTO에 걸리면 200ms 이상 멈춘다.
04서버 안에서
받고, 줄 서고, 틱에서 검증 · 판정 · 전파된다 · ▶ 이 장면 보기
서버에 도착한 패킷은 NIC에서 커널 수신 버퍼로, 다시 I/O 스레드로 넘어가 recv, 복호화, 역직렬화를 거친다. 세션 확인으로 누구의 패킷인지 정한 뒤 존 메시지 큐에 들어간다. 앞의 단계는 각각 <1ms지만, 큐에서는 다음 틱이 시작될 때까지 0~50ms를 기다린다.
틱 처리는 로직 스레드에서 약 12ms 동안 진행된다. 쿨다운, 사거리, 마나, 치트 여부를 검증하고, 데미지와 상태 변경을 판정한다. AOI 조회로 주변 플레이어(화면 예시는 32명)를 찾고, SkillCastNoti와 DamageNoti를 브로드캐스트한다. 결과는 송신 큐를 거쳐 I/O 스레드에서 직렬화, 암호화, send된다.
아이템, 로그 같은 DB 기록은 비동기로 처리되어 틱이 기다리지 않는다. 서버 구간에서 가장 긴 시간은 처리보다 다음 틱까지의 대기이고, 평균은 틱 간격의 절반이다.
05상대 화면까지
서버에서 상대 클라까지, 그리고 상대 클라가 일부러 기다리는 시간 · ▶ 이 장면 보기
서버가 보낸 패킷은 다운링크(상대 회선 기준 ~25ms)를 지나 상대 클라의 NIC와 recv(<1ms)에 도착한다. 게임 루프가 다음 프레임에 읽고(0~16ms), 보간 버퍼(~100ms), 애니메이션·이펙트(즉시), vsync에 맞춘 화면 출력(~16ms)을 거친다.
보간 버퍼는 서버 스냅샷 사이를 부드럽게 잇기 위해 상대 클라가 일부러 약 100ms 과거의 상태를 그리는 장치다. 화면에서 이 단계만 빨간색으로 강조되어 있다.
아래 타임라인은 같은 스킬이 각 화면에 보이는 시점이다. 내 화면에서는 입력 대기와 렌더를 거쳐 약 25ms에 시전이 시작되고(예측), 서버 판정(72ms)이 다운링크로 돌아와 그려지는 약 120ms에 데미지가 확정되며, 상대 화면에는 221ms에 내 스킬이 보인다.
06시간을 다 더하면
어디서 얼마나 걸리는지 (이벤트도 보간 시점에 맞춰 재생할 때) · ▶ 이 장면 보기
버튼부터 상대 화면까지 시간을 예시 조건(20Hz, RTT 50ms)으로 더하면 다음과 같다. 입력·처리 10ms, 업링크 25ms, 틱 대기 25ms, 틱 처리 12ms, 다운링크 25ms, 프레임 8ms, 보간 버퍼 100ms, 렌더 16ms로 합계는 약 221ms다.
- 보간 버퍼 ~100ms: 부드러운 움직임의 대가다. 지터가 크면 더 늘린다.
- 네트워크 편도×2 ~50ms: 내 업링크와 상대 다운링크의 합이다. 거리, 회선, 와이파이에 따라 달라진다.
- 틱 대기 + 처리 ~37ms: 틱레이트가 낮을수록, 서버가 바쁠수록 늘어난다.
- 클라 프레임 ~25ms: FPS가 낮으면 입력과 표시가 모두 늦어진다.
막대 아래에는 구간별 묶음이 표시된다. 내 클라 10ms, 업링크 네트워크 25ms, 서버 37ms, 다운링크 네트워크 25ms, 상대 클라 124ms(프레임, 보간 버퍼, 렌더)다. 상대 클라 구간이 가장 크고, 그 대부분이 보간 버퍼다.
보간 버퍼는 위치·상태 스냅샷에 쓰인다. 시전 같은 이벤트는 받자마자 재생하는 게임도 많고, 그때는 이 100ms가 빠진다. 위 합계는 이벤트도 보간 시점에 맞춰 재생하는 구현을 가정한 값이다.
이 영상에 나온 용어는 용어집에 정리했다.