React에서 한글 입력 시 이벤트 중복 실행 방지하기
React로 검색 기능을 개발하던 중, 한글을 입력하고 Enter를 누르면 검색이 두 번 실행되는 문제가 발생했다. 영어로 입력할 때는 정상적으로 동작했기 때문에 한글 입력 과정에서 뭔가 다른 점이 있다고 판단했다.
문제 상황
아래와 같은 검색 입력 컴포넌트가 있었다.
const SearchInput = () => {
const [keyword, setKeyword] = useState("");
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
console.log("검색 실행:", keyword);
// 검색 API 호출
}
};
return (
<input
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="검색어를 입력하세요"
/>
);
};
영어로 "hello"를 입력하고 Enter를 누르면 검색 실행이 한 번만 출력된다. 하지만 한글로 "안녕"을 입력하고 Enter를 누르면 검색 실행이 두 번 출력됐다.
원인
이 문제의 원인은 IME(Input Method Editor) 에 있었다. 한글, 일본어, 중국어 같은 언어는 여러 키 입력을 조합하여 하나의 글자를 완성한다. 예를 들어 "한"이라는 글자를 입력하려면 ㅎ, ㅏ, ㄴ을 순서대로 눌러야 한다. 이 조합 과정을 composition이라고 한다.
브라우저는 이 조합 과정에서 compositionstart, compositionupdate, compositionend 이벤트를 발생시킨다. 문제는 한글 입력 중 Enter를 누르면 다음과 같은 순서로 이벤트가 발생한다는 것이다.
keydown(Enter) — 조합 중인 상태에서 발생 (isComposing: true)compositionend— 조합 종료keydown(Enter) — 조합 완료 후 발생 (isComposing: false)
첫 번째 keydown은 IME가 조합을 끝내기 위해 발생시키는 것이고, 두 번째 keydown이 실제 사용자가 의도한 Enter 입력이다. 이 두 이벤트 모두 e.key === "Enter"로 감지되기 때문에 이벤트 핸들러가 두 번 실행되는 것이었다.
해결 방법
KeyboardEvent에는 isComposing이라는 속성이 있다. 이 속성은 현재 IME 조합이 진행 중인지를 나타내는 boolean 값이다. React에서는 e.nativeEvent.isComposing으로 접근할 수 있다.
const SearchInput = () => {
const [keyword, setKeyword] = useState("");
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.nativeEvent.isComposing) return;
if (e.key === "Enter") {
console.log("검색 실행:", keyword);
// 검색 API 호출
}
};
return (
<input
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="검색어를 입력하세요"
/>
);
};
e.nativeEvent.isComposing이 true일 때는 아직 IME 조합이 진행 중이라는 뜻이므로, 이때 발생하는 keydown 이벤트는 무시하면 된다. 이렇게 하면 조합이 완료된 후 실제 사용자가 누른 Enter에서만 검색이 실행된다.
React의 SyntheticEvent에는 isComposing 속성이 직접 노출되지 않기 때문에, e.nativeEvent를 통해 브라우저 네이티브 이벤트에 접근해야 한다는 점을 주의하자.
마무리
한글 입력 시 이벤트가 중복 실행되는 문제는 IME 조합 과정에서 keydown 이벤트가 두 번 발생하기 때문이다. e.nativeEvent.isComposing을 체크하여 조합 중인 이벤트를 필터링하면 간단하게 해결할 수 있다.
이 문제는 한글뿐만 아니라 일본어, 중국어 등 IME를 사용하는 모든 언어에서 발생할 수 있으므로, keydown 이벤트를 다룰 때는 항상 isComposing 체크를 습관적으로 넣어두는 것이 좋겠다.
