enter를 치면 input창이 클릭되도록 하는 함수를 만들었다.
keyup될 때 구현했는데 영어는 괜찮은데 자꾸 한글이 씹하는(?) 현상이 발생했다.
const handleKeyUp = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (textareaRef.current) {
const maxHeight = 4 * 22; // 최대 4줄, 줄당 22px
if (e.key === 'Enter') {
e.preventDefault();
if (e.ctrlKey) {
if (textareaRef.current.scrollHeight < maxHeight) {
const start = textareaRef.current.selectionStart;
const end = textareaRef.current.selectionEnd;
const value = textareaRef.current.value;
const newValue = value.substring(0, start) + '\n' + value.substring(end);
setContent(newValue);
textareaRef.current.selectionStart = textareaRef.current.selectionEnd = start + 1;
}
} else {
handleSendMessageButton();
}
}
}
};
<button
className={bot-btn-send ${isOpenBottomMenu ? 'active' : ''}}
onClick={handleSendMessageButton}>
<IoSend className="send-svg" />
</button>
알고보니 버튼이 두번 클릭되는 문제였다.
그럼 왜 영어는 괜찮은데 한글은 두번 클릭되는 현상이 발생할까?
바로 IME(입력기)가 작동하는 방식과 관련이 있다는 것!!
IME란 Input Method Editor로, 사용자가 여러 개의 키 입력을 통해 문자를 입력할 수 있도록 도와주는 소프트웨어이다.
이는 특히 한글, 중국어, 일본어와 같은 언어를 입력할 때 유용! 이러한 언어는 단일 키 입력으로 완전한 문자를 표현할 수 없으므로, IME는 여러 키 입력을 조합하여 최종 문자를 생성할 수 있다.
영어 입력의 경우, 각 키 입력이 즉시 처리되지만, 한국어 입력의 경우 IME가 여러 키 입력을 조합하여 하나의 글자를 완성한다. 이 과정에서 이벤트가 여러 번 발생할 수 있으며, 특히 조합이 완료되지 않은 상태에서 Enter 키를 누르면 onKeyDown과 onKeyUp 이벤트가 예상치 않게 처리될 수 있다는 것!
이에 대한 해결책으로
onCompositionStart와 onCompositionEnd를 사용하여 IME 입력이 시작하는 시점 끝나는 시점을 보고 끝났을 때만 handleSendMessageButton함수가 실행되게 변경해주었다.
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
const platform = getPlatform();
const isCtrlOrCmd = platform === 'Mac' ? e.metaKey : e.ctrlKey;
if (textareaRef.current) {
if (e.key === 'Enter' && !isComposing) {
if (isCtrlOrCmd) {
const maxHeight = 4 * 22; // 최대 4줄, 줄당 22px
if (textareaRef.current.scrollHeight < maxHeight) {
const start = textareaRef.current.selectionStart;
const end = textareaRef.current.selectionEnd;
const value = textareaRef.current.value;
const newValue = value.substring(0, start) + '\n' + value.substring(end);
setContent(newValue);
textareaRef.current.selectionStart = textareaRef.current.selectionEnd = start + 1;
}
} else {
e.preventDefault();
handleSendMessageButton();
}
}
}
};
const handleCompositionStart = () => {
setIsComposing(true);
};
const handleCompositionEnd = () => {
setIsComposing(false);
};
<textarea
ref={textareaRef}
className="bot-textarea"
onChange={handleInputChange}
onKeyDown={handleKeyDown}
onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd}
value={content}
placeholder="Send a message.."
rows={1}
onInput={adjustTextareaHeight}
/>
아주 잘 동작하는 것을 확인해볼 수 있었다!
'Problem&Solution' 카테고리의 다른 글
로컬state를 만들어 서버 부하 줄이기 (0) | 2024.07.17 |
---|---|
Typescript 인덱싱 에러 (0) | 2024.07.15 |
react-query, retetchOnWindowFocus (0) | 2024.06.26 |
react 버그 해결 (1) | 2024.06.10 |
react hook의 호출 규칙 (0) | 2024.05.08 |