최근 AI를 활용한 사이드 프로젝트에서 textarea에 입력한 한글만 이상하게 작동하는 걸 발견했다. 한글을 입력한 후 Enter를 누르면 마지막 글자가 포함되어 중복 이벤트가 발생하는 것이다. 왜 이런 문제가 생기는 걸까?
이 글에서는 그 원인을 입력기(IME) 한글 조합 방식과 이벤트 순서의 차이에서 찾고, 이를 React 환경에서 어떻게 해결할 수 있을지 예시 코드와 함께 정리해 보았다.
입력기(IME)의 한글 조합
한글은 자음 + 모음의 조합형 문자이다. 예를 들어 '한글' 입력 시 'ㅎ → ㅏ → ㄴ → ㄱ → ㅡ → ㄹ' 순서로 입력할 것이다. 이를 'ㅎㅏㄴㄱㅡㄹ'이 아닌 '한글'로 만들어주는 것이 IME(Input Method Editor), 즉 입력기이다. 이처럼 한글 입력에서 자모는 반드시 정해진 순서(초성→중성→종성)로 조합되어야 하는데, 이 규칙을 기반으로 입력기가 작동한다.
CompositionEvent
CompositionEvent는 일본어, 중국어, 그리고 한글과 같은 문자를 조합(composition)할 때 발생하는 브라우저 이벤트이다. 즉, 한글을 입력할 때마다 IME가 활성화되고, 최종적으로 글자가 확정되기 전까지 관련 이벤트가 계속해서 발생한다. 예를 들어, '가'라는 문자로 조합되기까지 compositionstart → compositionupdate → compositionend 이벤트를 처리한다. 이를 표로 정리하면 다음과 같다:
이벤트 이름 | 설명 |
compositionstart | 조합 입력(예: 한글 자음/모음 입력)이 시작될 때 발생 |
compositionupdate | 조합 중간에 값이 바뀔 때마다 발생(예: "ㄱ" → "가" 등) |
compositionend | 조합이 완료되거나 취소될 때 발생 |
특정 환경에서 생기는 문제
특이한 점은, macOS + Chrome/Edge 같은 특정 환경에서 보이는 문제라는 것이다. Firefox, Safari 브라우저는 Mac에서 해당 문제가 발생하지 않았다. 또한, Windows에서도 전혀 문제가 없었다.
문제 추측
해당 문제의 명확한 원인을 찾기 위해 노력을 많이 했지만, 명쾌한 해답을 얻지는 못했다. 하지만 macOS에서 한글 입력 시 활성화되는 IME 조합 방식과 브라우저의 이벤트 처리 순서의 차이로, keydown 이벤트가 충돌하거나 중복 발생하는 것 같다. textarea 태그의 기본 동작인 Enter 키의 줄바꿈과는 관련이 없었고, 브라우저마다 이벤트 처리 순서가 달랐기 때문이다.
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에서 읽어와야 한다.
'Web' 카테고리의 다른 글
Create / Update 시 응답에 변경된 리소스를 포함해야 할까? (0) | 2023.12.17 |
---|---|
회사 웹 서비스 개선기: URL 인코딩 (0) | 2023.08.15 |
URI의 스킴(scheme)과 프로토콜(protocol) (2) | 2023.04.25 |
URI, URL 그리고 URN 이해하기 (0) | 2022.12.25 |
IE로 접속 시 알림창(alert) 띄우기 (0) | 2022.04.29 |