이번 글은 겉보기엔 서버 쪽 로직이 아니라 아주 평범한 웹 프론트엔드 버그처럼 보였지만, 실제로는 모바일 브라우저의 특성을 몰라서 한참 헤맸던 사례입니다.

채팅 랭킹과 등급 시스템

Iris 봇에는 누적 채팅 수에 따라 자동으로 등급이 올라가는 채팅 랭킹 시스템이 있습니다. 이번에 포팅하면서 기존 방식(누적 채팅수 기반 자동 승급)에 더해, 포인트를 소모해서 등급을 직접 “구매"하는 방식도 선택적으로 켤 수 있도록 기능을 추가했습니다. 등급 자체의 개수(최대 6개)도 방마다 다르게 설정할 수 있게 만들었습니다.

증상: 줄이기는 되는데 늘리기가 안 된다

이 등급 개수를 설정하는 UI는 숫자 입력창(스피너)이었습니다. 테스트해보니 이상한 패턴이 나타났습니다 — 값을 줄이는 건 잘 되는데, 늘리는 조작만 화면에 반영되지 않는 것처럼 보였습니다. 처음엔 서버 쪽 유효성 검사 로직이 최댓값을 잘못 제한하고 있는 줄 알고 그쪽을 한참 들여다봤지만 문제가 없었습니다.

문제 해결 흐름

원인: 모바일에서 onchange가 안정적으로 안 터진다

원인은 프론트엔드 이벤트 처리에 있었습니다. 숫자 입력창의 값 변경을 감지하는 데 onchange 이벤트만 사용하고 있었는데, 데스크톱 브라우저에서는 이 이벤트가 안정적으로 발생하는 반면, 일부 모바일 브라우저에서는 스피너 버튼(위/아래 화살표)으로 값을 조작할 때 onchange가 기대만큼 일관되게 발생하지 않는 경우가 있었습니다. 특히 값을 증가시키는 조작 쪽에서 이벤트가 누락되는 패턴이 반복적으로 나타났습니다.

해결: oninput도 함께 바인딩

해결 방법은 onchange에만 의존하지 않고 oninput 이벤트도 함께 바인딩하는 것이었습니다. oninput은 값이 변경되는 매 순간(입력 중에도) 발생하는 이벤트라 onchange보다 더 촘촘하게 감지되고, 모바일 환경에서도 상대적으로 안정적으로 동작합니다. 두 이벤트를 모두 바인딩하고 나서는 값 증가·감소 모두 화면 설정 페이지에서 정상적으로 반영됐습니다.

돌아보며

이 관리자 설정 페이지들이 모바일 브라우저에서도 자주 쓰인다는 걸 고려하면, 앞으로 비슷한 입력 UI를 만들 때는 처음부터 onchange 단독이 아니라 oninput을 함께 고려하는 게 안전하다는 교훈을 얻었습니다. 데스크톱에서만 테스트하고 “잘 된다"고 넘어갔다면 한참 뒤에나 발견했을 버그였습니다.

핵심 요약

🤖 이 포스팅은 AI의 도움을 받아 작성되었습니다.