설계 사례: 점수 숫자와 플레이 근거를 따로 보기
2026년 9월 22일 아케이드의 컬러 블룸 검증 소스와 테스트를 읽어 확인한 흐름은 ‘입력 형식 검사 → 같은 시작 조건으로 계획 재현 → 조작 기록과 대조 → 점수 재계산’입니다. 입력 기록을 보내는 것만으로 충분하지 않고 순서·중복·규칙에 맞는 기록인지도 확인합니다. 이 글을 위해 아케이드 기능이나 운영 랭킹을 수정하지 않았습니다.
왜 이런 단계가 필요한지 세 칸짜리 가상 게임으로 줄여 보겠습니다. 정답 방향은 차례로 왼쪽·오른쪽·왼쪽이고, 해당 칸의 정답을 누를 때 1점입니다. 놓친 칸은 0점이며 점수에 들어간 입력만 기록합니다. 실제 컬러 블룸의 점수식·보안 기준이 아닌 교육용 모형입니다.
좁은 화면에서는 아래 표만 좌우로 스크롤할 수 있습니다. 키보드로는 표에 포커스를 둔 뒤 방향키를 사용하세요.
| 제출 | 재생 결과 | 판단 |
|---|---|---|
| 0번 왼쪽, 1번 오른쪽 / 2점 | 유효 입력 두 개, 2점 | 수락 |
| 같은 입력 / 3점 주장 | 계산은 여전히 2점 | 점수 불일치로 거절 |
| 0번 왼쪽을 두 번 / 2점 | 같은 칸 중복 | 숫자를 세기 전에 거절 |
| 1번 뒤에 0번 / 2점 | 시간 순서가 거꾸로 | 순서 오류로 거절 |
| 0번 오른쪽 / 1점 | 해당 칸의 정답과 다름 | 계획 불일치로 거절 |
| 입력 없음 / 0점 | 재생 점수도 0점 | 수락. 낮은 점수가 오류는 아님 |
VS Code → 새 텍스트 파일에 다음을 붙여넣고 replay-example.mjs로 저장합니다. Node.js가 있는 환경에서 Terminal → New Terminal을 열고 저장한 폴더에서 node replay-example.mjs를 실행하면 여섯 경우를 검사합니다. 서버나 실제 랭킹으로 요청을 보내지 않습니다.
import assert from 'node:assert/strict';
function accepts(trace, claimedScore) {
const plan = ['left', 'right', 'left'];
let previous = -1;
let score = 0;
for (const [step, key] of trace) {
if (!Number.isInteger(step) || step <= previous ||
step >= plan.length || key !== plan[step]) return false;
previous = step;
score++;
}
return Number.isInteger(claimedScore) && score === claimedScore;
}
const normal = [[0, 'left'], [1, 'right']];
assert.equal(accepts(normal, 2), true);
assert.equal(accepts(normal, 3), false);
assert.equal(accepts([[0, 'left'], [0, 'left']], 2), false);
assert.equal(accepts([[1, 'right'], [0, 'left']], 2), false);
assert.equal(accepts([[0, 'right']], 1), false);
assert.equal(accepts([], 0), true);
console.log('6 replay cases passed');
이 예제를 실행한 결과는 6 replay cases passed입니다. 숫자만 비교하는 방식은 세 번째의 중복 입력에 취약할 수 있습니다. 반대로 높은 점수만 의심하는 방식은 정상적인 좋은 기록을 거절할 수 있습니다. 작은 모형에서도 ‘기록이 규칙상 가능한가’와 ‘그 기록의 점수가 맞는가’를 따로 물어야 합니다.
재생에 성공해도 사람이 플레이했다는 증명은 아닙니다
규칙에 맞는 입력을 프로그램이 만들 수도 있습니다. 실제 서비스에는 입력 크기, 세션의 유효성, 경과 시간, 게임 버전, 제출 권한 같은 별도 검사가 필요합니다. 위 함수에는 그런 경계와 전체 입력 형식 검사가 없으므로 운영용 검증기를 대신할 수 없습니다. 특히 게임 규칙을 바꿀 때는 이전 버전의 판을 새 규칙으로 재계산하지 않도록 버전 처리도 검토해야 합니다.
이 설계의 비용은 클라이언트와 서버가 같은 규칙을 유지해야 한다는 것입니다. 잘못 공유하면 정상 기록도 거절됩니다. 그래서 소스의 테스트는 정상·0점·점수 불일치·다른 시작 조건 같은 경우를 나눠 둡니다. 여기서 실행한 것은 위 독립 예제뿐이며, 별도 저장소 테스트와 운영 서버의 통과 여부를 대신 보고하는 것은 아닙니다.