본문 바로가기
Web

[macOS] KeyDown 이벤트 한글 입력 관련 문제 원인과 해결 방법(feat. IME)

by Vintz 2025. 7. 4.
반응형

최근 AI를 활용한 사이드 프로젝트에서 textarea에 입력한 한글만 이상하게 작동하는 걸 발견했다. 한글을 입력한 후 Enter를 누르면 마지막 글자가 포함되어 중복 이벤트가 발생하는 것이다. 왜 이런 문제가 생기는 걸까?

 

이 글에서는 그 원인을 입력기(IME) 한글 조합 방식과 이벤트 순서의 차이에서 찾고, 이를 React 환경에서 어떻게 해결할 수 있을지 예시 코드와 함께 정리해 보았다.

입력기(IME)의 한글 조합

한글은 자음 + 모음의 조합형 문자이다. 예를 들어 '한글' 입력 시 'ㅎ → ㅏ → ㄴ → ㄱ → ㅡ → ㄹ' 순서로 입력할 것이다. 이를 'ㅎㅏㄴㄱㅡㄹ'이 아닌 '한글'로 만들어주는 것이 IME(Input Method Editor), 즉 입력기이다. 이처럼 한글 입력에서 자모는 반드시 정해진 순서(초성→중성→종성)로 조합되어야 하는데, 이 규칙을 기반으로 입력기가 작동한다.

키보드에서 입력한 자모를 실시간으로 조합해 문자를 생성해주는 IME - https://en.wikipedia.org/wiki/Input_method

CompositionEvent

CompositionEvent는 일본어, 중국어, 그리고 한글과 같은 문자를 조합(composition)할 때 발생하는 브라우저 이벤트이다. 즉, 한글을 입력할 때마다 IME가 활성화되고, 최종적으로 글자가 확정되기 전까지 관련 이벤트가 계속해서 발생한다. 예를 들어, '가'라는 문자로 조합되기까지 compositionstart → compositionupdate → compositionend 이벤트를 처리한다. 이를 표로 정리하면 다음과 같다:

이벤트 이름 설명
compositionstart 조합 입력(예: 한글 자음/모음 입력)이 시작될 때 발생
compositionupdate 조합 중간에 값이 바뀔 때마다 발생(예: "ㄱ" → "가" 등)
compositionend 조합이 완료되거나 취소될 때 발생

특정 환경에서 생기는 문제

특이한 점은, macOS + Chrome/Edge 같은 특정 환경에서 보이는 문제라는 것이다. Firefox, Safari 브라우저는 Mac에서 해당 문제가 발생하지 않았다. 또한, Windows에서도 전혀 문제가 없었다.

문제 추측

해당 문제의 명확한 원인을 찾기 위해 노력을 많이 했지만, 명쾌한 해답을 얻지는 못했다. 하지만 macOS에서 한글 입력 시 활성화되는 IME 조합 방식과 브라우저의 이벤트 처리 순서의 차이로, keydown 이벤트가 충돌하거나 중복 발생하는 것 같다. textarea 태그의 기본 동작인 Enter 키의 줄바꿈과는 관련이 없었고, 브라우저마다 이벤트 처리 순서가 달랐기 때문이다.

 

왼쪽부터 Chrome(Edge도 동일), Firefox, Safari 순. Safari는 왜 저러는지 모르겠다.

macOS와 블링크 엔진 기반 브라우저에서 생기는 문제이며, 특히 'Enter' 키에서만 이상 작동하는 것을 발견했다. 컨트롤, 커맨드 키는 중복 이벤트가 발생하지 않았다. Enter 키에 대한 기본 동작과 블링크 엔진 기반 브라우저의 composition update와 end 이벤트가 함께 발생되어, 복합적인 이유로 keydown 이벤트를 중복으로 발생시키는 게 아닐까란 생각이 들었다.

해결 방법

해결 방법은 쉽다. 아래와 같이 문자가 조합 중일 때는 핸들러를 종료시킨다.

const isEmpty = !message.trim();
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
  const isComposing = e.nativeEvent.isComposing;

  if (isComposing) {
    return;
  }

  const isEnter = e.key === 'Enter' && !e.shiftKey;
  const ignoreOnEnter = isEnter && isEmpty;
  const submitOnEnter = isEnter && !isEmpty;

  if (ignoreOnEnter) {
    e.preventDefault();
    return;
  }

  if (submitOnEnter) {
    e.preventDefault();
    handleSubmit(e);
  }
};

이렇게 문자 조합이 완료된 상태에만 이벤트 처리를 해주면 중복 이벤트가 발생하지 않는다. isComposing 속성을 활용하여, compositionend 이벤트만 처리해서 해결한 것이다. 여기서 주의할 점은 React에서 isComposing 속성을 사용하려면 e.nativeEvent에서 읽어와야 한다.

반응형