게임 채팅 IME 조합 입력 처리: 한글·일본어 입력 중 Enter 키와 커서 충돌 방지 가이드
게임 채팅 UI에서 한글·일본어(CJK) IME 조합 중 발생하는 Enter 키 조기 전송과 커서 튐 현상의 원인을 분석하고 Unity와 Unreal Engine에서 조합 상태를 감지하여 입력을 제어하는 실무 해결책을 제공합니다.

핵심 요약
게임 채팅창에서 한글·일본어 등 CJK 문자를 입력할 때 Enter 키를 누르면 채팅이 전송되면서 마지막 글자가 누락되거나 중복 입력되는 버그는 IME 조합 상태(Composition State)와 엔진의 키 다운(KeyDown) 이벤트 처리 시점 불일치 때문에 발생합니다. 이를 해결하려면 키 입력 처리기에서 IME 조합 문자열(Composition String)의 활성화 여부를 먼저 검사하여 조합 중 발생하는 첫 번째 Enter 키는 ‘문자 조합 확정(Commit)‘용으로만 소비하고 채팅 메시지 전송 로직의 실행을 차단해야 합니다.
왜 게임 채팅에서 한글·일본어를 입력할 때 Enter 키가 오작동할까?
영어와 달리 한글, 일본어, 중국어와 같은 CJK(Chinese, Japanese, Korean) 언어는 자모가 결합하여 하나의 완성된 글자를 만드는 복합 문자 구조를 가집니다. 운영체제(OS)는 이러한 입력을 처리하기 위해 IME(Input Method Editor, 입력기)를 사용합니다.
IME 환경에서 문자가 완성되기 전 단계의 문자열을 조합 문자열(Composition String)이라고 부릅니다. 일반적인 텍스트 에디터에서는 조합 상태와 확정 상태를 OS 레벨에서 매끄럽게 중계하지만 자체 렌더링 루프와 인풋 파이프라인을 가진 게임 엔진(Unity, Unreal Engine 등)에서는 다음과 같은 충돌이 발생합니다.
- 사용자가 한글 ‘가’를 입력하기 위해 ‘ㄱ’과 ‘ㅏ’를 조합 중인 상태에서
Enter키를 입력합니다. - OS IME는 조합을 끝내고 완성된 문자 ‘가’를 텍스트 버퍼에 확정(Commit)하려 합니다.
- 동시에 게임 엔진의 UI 이벤트 시스템은
Enter키 다운 이벤트를 감지하고 ‘채팅 메시지 전송’ 함수를 즉시 호출합니다. - 그 결과, 마지막 글자가 확정되기 전에 빈 문자열이나 이전 텍스트만 전송되거나 다음 채팅창이 열릴 때 마지막 조합 글자가 잔여물로 남아 입력되는 버그가 발생합니다.
| 구분 | 직접 입력 언어 (영어 등) | IME 조합 언어 (한글, 일본어 등) |
|---|---|---|
| 입력 단위 | 단일 키 입력 즉시 문자 생성 | 키 입력 누적 후 조합 과정을 거쳐 문자 확정 |
| Enter 키 역할 | 줄바꿈 또는 메시지 전송 | 1차: 조합 확정(Commit), 2차: 메시지 전송 |
| 커서 위치 결정 | 문자열 끝 인덱스에 고정 | 조합 중인 글자의 음절 길이 및 후보창 위치에 종속 |
| 충돌 증상 | 없음 | 마지막 글자 중복 전송, 글자 잘림, 커서 위치 튐 |

