무엇을 하고 싶었나
게임에서 같은 방향키를 두 번 빠르게 누르면 그 방향으로 대시(짧은 순간 이동)하는 조작이 있었어요. 두 키를 번갈아 누르면 대각선으로 대시하는데, 두 키를 같이 두 번 누르면 한 축으로만 대시했어요. 눈으로는 같은 조작인데 결과가 달라서 버그로 보였어요.
미리 알면 좋은 개념이에요.
keydown이벤트: 키를 누를 때 브라우저가 보내는 신호예요. 키 하나당 이벤트 하나이고, "동시에 눌렀다"는 개념은 없어요. W와 D를 같이 눌러도 이벤트는 따로 와요.- 고정 시뮬레이션 스텝: 게임은 화면을 그리는 루프(
requestAnimationFrame)와 규칙을 한 칸 진행하는 고정 간격 계산이 따로 돌아요. 입력 이벤트는 이 둘의 사이사이 아무 때나 끼어들어요.
무엇을 했나
원인은 이벤트 순서였어요. 이전 코드는 더블탭이 성립하는 첫 번째 키의 이벤트에서 방향을 바로 확정하고 다음 스텝에 소비했어요. W 이벤트가 먼저 대시를 예약하면, 뒤따라온 D 이벤트는 이미 정해진 방향에 합쳐지지 못했어요. 번갈아 누를 때는 다른 키가 이미 눌린 상태로 남아 있어서 우연히 대각선이 됐던 거예요.
- 첫 키 이벤트에서 50ms짜리 수집창을 열고, 창 안에 들어온 수직·수평 축만 보충해 방향을 합쳐요. 창은 첫 키에서만 시작하고 연장하지 않아요.
- 대시 버퍼는 렌더 루프가 아니라 시뮬레이션 스텝에서 한 번만 소비해요. 다른 축의 키가 없으면 창이 끝난 뒤 단일 축으로 실행해요.
- 창이 포커스를 잃거나(blur), 일시정지, 재시작할 때는 버퍼와 첫 탭 기록을 비워요.
- 기각한 대안: 즉시 확정은 같은 chord의 키 이벤트 사이에 프레임이 끼면 또 실패하고, 더 긴 대기는 조작 지연을 늘려서 짧게 제한했어요.
검증은 같이 누르기의 두 번째 keydown 사이에 프레임을 끼우는 단위 테스트로 했어요. 수정 전에는 실패하고 수정 후에는 통과하는 것을 확인했고, 실제 런타임에서 8가지 조합(네 대각선 × 키 순서 2가지)과 일시정지 중 취소도 확인했어요.
무엇을 배웠나
한 chord에 속하는 입력은 첫 이벤트에서 확정하지 않아요. 짧고 상한이 있는 수집창에 모으고, 소비 시점과 리셋 경로를 따로 검증해요. → 키보드 chord 수집창
같은 조작도 순서가 다르면 다른 경로예요. 기존 테스트는 한 키를 누른 채 다른 키를 두 번 누르는 경로만 확인해서 이 회귀를 놓쳤어요.
아직 확인하지 못한 것: 물리 키보드에서의 체감과 배포 빌드 반영 여부예요. 브라우저 검증은 DOM KeyboardEvent를 런타임에 보낸 것이고, 한 번의 경험이라 다른 입력 정책이나 다른 게임에 일반화할 근거는 없어요.