한글 입력 중 onChange가 두 번 터질 때 — IME 조합 이벤트 다루기
한글로 폼을 채우면 onChange가 여러 번 터지고 Enter가 두 번 실행되는 이유와, isComposing으로 잡는 법을 빌드로그로 정리했어요.
한글 입력 중 onChange가 두 번 터질 때 — IME 조합 이벤트 다루기
플랜비(plan-b)에서 멤버 등록 폼을 만들다가 이상한 걸 만났어요. 이름을 한글로 치는데 마지막 글자가 가끔 두 번 들어가거나, Enter 한 번에 등록이 두 번 되더라고요. 영어로 칠 땐 멀쩡했고요. 혹시 이런 적 없나요?
콘솔을 찍어 보니 범인은 조합 입력
console.log를 onChange에 걸어 두고 "가"를 쳐 봤더니, 이벤트가 한 글자에 두세 번씩 찍혔어요. 원인은 IME(입력기)였어요. 한글·일본어·중국어는 자모를 조합해 한 글자를 만들기 때문에, 조합이 끝나기 전까지 브라우저가 중간 상태를 계속 흘려보내요.
이 조합 과정에는 이벤트 3개가 붙어 있어요. compositionstart(조합 시작), compositionupdate(조합 중), compositionend(조합 확정). 영어는 이 과정이 없으니 문제가 안 생겼던 거죠.
isComposing 하나로 갈리는 흐름
다행히 이벤트 객체에 조합 중인지 알려주는 값이 있어요. React라면 e.nativeEvent.isComposing이에요. 조합이 진행 중이면 true, 확정되면 false예요.
제가 실제로 겪은 두 번 터짐은 사실 Enter 쪽이 더 컸어요. 한글을 조합하다 Enter를 누르면, 첫 keydown은 "조합을 확정"하는 용도로 쓰이고(이때 isComposing이 true), 그다음 진짜 Enter가 한 번 더 들어와요. 그래서 등록이 두 번 됐던 거예요.
고쳐 보니 코드는 세 줄이면 충분했어요. onKeyDown 안에서 if (e.nativeEvent.isComposing) return; 을 맨 위에 두고, 그 아래에서 Enter 키일 때만 submit을 부르는 식이에요. 조합 확정용 Enter는 앞줄에서 걸러지니까요.
값 자체가 튀는 것도 잡아 두기
onChange로 들어오는 값이 조합 중간 상태라 검증 로직이 헷갈리는 경우도 있었어요. 저는 실시간 검증(길이 체크 같은 것)은 compositionend 이후로 미뤘어요. 조합이 끝난 값만 검사하니까 마지막 글자가 잘리거나 중복되는 문제가 사라졌어요.
- isComposing 확인
- Enter는 확정 후 처리
- 검증은 compositionend 뒤로
한 가지 주의할 점은, compositionend가 change보다 먼저 오는지 나중에 오는지가 브라우저마다 조금 달라요. 그래서 이벤트 순서에 의존하는 코드보다 isComposing 값을 직접 보는 쪽이 마음 편했어요.
정리하며
한글 입력 버그는 대부분 "글자 하나가 여러 이벤트로 쪼개진다"는 걸 몰라서 생겨요. 디버깅에 30분쯤 헤맸지만, 원인을 알고 나니 isComposing 한 줄로 끝났어요. 다국어 입력을 받는 폼을 만든다면, 영어만 테스트하지 말고 한글로도 꼭 쳐 보세요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.