IME 입력 흐름과 키 이벤트 처리 메커니즘
게임 엔진의 입력 파이프라인에서 IME 이벤트와 일반 키보드 이벤트는 비동기적으로 처리되거나 프레임 단위 이벤트 큐의 우선순위에 따라 순서가 뒤바뀔 수 있습니다.
flowchart TD
A[사용자 키 입력: Enter] --> B{IME가 현재 문자를 조합 중인가?}
B -- Yes (Composing) --> C[IME 조합 확정 처리 실행]
C --> D[조합 문자열을 입력 필드 버퍼에 반영]
D --> E[Enter 키 이벤트 소비 Handled 처리]
E --> F[메시지 전송 차단: 다음 입력을 대기]
B -- No (Committed) --> G[일반 Enter 키 다운 이벤트 전달]
G --> H[채팅 메시지 전송 및 입력창 초기화]
위 다이어그램처럼 입력 파이프라인의 최상단에서 **조합 활성 여부(IsComposing)**를 판별하는 분기 처리가 필수적입니다. 조합 중일 때 들어온 Enter는 이벤트를 ‘처리됨(Handled)’ 상태로 만들어 상위 전송 로직으로 전파되지 않도록 차단해야 합니다.
Unity TextMeshPro에서 IME Enter 충돌을 어떻게 해결할까?
Unity의 기본 TMP_InputField는 CJK IME 처리를 일부 지원하지만 포커스 제어와 빠른 키 입력 시 조합 확정과 제출(Submit) 이벤트가 같은 프레임에 묶이는 한계가 있습니다.
Step 1: TMP_InputField의 제출 콜백에서 조합 상태 확인
TMP_InputField에는 OnKeyDown(Event) 오버라이드가 없으므로 입력 필드를 상속해 이 메서드를 재정의할 수 없습니다. 대신 onSubmit 콜백을 받는 컴포넌트에서 조합 상태와 직전 프레임의 조합 종료를 확인하고, 통과한 메시지만 게임 로직에 전달합니다.
using System;
using UnityEngine;
using TMPro;
using UnityEngine.Events;
[Serializable]
public class ChatSubmitEvent : UnityEvent<string> { }
public class ImeSubmitGuard : MonoBehaviour
{
[SerializeField] private TMP_InputField inputField;
[SerializeField] private ChatSubmitEvent onAcceptedSubmit = new ChatSubmitEvent();
private bool wasComposing;
private bool hasCompositionEndFrame;
private int compositionEndFrame;
private void OnEnable()
{
inputField.onSubmit.AddListener(HandleSubmit);
}
private void OnDisable()
{
inputField.onSubmit.RemoveListener(HandleSubmit);
}
private void Update()
{
bool isComposing = inputField != null && inputField.isFocused &&
!string.IsNullOrEmpty(Input.compositionString);
if (isComposing)
{
wasComposing = true;
}
else if (wasComposing)
{
wasComposing = false;
compositionEndFrame = Time.frameCount;
hasCompositionEndFrame = true;
}
}
private void HandleSubmit(string message)
{
bool justFinishedComposing = hasCompositionEndFrame &&
Time.frameCount <= compositionEndFrame + 1;
if (!string.IsNullOrEmpty(Input.compositionString) || wasComposing || justFinishedComposing)
return;
onAcceptedSubmit.Invoke(message);
}
}
Step 2: 통과한 메시지를 서버로 전송
ImeSubmitGuard의 onAcceptedSubmit에 아래 UI 매니저 콜백을 연결합니다. 이 콜백은 IME 확인을 통과한 메시지만 받으므로 네트워크 전송과 입력 필드 초기화에 집중할 수 있습니다.
public void OnChatInputSubmitted(string message)
{
SendMessageToServer(message);
chatInputField.text = string.Empty;
chatInputField.ActivateInputField();
}

