멘션 input에 이름을 입력 후

크롬 브라우저에서 한글 입력 시 입력중에 글자 아래 밑줄이 쳐지는데 이것 때문에 키보드 이벤트가 두 번 발생한다

Untitled

이는 입력 방식 편집기(input method editor, IME) 에서 이벤트를 처리할 때 발생하는 문제로 한글 뿐만 아니라 일본어, 중국어에서도 발생하는 문제이다.

IME : 한글, 한자처럼 컴퓨터 자판에 있는 글자보다 수가 더 많은 문자를 계산하거나 조합하여 입력해 주는 시스템 소프트웨어

결론적으로 IME에서 keydown, keyup 이벤트가 발생했을 때 OS, 브라우저 에서 해당 이벤트를 처리하기 때문에 2번 작동하게 되는 것이다.

IME가 현재 입력값을 조합 중 인지를 boolean 값으로 반환하는 KeyboardEvent.isComposing 를 이용해서 해결할 수 있다.

  • 조합 중이라면 isComposing : true
  • 조합이 끝나면 isComposing : false

이 나오므로 이를 통해 2번의 이벤트 중 한번을 걸러낼 수 있다.

onKeyPress 이벤트는 1번만 작동하지만 react에서 onKeyPress 없애버렸당 MDN에서도 여러가지 이유로 더이상 지원하지 않는 이벤트라고 한다(다 이유가 있는겨)

이를 react의 event객체를 뒤져보면 isComposing이 없다.

React의 event객체는 브라우저의 event객체가 아닌 합성 이벤트(SyntheticEvent)이다.

또한 react에서는 composing 여부를 제공해 준다

이를 통해 composing 이 되었을 때useState값을 true로 바꿔 이벤트가 작동하게 해줘도 되지만

상태값을 저장할 state가 필요해지기 때문에 다른 방법으로 해결했다.

공식문서에 따르면 브라우저 고유 이벤트가 필요하다면 event.nativeEvent 내에서 담겨져있다.

따라서 evnet.nativeEvent.isComposing을 통해 조합 여부를 확인하고 조합이 완료되었을 때만 핸들러 로직이 작동하도록 구현하였다.

const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
    const mentionLength = autoCompleteList.length;

    if (mentionLength <= 0 || !inputRef.current) return;

    if (event.nativeEvent.isComposing) return; // 조합 중이면 종료

    switch (event.key) {
      case "ArrowDown":
        setFocusIndex((prev) => (prev + 1) % mentionLength);
        break;
      case "ArrowUp":
        setFocusIndex((prev) => (prev - 1 + mentionLength) % mentionLength);
        break;
      case "Enter":
        handleAddChoiceList(autoCompleteList[focusIndex]);
        break;
    }
  };

...

<Input
  type="text"
  onChange={searchPeople}
  onKeyDown={handleKeyDown}
  ref={inputRef}
  placeholder="멘션할 대상을 선택해주세요."
  className="text-base text-content-5 placeholder-content-1"
/>

이를 통해 한글 입력시 이벤트가 2번 작동하는 에러를 해결하였다.


참고

참고자료1

참고자료2

참고자료3

댓글남기기