Engineering note · HTML / Javascript

한글 IME 조합 이벤트

2026년 07월 23일이현수
태그htmljavascriptimeeventfocus

Windows 브라우저에서 한글 입력 조합 중 다른 항목을 클릭할 때 IME 조합 완료 이벤트가 뒤늦게 실행되며 포커스가 되돌아오는 원인과 isComposing 제어 해결법.

Windows 환경 브라우저에서만 한글을 Input에 입력하고 검색 결과 항목을 클릭했더니, 포커스가 다시 검색 입력창으로 돌아오는 버그가 있었다. macOS에서는 나타나지 않고 Windows 환경에서만 발생했다.

포커스 탈출 시점에 IME 조합 완료 이벤트 발생

한글 IME 조합 및 포커스 탈출 이벤트 흐름

한글이나 일어 같은 CJK 문자는 IME(Input Method Editor)를 거치는 조합 문자다. 한글 조합이 완료되지 않은 상태에서 사용자가 바깥 영역을 클릭하면 브라우저는 다음 순서로 이벤트를 발생시킨다.

  1. 검색 입력창에서 한글 자모 조합 진행 (compositionupdate)
  2. 조합을 마치지 않은 상태에서 아래 검색 결과 항목 클릭
  3. 포커스가 빠져나가면서 브라우저 IME가 조합을 끝내고 뒤늦게 조합 완료 이벤트 발송 (compositionend 및 커스텀 typed 이벤트 재발생)
  4. typed 이벤트 콜백 내에 있던 지연 포커스 부여 코드 실행
ts
// 입력 완료 시 사용자 편의를 위해 넣어두었던 기존 코드
setTimeout(() => {
  inputRef.value?.focus()
}, 500)

입력 완료 후 사용자가 바로 연속 입력을 할 수 있도록 넣어둔 500ms 지연 focus() 코드가 문제였다. 바깥 항목을 클릭해 포커스를 옮긴 직후, 뒤늦게 튀어 오른 조합 완료 이벤트 때문에 이동했던 포커스를 입력창으로 다시 이동한 것.

처음에는 클릭 이벤트 직전에 focus()blur()를 코드에서 연달아 호출해 한글 조합을 미리 끝내버리려 했다.

ts
// ❌ 임시 방편으로 시도했던 포커스 연타
inputRef.value?.focus()

하지만 이미 포커스가 있는 요소에 focus()를 연달아 호출하는 것은 IME 조합을 종료하는 해결책으로 적절하지 않다. 빠른 타자 속도나 네트워크 지연 환경에서는 오히려 한글 자모가 ㅎㅏㄴ 처럼 쪼개져 입력되는 부작용이 생긴다.

IME 조합 제어는 브라우저 네이티브 흐름에 맡기고, 프론트엔드 코드에서는 조합 이벤트 상태 플래그를 이용해 불필요한 이벤트 재실행을 차단해야 한다.

compositionstartcompositionend 이벤트를 추적하거나 InputEvent.isComposing 속성을 검사해서 로직을 구현할 수 있다.

ts
const input = document.querySelector<HTMLInputElement>('#search-input')!
let isComposing = false

input.addEventListener('compositionstart', () => {
  isComposing = true
})

input.addEventListener('compositionend', () => {
  isComposing = false
  // 조합이 끝난 최종 텍스트로만 검색 수행
  triggerSearch(input.value)
})

input.addEventListener('input', (event) => {
  const inputEvent = event as InputEvent

  // 한글 조합 중이거나 조합 직후 튀어 오른 input 이벤트는 무시
  if (isComposing || inputEvent.isComposing) return
  triggerSearch(input.value)
})

추가로 검색 이벤트 콜백에서 무조건 포커스를 재부여하던 setTimeout(..., 500) 로직을 삭제했다. 포커스 유지가 꼭 필요한 상황이라면 사용자가 입력창 내부를 직접 클릭했는지(event.isTrusted) 여부를 확인한 뒤 명시적으로만 포커스를 제어하는 편이 안전하다.