[트러블슈팅] 한글 입력 시 Enter 이벤트가 두 번 호출되는 문제

·6일 전
0

React /  JavaScript

목록 보기
14/14
post-thumbnail

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) 이라고 한다.


브라우저는 이 조합 과정을 다음 이벤트로 관리한다.

  • compositionstart
  • compositionupdate
  • compositionend

문제는 이 조합 과정 중에도 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을 호출하면 상태가 비동기로 초기화될 수 있기 때문에 현재 값을 별도의 변수에 담은 후 호출하는 순서로 변경했다.


정리

  • 한글 입력은 조합 입력 방식이기 때문에 Enter가 두 번 발생할 수 있다.
  • e.nativeEvent.isComposing 값으로 조합 중인지 확인할 수 있다.
  • 조합 중일 때는 Enter 이벤트를 무시하면 된다.
  • 이 방식은 일본어, 중국어 등 IME 기반 입력에서도 동일하게 적용된다.

참고

profile
중요한 건 꺾여도 다시 일어서는 마음

0개의 댓글