Open Source

새 Text 입력 시 첫 문자만 포맷이 적용되는 문제 해결

2026년 1월 10일

Lexical 에디터 오픈소스 기여 과정에서 발견한 텍스트 대치 시 포맷 적용 문제와 이를 해결한 과정을 공유합니다.

개요

Lexical은 Meta에서 만든 텍스트 에디터 프레임워크입니다.
텍스트를 전체 선택한 후 서식이 있는 새로운 텍스트를 입력할 때,
첫 글자에만 서식이 적용되고 나머지 글자에는 서식이 적용되지 않는 버그를 해결했습니다.

문제 상황

이슈에서 보고된 재현 과정은 다음과 같습니다.

  1. "hello world" 입력
  2. "hello" 문자열에 포맷(Bold 등) 적용
  3. 모든 문자 선택
  4. "hello" 다시 입력

결과: 입력된 결과에서 첫 글자인 'h'에는 포맷이 유지되지만, 이어지는 'ello'에는 포맷이 적용되지 않고 새로운 노드로 생성되는 현상이 발생했습니다.

원인 분석: InsertText 함수

Text를 삽입하는 insertText 함수에서 다중 노드(Multi-node)를 처리하는 로직에 문제가 있었습니다.

사용자가 텍스트를 선택하고 입력을 시작하면, Lexical은 선택된 영역을 처리하고 새로운 텍스트를 삽입합니다.
분석 결과, 다중 노드가 선택된 상태에서 텍스트를 입력할 때 첫 번째 노드에 텍스트를 삽입하는 과정에서 문제가 발생했습니다.

기존 로직은 다중 노드의 텍스트를 병합한 후, 새 텍스트를 삽입할 때 첫 문자를 입력 후 첫 번째 노드의 포맷으로 업데이트하지 않았습니다.
이로 인해 첫 글자('h')가 입력된 직후, 포맷 정보가 초기화되어 이어지는 문자열('ello')이 입력될 때는 포맷이 없는 새로운 노드로 분리되어 생성된 것입니다.

해결 방법

다중 노드 선택 상태에서 텍스트를 삽입할 때, 첫 번째 노드에 첫 글자를 삽입 후 포맷과 스타일을 첫 번째 노드로 동기화하는 로직을 추가했습니다.

// ... (생략) ...
if (!$isTokenOrTab(firstNode)) {
  firstNode = firstNode.spliceText(
    startOffset,
    firstNodeTextLength - startOffset,
    text,
    true,
  );
  if (firstNode.getTextContent() === "") {
    firstNode.remove();
  } else if (this.anchor.type === "text") {
    if (firstNode.isComposing()) {
      // When composing, we need to adjust the anchor offset so that
      // we correctly replace that right range.
      this.anchor.offset -= text.length;
    } else {
      // (수정) 포맷과 스타일을 firstNode와 동기화
      this.format = firstNode.getFormat();
      this.style = firstNode.getStyle();
    }
  }
}
// ... (생략) ...

이렇게 수정하면 'h'가 입력된 후에도 에디터가 같은 포맷이라는 것을 인지하게 되어, 뒤이어 입력되는 'ello'도 동일한 노드 내에 같은 포맷으로 자연스럽게 입력됩니다.

느낀점

insertText 함수가 꽤 길고 복잡해서 전체 흐름을 이해하는 데 노력이 필요했습니다.
현재 발생하는 이슈를 디버깅하며 각 노드를 처리하는 부분을 탐색한 끝에 원인을 찾을 수 있었습니다.
Lexical 에디터의 기본적인 동작과 관련된 버그를 고칠 수 있었습니다.