
텍스트 하이라이트의 주요 접근 방식
텍스트 하이라이트는 언뜻 보기에는 간단해 보입니다—배경을 노란색으로 바꾸기만 하면 되니까요—하지만 실제로는 꽤 복잡합니다. 아래 예시처럼 하이라이트된 부분이 여러 요소에 걸쳐 있고 아래 요소의 일부 텍스트도 포함하고 있습니다.
아래에서는 텍스트 하이라이트를 구현하는 몇 가지 일반적인 접근 방식을 살펴보고, 각각의 장단점과 실제 사용 사례를 논의하여 주요 기술적 해결책을 정리해보겠습니다.
CSS Highlight API — 현대적이고 효율적인 텍스트 하이라이트
이는 현재 텍스트 하이라이트를 구현하는 최선의 선택으로, 사용하기 쉽고 성능이 우수합니다. 유일한 단점은 브라우저가 비교적 최신 버전을 지원해야 한다는 것이지만, 2025년 현재 모든 주요 브라우저가 지원하고 있습니다.
Highlight 객체
먼저 각 하이라이트 영역에 대해 Range
객체를 생성해야 합니다. Range는 오랫동안 존재해온 API로, DOM 트리에서 연속적인 영역을 나타냅니다—익숙하지 않다면 직접 공부해보세요.
const range = new Range();
range.setStart(someNode, startOffset);
range.setEnd(someNode, endOffset);
그런 다음 Highlight
객체에 추가합니다. 하이라이트는 여러 개의 불연속적인 영역을 가질 수 있으므로, 하나의 Highlight
객체가 여러 개의 Range를 포함할 수 있습니다.
const highlight = new Highlight(range1, range2, ..., rangeN);
이는 Set과 유사한 객체이므로 add
, delete
등의 메서드를 사용하여 포함된 Range를 수정할 수 있습니다.
CSS.highlights
그런 다음 Highlight 객체를 CSS.highlights
에 추가합니다. 이는 Map과 유사한 객체로, get
, set
, delete
등의 멤버 추가/삭제 메서드와 동일한 반복 메서드를 가지고 있습니다.
Map과 마찬가지로, Highlight를 추가할 때는 하이라이트의 이름으로 키를 지정해야 합니다. 이 이름은 매우 유용합니다—아래에서 보겠지만, 이 이름을 사용하여 하이라이트 스타일을 설정합니다. 예를 들어 녹색 배경 등입니다. 따라서 같은 Highlight 객체 내의 모든 하이라이트는 동일한 스타일을 가집니다. 다른 텍스트를 파란색으로 하이라이트하고 싶다면 새로운 하이라이트를 생성해야 합니다.
CSS.highlights.set("my-highlight", highlight);
::highlight 의사 요소 정의
다음으로 CSS 스타일이 등장합니다. ::highlight(하이라이트 이름)
의사 요소를 사용하여 하이라이트 스타일을 정의할 수 있습니다.
::highlight(my-highlight) {
background-color: yellow;
color: black;
}
참고: 사용할 수 있는 CSS 속성은 소수만 있습니다.
background-color
,color
,cursor
등. 자세한 내용은 명세를 참조하세요.
하이라이트 범위 동적 수정
CSS.highlights
는 Map과 유사하며, Highlight를 업데이트하는 유사한 메서드가 있습니다:
CSS.highlights.clear()
set(highlightName, Highlight)
delete(highlightName)
Highlight
객체는 Set과 유사하며, Range를 업데이트하는 유사한 메서드가 있습니다:
Highlight.add(range)
delete(range)
텍스트 요소 스타일 수정
DOM을 순회하여 대상 텍스트 노드를 찾고 해당 배경 스타일을 직접 수정하여 하이라이트를 구현합니다.
- DOM 수정이 필요하며, 브라우저 리플로우가 발생하여 상당한 성능 오버헤드가 있습니다.
- 요소 간 하이라이트(하이라이트 범위가 여러 요소에 걸쳐 있는 경우) 구현이 상당히 복잡합니다.
- 하이라이트된 콘텐츠가 비교적 고정되어 있고 범위가 작은 시나리오에 적합합니다.
텍스트 DOM을 변경하지 않고 오버레이로 하이라이트 렌더링
투명한 오버레이 레이어를 생성하고, 대상 텍스트의 화면 위치를 계산한 다음, 오버레이에 반투명 하이라이트를 그립니다.
하이라이트 영역 위치 지정
Element.getClientRects()
또는getBoundingClientRect()
를 사용하여 페이지에서 텍스트 좌표를 가져옵니다.- 여러 줄 텍스트를 처리할 때, 각 줄은 개별 하이라이트 블록이 필요한 사각형 영역에 해당합니다.
- 창 크기 변경이나 글꼴 크기 변경 시 하이라이트 위치를 다시 계산해야 하며, 성능에 영향을 줄 수 있습니다.
하이라이트 그리기 방법
DIV를 오버레이로 사용
절대 위치 지정된 부모 컨테이너와 반투명 배경의 자식 요소를 사용하여 텍스트를 오버레이합니다. 예를 들어, Monaco 에디터는 이 접근 방식을 사용하여 동일한 단어 하이라이트를 구현합니다.
<div class="editor" style="position: relative;">
<div class="overlay" style="position: absolute; height: 0;">
<div
class="highlight"
style="top:0; left:57px; width:23px; background-color: rgba(173,214,255,0.15);"
></div>
<!-- 더 많은 하이라이트 영역 추가 -->
</div>
<div class="content">
<!-- 텍스트 콘텐츠 -->
</div>
</div>
- 오버레이의 위치는 absolute이며, 부모 요소가
position: static
이 아니도록 하여 absolute 위치 지정이 작동하도록 합니다. 이를 통해 내부 하이라이트 요소를 어디든 배치할 수 있습니다. - 텍스트 위에 배치하려면 HTML에서 오버레이를 텍스트 컨테이너 앞에 배치하기만 하면 됩니다. 오버레이 높이는 0으로 설정하여 아래 텍스트와의 상호작용에 영향을 주지 않도록 합니다.
- 내부에 요소를 추가하고,
background-color
를 반투명 하이라이트 색상으로 설정하고, 하이라이트된 텍스트 위치에 배치하고, 너비를 설정합니다. - Monaco 에디터(VSCode가 이를 기반으로 함)는 이 방법을 사용합니다. 공식 데모를 참조하세요. 키워드를 선택하면 동일한 키워드가 하이라이트됩니다. HTML을 보면 이러한 키워드들이 스타일을 변경하지 않았지만, 에디터 인터페이스에 오버레이가 있고 내부에 반투명 하이라이트 색상이 그려져 있습니다(
class="view-overlays"
div 참조).
SVG를 오버레이로 사용
- 오버레이를 페이지와 동일한 크기로 설정
- 내부에
<rect>
를 추가한 다음, 하이라이트된 텍스트 위치에 그리기 - foliate-js가 이 방법을 사용합니다. overlayer.js를 참조하세요.
Selection API와 ::selection 의사 요소 사용 — 브라우저의 기본 텍스트 선택 하이라이트
브라우저는 자연스럽게 선택된 텍스트를 하이라이트하므로, 선택을 제어하면 간접적으로 하이라이트를 구현할 수 있습니다. 스타일은 CSS의 ::selection
의사 요소를 통해 사용자 정의할 수 있습니다.
const range = new Range();
range.setStart(parentNode, startOffset);
range.setEnd(parentNode, endOffset);
// 선택 설정
const selection = document.getSelection();
selection.removeAllRanges();
selection.addRange(range);
::selection {
background-color: #c3c2c2;
color: white;
}
이 방법은 꽤 영리하고, 간단하며, 넓은 브라우저 지원을 가지고 있지만, 큰 단점이 있습니다: 동시에 하나의 선택만 존재할 수 있기 때문에, 프로그래밍적 선택과 실제 사용자 선택이 충돌하고 서로 덮어쓰게 됩니다. 따라서 다음 조건을 만족하는 시나리오에만 적합합니다:
- 사용자가 텍스트를 선택할 수 없음
- 연속된 하이라이트된 텍스트 세그먼트가 하나뿐임
- 오래된 브라우저를 지원해야 함
- 게으르고 다른 방법을 피하고 싶음