
React에서 텍스트 입력 핸들러를 구현하다가
한글 입력 중 Enter를 누르면 이벤트가 두 번 호출되는 현상이 있었다.
영문 입력 시에는 문제가 없었지만, 한글 입력에서만 중복 호출이 발생했다.
다음은 기존 코드이다.
const handleKeyDown = async (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.isPropagationStopped()) return;
if (e.key === 'Enter' && !e.shiftKey && text.trim().length > 0) {
e.preventDefault();
e.stopPropagation();
console.log('enter');
console.log(text);
onSubmit?.(text);
setText('');
}
};
영문 입력에서는 정상 동작했다.
하지만 한글 입력 중 Enter를 누르면 console.log가 두 번 찍히거나,
마지막 글자가 한 번 더 붙는 현상이 발생했다.
한글 입력은 단일 키 입력이 아니다.
‘ㅎ’, ‘ㅏ’, ‘ㄴ’을 순서대로 입력하면 조합 과정을 거쳐 ‘한’이라는 글자가 만들어진다.
이 과정을 조합 입력(Composition) 이라고 한다.
브라우저는 이 조합 과정을 다음 이벤트로 관리한다.
compositionstartcompositionupdatecompositionend문제는 이 조합 과정 중에도 keydown 이벤트가 발생한다는 점이다.
한글 조합이 완료되기 전, 즉 compositionend 이전에 Enter를 누르면
이벤트가 두 번 호출된다.
조합 중 한 번, 조합 완료 후 한 번이다.
KeyboardEvent 객체에는 조합 중인지 여부를 알 수 있는 속성이 있다.
isComposing 이다.
React에서는 e.nativeEvent.isComposing 으로 접근할 수 있다.
이 값이 true일 때는 아직 조합이 끝나지 않은 상태이다.
따라서 이 시점의 Enter 이벤트는 무시해야 한다.
const handleKeyDown = async (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
// 조합 중에는 Enter 이벤트 무시
if (e.nativeEvent.isComposing) return;
if (e.key === 'Enter' && !e.shiftKey && text.trim().length > 0) {
e.preventDefault();
e.stopPropagation();
console.log('enter');
console.log(text);
const currentText = text;
setText('');
onSubmit?.(currentText);
}
};
if (e.nativeEvent.isComposing) return; 한 줄로 해결했다.
조합 중 Enter 이벤트를 무시했기 때문에 이제 이벤트는 한 번만 호출된다.
또한 setText('') 이후 onSubmit을 호출하면 상태가 비동기로 초기화될 수 있기 때문에 현재 값을 별도의 변수에 담은 후 호출하는 순서로 변경했다.
e.nativeEvent.isComposing 값으로 조합 중인지 확인할 수 있다.