Unreal Engine Slate / UMG에서의 IME 조합 처리 구현법
언리얼 엔진에서는 Slate 계층의 SEditableTextBox 또는 FSlateApplication의 IME 컨텍스트를 활용합니다. 기본적으로 언리얼은 ITextInputMethodContext 인터페이스를 통해 플랫폼 IME와 통신합니다.
Step 1: SEditableTextBox의 텍스트 커밋 이벤트 사용
키 입력을 먼저 가로채면 IME 확정 처리를 방해할 수 있습니다. 기본 텍스트 위젯이 조합을 처리하도록 두고, OnTextCommitted에서 Enter에 의한 커밋만 애플리케이션의 전송 후보로 받습니다. 아래는 Slate 위젯을 만드는 함수 예시입니다.
#include "Widgets/Input/SEditableTextBox.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
TSharedRef<SEditableTextBox> CreateChatInput(FOnTextCommitted OnChatSubmitted)
{
return SNew(SEditableTextBox)
.OnTextCommitted_Lambda([OnChatSubmitted](const FText& Text, ETextCommit::Type CommitType)
{
if (CommitType == ETextCommit::OnEnter)
OnChatSubmitted.ExecuteIfBound(Text, CommitType);
});
}
ITextInputMethodSystem에는 활성 컨텍스트를 반환하는 GetActiveContext()가 없습니다. 직접 만든 텍스트 편집기에 컨텍스트를 등록한 경우에는 그 컨텍스트를 보관하여 IsComposing()을 조회할 수 있지만, 기본 SEditableTextBox에서 해당 컨텍스트를 얻는 공개 getter가 있다고 가정해서는 안 됩니다. 위 커밋 콜백만으로 모든 OS·IME 조합의 중복 전송 방지가 보장되지는 않으므로 목표 엔진 버전과 입력기로 조합 확정, 연속 Enter, 포커스 변경을 시험해야 합니다. API 기준은 SEditableTextBox와 ITextInputMethodSystem을 참고하십시오.
Step 2: UMG의 커밋 이벤트 바인딩
UMG에서는 UEditableTextBox의 OnTextCommitted 이벤트에서 같은 커밋 유형을 확인하여 게임 전송 로직에 연결합니다.
커서 튐(Cursor Glitch) 및 중복 전송 방지를 위한 핵심 체크리스트
- Backspace 연속 입력 시 음절 분해 처리
한글은 ‘각’ 상태에서 Backspace를 누르면 ‘가’가 되어야 합니다. 엔진의 입력 버퍼를 직접 수정할 때 전체 글자를 날려버리지 않도록 OS IME의 서브스트링 이벤트를 그대로 위임해야 합니다. - 조합 중 마우스 포커스 아웃(Focus Out) 처리
입력 도중 UI 바깥을 클릭하거나 전송 버튼을 눌렀을 때 조합 중인 문자열이 어떻게 확정·취소되는지 시험해야 합니다. UnityTMP_InputField에는 공개EndComposition()메서드가 없습니다. Unreal의ITextInputMethodContext.EndComposition()도 커밋을 보장하지 않으므로 이를 호출하면 유실이 방지된다고 가정하지 말고 엔진의 기본 포커스 처리와 OS IME 동작을 확인합니다. - 일본어 가타카나/한자 변환 후보창(Candidate List) 위치 보정
일본어 입력 시 Space 키로 변환 창을 띄울 때 좌표가 어긋나면 UI 스케일과 창의 화면 좌표를 확인합니다. Unreal의 커스텀ITextInputMethodContext구현에서는GetTextBounds()와GetScreenBounds()로 화면 좌표를 제공하며, 기본 텍스트 위젯을 쓴다면 먼저 엔진의 배율·레이아웃 처리를 점검합니다.
자주 묻는 질문 (FAQ)
Q1. Input.compositionString이 비어있는데도 Enter가 두 번 처리되는 이유는 무엇인가요?
OS IME 드라이버에 따라 Enter 키 입력 시 ‘조합 확정(WM_IME_ENDCOMPOSITION)’ 이벤트와 ‘키 다운(WM_KEYDOWN)’ 메시지가 동일한 윈도우 메시지 펌프 틱에 도착합니다. 이로 인해 첫 프레임에서 조합이 비워지자마자 같은 프레임 뒤쪽에서 키 다운이 실행되어 전송으로 이어집니다. 본문의 wasComposing 플래그처럼 1프레임 래치(Latch) 변수를 두어 조합 종료 직후의 첫 번째 Enter 입력을 걸러내야 합니다.
Q2. 모바일(iOS/Android) 가상 키보드에서도 동일한 로직이 적용되나요?
모바일 환경에서는 TouchScreenKeyboard.text와 키보드 종료 상태를 확인합니다. PC처럼 OnKeyDown을 가로채기보다는 status의 Done, Canceled, LostFocus를 구분하여 전송·취소·포커스 변경을 각각 처리하고, 대상 OS의 입력기로 시험해야 합니다.
Q3. 일본어 변환 중 Enter를 눌렀을 때와 전송 Enter를 어떻게 구분하나요?
일본어 IME는 히라가나를 입력한 뒤 Space로 변환 후보를 선택하고 Enter로 확정할 수 있습니다. 조합 확정 자체는 엔진의 텍스트 위젯에 맡겨야 합니다. Unity 예제는 현재·직전 프레임의 조합 상태로 애플리케이션 전송 콜백을 억제하고, Unreal 예제는 텍스트 커밋 콜백을 사용합니다. 조합 종료와 키 이벤트의 순서는 OS·IME에 따라 달라지므로 이 예제만으로 완전한 구분을 보장하지 않으며, 실제 입력기별 테스트가 필요합니다.
마치며
게임 채팅과 텍스트 입력에서 IME 조합 처리는 CJK 언어권 플레이어의 사용성을 좌우합니다. 엔진의 기본 조합 처리를 유지하고 애플리케이션 전송 콜백을 분리한 뒤, 조합 확정·연속 Enter·포커스 이동·모바일 키보드 종료를 실제 입력기로 검증하는 것이 좋습니다.



