요약
Pretext.js는 DOM 작업 대신 순수 연산을 통해 여러 줄의 텍스트를 측정하고 배치하는 의존성 없는(zero-dependency) TypeScript 라이브러리입니다. 이는 강제 동기 리플로우(reflow)를 제거하고, getBoundingClientRect()보다 약 500배 빠른 텍스트 측정 기능을 제공하며, 지구상의 모든 주요 문자 체계를 지원합니다. 가상 스크롤러, 채팅 UI 또는 데이터 그리드를 구축하는 경우, 이 라이브러리는 브라우저가 30년 동안 무시해왔던 문제를 해결해줍니다.
서론
JavaScript가 getBoundingClientRect()를 호출하거나 offsetHeight를 읽을 때마다 브라우저는 모든 작업을 중단합니다. 보류 중인 스타일 변경 사항을 플러시하고, 레이아웃을 다시 계산하며, 전체 렌더링 과정을 강제합니다. 이를 강제 동기 리플로우(forced synchronous reflow)라고 하는데, 이는 브라우저가 수행할 수 있는 단일 작업 중 가장 비용이 많이 드는 작업입니다.
이제 가상 목록에 있는 1,000개의 채팅 버블이나 데이터 그리드의 10,000개 행에 이 작업을 곱해보십시오. 그 결과는? 프레임 드롭, 버벅거림(jank), 그리고 앱이 고장 났다고 생각하는 사용자들입니다.
Meta에서 react-motion (GitHub 별 21,700개 이상) 개발자이자 React 및 ReasonML의 핵심 기여자였던 Cheng Lou는 이 문제를 해결하기 위해 Pretext.js를 만들었습니다. 이 라이브러리는 2026년 3월에 출시되어 며칠 만에 14,000개 이상의 GitHub 별을 얻었으며, 올해 가장 큰 Hacker News 스레드 중 하나를 촉발시켰습니다.
이 글은 Pretext.js가 무엇을 하는지, 내부적으로 어떻게 작동하는지, 언제 사용해야 하는지, 그리고 어떤 한계가 있는지 분석합니다. 이 글을 읽고 나면 이 라이브러리가 여러분의 기술 스택에 적합한지 알게 될 것입니다.
Pretext.js는 무엇인가요?
Pretext.js 는 순수 JavaScript/TypeScript 텍스트 레이아웃 엔진입니다. getBoundingClientRect()나 offsetHeight, 리플로우(reflow) 및 스래싱(thrashing) 없이 오로지 연산을 통해 여러 줄의 텍스트를 측정하고 배치합니다.

핵심 아이디어는 간단합니다. 브라우저에 "이 텍스트는 얼마나 높이인가요?"라고 묻는 대신 (이는 브라우저가 먼저 렌더링하도록 강제합니다), Pretext.js는 Canvas API의 폰트 메트릭스를 사용하여 수학적으로 답을 계산합니다.
전체 API 표면은 다음과 같습니다:
import { prepare, layout } from '@chenglou/pretext';
// Step 1: Prepare text (one-time, cacheable)
const handle = prepare('Hello, pretext.js', '16px "Inter"');
// Step 2: Layout at any width (pure arithmetic, microseconds)
const { height, lineCount } = layout(handle, 400, 24);
이게 전부입니다. 두 개의 함수. 하나는 텍스트 세그먼트를 측정하고 캐시합니다. 다른 하나는 레이아웃을 계산하기 위해 연산을 수행합니다. prepare() 호출은 브라우저를 건드리는 유일한 작업이며 (Canvas measureText()를 통해), 그 이후의 layout()은 순수 수학입니다.
API를 많이 사용하는 애플리케이션에 왜 이것이 중요한가요?
스트리밍 API 응답을 소비하는 앱을 구축하고 있다면 (예: AI 비서, 실시간 대시보드 또는 공동 작업 편집기), 텍스트를 렌더링하기 전에 들어오는 텍스트의 높이를 알아야 합니다. 그렇지 않으면 가상 스크롤러가 버벅거리고, 채팅 UI가 튀어오르며, 사용자들이 이를 알아차릴 것입니다.
Pretext.js는 밀리초 대신 마이크로초 단위로 높이를 제공합니다. 그 차이는 빠르게 누적됩니다.
Pretext.js가 해결하는 문제
이 라이브러리가 존재하는 이유를 이해하려면 JavaScript가 레이아웃 속성을 읽을 때 어떤 일이 발생하는지 이해해야 합니다.
강제 동기 리플로우 설명
이 코드를 작성할 때:
const elements = document.querySelectorAll('.text-block');
elements.forEach(el => {
const height = el.getBoundingClientRect().height; // REFLOW!
// use height for positioning...
});
각 getBoundingClientRect() 호출은 브라우저에게 다음을 강제합니다:
- JavaScript 실행 중지
- 모든 보류 중인 스타일 변경 사항 플러시
- 전체 문서 (또는 하위 트리)의 레이아웃 다시 계산
- 계산된 값 반환
이를 "레이아웃 스래싱(layout thrashing)"이라고 합니다. 1,000개의 요소를 측정하는 루프에서 브라우저는 1,000번의 전체 레이아웃 재계산을 수행합니다. 비용은? 대략 94밀리초이며, 이는 60fps에서 6프레임이 손실됨을 의미합니다.
가상 스크롤링 문제
가상 스크롤링 라이브러리(예: react-window 또는 tanstack-virtual)는 스크롤 위치를 계산하기 위해 모든 항목의 높이를 알아야 합니다. 고정 높이 항목의 경우 이는 사소한 일입니다. 가변 높이 텍스트 콘텐츠의 경우 악몽과 같습니다.
표준 해결책은 항목을 화면 밖에서 렌더링하고, 측정한 다음 배치하는 것입니다. 이는 작동하지만 가상 스크롤링의 목적을 무색하게 합니다. 렌더링을 피하려는 DOM 노드를 렌더링하고 있는 셈입니다.
일부 라이브러리는 높이를 추정하고 렌더링 후 수정하여 눈에 띄는 점프를 유발합니다. 다른 라이브러리는 개발자가 고정 높이를 지정하도록 강제하여 표시할 수 있는 내용을 제한합니다.
Pretext.js는 이러한 종류의 모든 해결책을 없앱니다. DOM 노드가 존재하기 전에 정확한 텍스트 높이를 계산합니다.
실제 수치
Pretext.js는 그들의 사이트에 벤치마크 결과를 게시했습니다:
| 접근 방식 | 1,000개 텍스트 블록 | 500개 텍스트 블록 |
|---|---|---|
DOM (getBoundingClientRect) |
~94ms (6 프레임 드롭) | ~47ms |
Pretext.js (layout()) |
~2ms | ~0.09ms |
| 속도 차이 | ~47배 빠름 | ~500배 빠름 |
DOM 측정의 호출당 오버헤드는 일정하게 유지되는 반면 Pretext의 연산 비용은 선형적으로 증가하므로, 더 작은 배치에서는 속도 향상이 훨씬 더 드라마틱합니다.
Pretext.js의 내부 작동 방식
이 라이브러리는 세 가지 명확한 단계로 작동합니다. 이를 이해하면 라이브러리 사용 방법을 최적화하는 데 도움이 됩니다.
1단계: 텍스트 분할(segmentation)
prepare()를 호출하면, Pretext.js는 먼저 입력 텍스트를 정규화합니다. 공백을 처리하고, 유니코드 줄 바꿈 규칙(UAX #14)을 적용하며, 텍스트를 나눌 수 있는 단위로 분할합니다.
여기서 다국어 지원이 빛을 발합니다. 분할 엔진은 다음을 올바르게 처리합니다:
- CJK 문자 (중국어, 일본어, 한국어): 각 문자가 유효한 줄 바꿈 지점입니다.
- 아랍어 및 히브리어: 양방향 마커가 있는 오른쪽에서 왼쪽으로 쓰는 텍스트
- 태국어: 단어 사이에 공백이 없으며, 사전 기반 분할이 필요합니다.
- 힌디어/데바나가리: 복잡한 결합 자음과 합자
- 이모지: 다중 코드 포인트 이모지 시퀀스 (깃발, 피부색, ZWJ 시퀀스)를 올바르게 처리합니다.
- 소프트 하이픈:
­줄 바꿈 기회를 존중합니다.
2단계: Canvas 측정
분할 후, Pretext.js는 각 세그먼트를 Canvas measureText() API를 통해 처리합니다. 이것이 라이브러리가 수행하는 유일한 브라우저 호출이며, Canvas 텍스트 측정은 레이아웃 리플로우를 유발하지 않으므로 빠릅니다.
// Internal: how Pretext measures text
const ctx = offscreenCanvas.getContext('2d');
ctx.font = '16px "Inter"';
const metrics = ctx.measureText('Hello'); // No reflow!
const width = metrics.width; // Glyph advance width
측정값은 세그먼트와 폰트 조합별로 캐시됩니다. 동일한 텍스트와 폰트로 prepare()를 두 번 호출하면 두 번째 호출은 캐시된 데이터를 재사용합니다.
3단계: 순수 연산 레이아웃
layout() 함수는 캐시된 세그먼트 너비와 컨테이너 너비를 받아 탐욕 알고리즘을 사용하여 줄 바꿈을 계산합니다:
- 총 너비가 컨테이너 너비를 초과할 때까지 세그먼트 너비 합산
- 새 줄로 바꿈
- 모든 세그먼트가 배치될 때까지 반복
- 총 높이를 얻기 위해 줄 수에 줄 높이 곱하기
DOM 없음. Canvas 없음. 순수한 덧셈과 비교.
이것이 layout()이 매우 빠른 이유입니다. 자와 계산기를 사용하여 종이에 직접 작성하는 것과 같은 수학적 계산을 수행합니다.
재사용 가능한 핸들 패턴
Pretext.js의 가장 좋은 설계 결정 중 하나는 prepare()가 재사용 가능한 핸들을 반환한다는 것입니다. 단일 prepare() 호출이 모든 컨테이너 너비에서 작동합니다:
const handle = prepare(longArticleText, '16px "Inter"');
// Compute height for mobile, tablet, and desktop in microseconds
const mobile = layout(handle, 375, 24); // { height: 2400, lineCount: 100 }
const tablet = layout(handle, 768, 24); // { height: 1200, lineCount: 50 }
const desktop = layout(handle, 1200, 24); // { height: 720, lineCount: 30 }
이 패턴은 반응형 디자인 계산에 완벽합니다. 한 번 측정하고 어떤 너비에서도 즉시 레이아웃을 계산할 수 있습니다.
실용적인 사용 사례
1. 가변 높이 텍스트를 사용하는 가상 스크롤링
이것이 주요 사용 사례입니다. Pretext.js를 가상 스크롤러와 통합하는 방법은 다음과 같습니다:
import { prepare, layout } from '@chenglou/pretext';
interface TextItem {
id: string;
content: string;
}
function computeHeights(items: TextItem[], containerWidth: number) {
return items.map(item => {
const handle = prepare(item.content, '14px "Inter"');
const { height } = layout(handle, containerWidth, 20);
return { id: item.id, height: height + 32 }; // +32 for padding
});
}
// 10,000 items measured in ~4ms
const heights = computeHeights(chatMessages, 600);
오프스크린 렌더링 없음. 높이 추정 없음. 항목이 뷰로 스크롤될 때 눈에 띄는 점프 없음.
2. AI 채팅 인터페이스
AI 비서는 응답을 토큰별로 스트리밍합니다. 새 토큰마다 줄 수가 변경되어 아래의 모든 것을 이동시킬 수 있습니다. 전통적인 DOM 측정 방식에서는 모든 토큰 업데이트가 리플로우를 유발합니다.
Pretext.js를 사용하면 각 청크가 도착한 후 DOM을 건드리지 않고 높이를 다시 계산할 수 있습니다:
let streamedText = '';
const font = '15px "SF Pro"';
socket.on('token', (token: string) => {
streamedText += token;
const handle = prepare(streamedText, font);
const { height } = layout(handle, bubbleWidth, 22);
// Update virtual scroller position without DOM measurement
scroller.updateItemHeight(messageId, height + padding);
});
3. 텍스트 열이 있는 데이터 그리드
스프레드시트 스타일 앱은 열 자동 크기 조절 기능이 필요합니다. DOM을 통해 수천 개의 셀 값을 측정하는 것은 비용이 많이 듭니다. Pretext.js는 이를 빠르게 만듭니다:
function computeColumnWidth(values: string[], font: string, padding: number) {
let maxWidth = 0;
for (const value of values) {
const handle = prepare(value, font);
// Layout with infinite width = single line = natural text width
const { height } = layout(handle, Infinity, 20);
// Use handle's internal width tracking for column sizing
maxWidth = Math.max(maxWidth, /* computed width */);
}
return maxWidth + padding;
}
4. 다국어 콘텐츠 피드
다중 스크립트 콘텐츠(예: 중국어 게시물, 아랍어 답글, 한국어 댓글)가 혼합된 소셜 미디어 피드는 각 스크립트마다 줄 바꿈 규칙이 다르기 때문에 가상화하기가 매우 어렵습니다.
Pretext.js는 동일한 API로 이 모든 것을 처리합니다:
const posts = [
{ text: 'This library changed everything', lang: 'en' },
{ text: 'RTL text with correct bidirectional layout', lang: 'ar' },
{ text: 'CJK text gets proper character-level breaks', lang: 'zh' },
];
// Same API, correct results for every script
posts.forEach(post => {
const handle = prepare(post.text, '16px system-ui');
const { height } = layout(handle, 400, 24);
});
Apidog로 텍스트 레이아웃 테스트하기
API 기반의 텍스트가 많은 UI를 구축할 때, 올바른 레이아웃을 구현하는 것은 싸움의 절반에 불과합니다. 또한 텍스트 구성 요소에 제공되는 API 응답이 올바른 형식으로, 올바른 속도로 올바른 데이터를 제공하는지 확인해야 합니다.

Apidog는 이를 간단하게 만듭니다. 스트리밍 API 응답을 모의(mock)하여 Pretext.js 통합이 점진적 텍스트 로딩을 어떻게 처리하는지 테스트할 수 있습니다. 다양한 텍스트 길이, 언어 및 유니코드 엣지 케이스로 테스트 시나리오를 설정한 다음, 배포하기 전에 가상 스크롤러가 올바르게 작동하는지 확인할 수 있습니다.
AI 채팅 제품을 구축하는 팀을 위해 Apidog의 API 테스트 환경은 다음을 가능하게 합니다:
- 실제 LLM 출력을 시뮬레이션하기 위해 청크(chunk) 텍스트로 스트리밍 응답 모의(mock)
- 사용자가 레이아웃 버그를 발견하기 전에 다국어 페이로드로 테스트
- 텍스트 필드가 예상 형식을 포함하는지 확인하기 위해 응답 스키마 유효성 검사
- 텍스트 렌더링 엣지 케이스를 다루는 자동화된 테스트 스위트 실행
텍스트 레이아웃 라이브러리는 그것에 공급되는 데이터만큼만 좋습니다. 측정 엔진이 아무리 빠르게 실행되더라도 불량한 API 응답은 불량한 레이아웃을 생성합니다.
알려진 한계 및 비판
Pretext.js는 완벽하지 않습니다. Hacker News 스레드에서 이 라이브러리를 채택하기 전에 알아야 할 몇 가지 타당한 우려 사항이 제기되었습니다.
렌더링 정확도 엣지 케이스
여러 사용자가 Safari 및 Chrome 데모에서 텍스트가 경계 상자를 넘어 확장되는 현상을 보고했습니다. 라이브러리의 연산은 특정 시나리오에서 브라우저의 네이티브 레이아웃과 달라질 수 있습니다:
- 특이한 커닝(kerning) 쌍을 가진 폰트
- 단일 블록 내에서 혼합된 폰트 크기를 가진 텍스트
- Canvas와 DOM 간의 서브픽셀 렌더링 차이
- 브라우저별 텍스트 형태 형성 특이점
이러한 엣지 케이스는 가상 스크롤링(몇 픽셀의 오류는 눈에 띄지 않음)에서는 덜 중요하고, 픽셀 완벽한 타이포그래피에서는 더 중요합니다.
Canvas 측정은 비용이 듭니다
prepare() 호출은 여전히 브라우저의 Canvas 텍스트 엔진을 사용합니다. 프레임당 수천 개의 고유한 prepare() 핸들을 생성하는 애플리케이션의 경우, 이는 병목 현상이 될 수 있습니다. 해결책은 캐싱과 배치 처리이지만, 라이브러리가 이를 강제하지는 않습니다.
CSS 속성 미지원
Pretext.js는 폰트 사양으로 원시 텍스트를 측정합니다. 레이아웃에 영향을 미치는 CSS 속성은 고려하지 않습니다:
letter-spacingword-spacingtext-indenttext-transformfont-feature-settingsfont-variant
텍스트 스타일링이 이러한 CSS 속성에 의존하는 경우, 계산된 높이는 브라우저가 렌더링하는 것과 일치하지 않습니다. 이러한 차이를 수동으로 고려하거나 받아들여야 합니다.
DOM 렌더링을 대체하지 않습니다
Pretext.js는 텍스트가 얼마나 높을지 알려줍니다. 텍스트를 직접 렌더링하지는 않습니다. 텍스트를 표시하려면 여전히 DOM 노드(또는 Canvas/SVG 렌더링)가 필요합니다. 이 라이브러리의 가치는 렌더링 단계가 아닌 측정 단계에 있습니다.
Pretext.js 대 전통적인 접근 방식
| 특징 | Pretext.js | DOM 측정 | 추정 높이 |
|---|---|---|---|
| 속도 (1천 개 항목) | ~2ms | ~94ms | ~0ms (측정 없음) |
| 정확도 | 높음 (Canvas 기반) | 완벽함 (실측치) | 낮음 (휴리스틱) |
| DOM 의존성 | prepare() 이후 없음 |
완전함 | 없음 |
| 리플로우 유발 | 없음 | 측정당 하나 | 없음 |
| 다국어 | 완전한 유니코드 지원 | 완전함 (브라우저 네이티브) | 부족함 (하드코딩된 비율) |
| CSS 속성 지원 | 제한적 (폰트만) | 완전함 | 없음 |
| 메모리 오버헤드 | 캐시된 세그먼트 | DOM 노드 | 최소 |
| 반응형 레이아웃 | 한 번의 prepare(), 여러 번의 layout() |
너비당 재측정 | 너비당 재추정 |
올바른 선택은 여러분의 제약 조건에 따라 달라집니다. 픽셀 완벽한 정확도와 CSS 속성 지원이 필요하다면 DOM 측정이 여전히 실측치입니다. 수천 개의 항목에서 속도가 필요하고 사소한 서브픽셀 차이를 허용할 수 있다면 Pretext.js가 훨씬 더 우월합니다.
시작하기
설치
npm install @chenglou/pretext
# or
pnpm add @chenglou/pretext
# or
bun add @chenglou/pretext
기본 사용법
import { prepare, layout } from '@chenglou/pretext';
// Measure a paragraph
const handle = prepare(
'Pretext.js computes text layout without touching the DOM.',
'16px "Inter"'
);
// Get height at a specific container width
const result = layout(handle, 600, 24);
console.log(result.height); // e.g., 48 (2 lines x 24px)
console.log(result.lineCount); // e.g., 2
React 통합
import { prepare, layout } from '@chenglou/pretext';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useMemo, useRef } from 'react';
function VirtualChat({ messages }: { messages: string[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const containerWidth = 600;
const font = '14px "Inter"';
const lineHeight = 20;
const heights = useMemo(() => {
return messages.map(msg => {
const handle = prepare(msg, font);
const { height } = layout(handle, containerWidth, lineHeight);
return height + 24; // padding
});
}, [messages]);
const virtualizer = useVirtualizer({
count: messages.length,
getScrollElement: () => parentRef.current,
estimateSize: (index) => heights[index],
});
return (
<div ref={parentRef} style={{ height: '100vh', overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
{virtualizer.getVirtualItems().map(virtualRow => (
<div
key={virtualRow.key}
style={{
position: 'absolute',
top: virtualRow.start,
width: containerWidth,
}}
>
{messages[virtualRow.index]}
</div>
))}
</div>
</div>
);
}
이렇게 하면 메시지가 DOM에 렌더링되기 전에 정확한 항목 높이가 계산되는 가상 채팅을 얻을 수 있습니다. 추정 없음, 보정 점프 없음, 리플로우 없음.
인터랙티브 플레이그라운드
Pretext.js 웹사이트에는 pretextjs.dev/playground에서 인터랙티브 플레이그라운드가 포함되어 있어 텍스트를 붙여넣고, 폰트를 선택하고, 컨테이너 너비를 조정하고, 실시간으로 레이아웃 계산을 확인할 수 있습니다. 이는 통합 전에 동작을 확인하는 가장 빠른 방법입니다.
Pretext.js를 사용하지 말아야 할 때
Pretext.js는 모든 텍스트 측정 문제에 대한 올바른 도구가 아닙니다:
- 콘텐츠를 알고 있는 정적 페이지: 텍스트가 변경되지 않고 가상화를 하지 않는다면 CSS가 레이아웃을 잘 처리합니다. 라이브러리가 필요 없습니다.
- 픽셀 완벽한 인쇄 레이아웃: Canvas 측정과 DOM 렌더링 간의 서브픽셀 차이는 인쇄 해상도에서 중요합니다. DOM을 실측치로 사용하십시오.
- 과도한 CSS 텍스트 스타일링:
letter-spacing,text-indent또는font-feature-settings에 의존하는 경우, 높이 계산이 렌더링된 출력과 달라질 것입니다. - 서버 측 렌더링: Pretext.js는 Canvas API에 의존하는데,
node-canvas와 같은 폴리필 없이는 Node.js에서 사용할 수 없습니다. 서버 측 지원은 로드맵에 있지만 아직 출시되지 않았습니다. - 작고 정적인 목록: 목록에 50개 항목이 있다면 DOM 측정은 5ms 미만이 걸립니다. 이 정도 최적화는 의존성을 추가할 가치가 없습니다.
Pretext.js 대 전통적인 접근 방식
| 특징 | Pretext.js | DOM 측정 | 추정 높이 |
|---|---|---|---|
| 속도 (1천 개 항목) | ~2ms | ~94ms | ~0ms (측정 없음) |
| 정확도 | 높음 (Canvas 기반) | 완벽함 (실측치) | 낮음 (휴리스틱) |
| DOM 의존성 | prepare() 이후 없음 |
완전함 | 없음 |
| 리플로우 유발 | 없음 | 측정당 하나 | 없음 |
| 다국어 | 완전한 유니코드 지원 | 완전함 (브라우저 네이티브) | 부족함 (하드코딩된 비율) |
| CSS 속성 지원 | 제한적 (폰트만) | 완전함 | 없음 |
| 메모리 오버헤드 | 캐시된 세그먼트 | DOM 노드 | 최소 |
| 반응형 레이아웃 | 한 번의 prepare(), 여러 번의 layout() |
너비당 재측정 | 너비당 재추정 |
올바른 선택은 여러분의 제약 조건에 따라 달라집니다. 픽셀 완벽한 정확도와 CSS 속성 지원이 필요하다면 DOM 측정이 여전히 실측치입니다. 수천 개의 항목에서 속도가 필요하고 사소한 서브픽셀 차이를 허용할 수 있다면 Pretext.js가 훨씬 더 우월합니다.
시작하기
설치
npm install @chenglou/pretext
# or
pnpm add @chenglou/pretext
# or
bun add @chenglou/pretext
기본 사용법
import { prepare, layout } from '@chenglou/pretext';
// Measure a paragraph
const handle = prepare(
'Pretext.js computes text layout without touching the DOM.',
'16px "Inter"'
);
// Get height at a specific container width
const result = layout(handle, 600, 24);
console.log(result.height); // e.g., 48 (2 lines x 24px)
console.log(result.lineCount); // e.g., 2
React 통합
import { prepare, layout } from '@chenglou/pretext';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useMemo, useRef } from 'react';
function VirtualChat({ messages }: { messages: string[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const containerWidth = 600;
const font = '14px "Inter"';
const lineHeight = 20;
const heights = useMemo(() => {
return messages.map(msg => {
const handle = prepare(msg, font);
const { height } = layout(handle, containerWidth, lineHeight);
return height + 24; // padding
});
}, [messages]);
const virtualizer = useVirtualizer({
count: messages.length,
getScrollElement: () => parentRef.current,
estimateSize: (index) => heights[index],
});
return (
<div ref={parentRef} style={{ height: '100vh', overflow: 'auto' }}>
<div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>
{virtualizer.getVirtualItems().map(virtualRow => (
<div
key={virtualRow.key}
style={{
position: 'absolute',
top: virtualRow.start,
width: containerWidth,
}}
>
{messages[virtualRow.index]}
</div>
))}
</div>
</div>
);
}
이렇게 하면 메시지가 DOM에 렌더링되기 전에 정확한 항목 높이가 계산되는 가상 채팅을 얻을 수 있습니다. 추정 없음, 보정 점프 없음, 리플로우 없음.
인터랙티브 플레이그라운드
Pretext.js 웹사이트에는 pretextjs.dev/playground에서 인터랙티브 플레이그라운드가 포함되어 있어 텍스트를 붙여넣고, 폰트를 선택하고, 컨테이너 너비를 조정하고, 실시간으로 레이아웃 계산을 확인할 수 있습니다. 이는 통합 전에 동작을 확인하는 가장 빠른 방법입니다.
Pretext.js를 사용하지 말아야 할 때
Pretext.js는 모든 텍스트 측정 문제에 대한 올바른 도구가 아닙니다:
- 콘텐츠를 알고 있는 정적 페이지: 텍스트가 변경되지 않고 가상화를 하지 않는다면 CSS가 레이아웃을 잘 처리합니다. 라이브러리가 필요 없습니다.
- 픽셀 완벽한 인쇄 레이아웃: Canvas 측정과 DOM 렌더링 간의 서브픽셀 차이는 인쇄 해상도에서 중요합니다. DOM을 실측치로 사용하십시오.
- 과도한 CSS 텍스트 스타일링:
letter-spacing,text-indent또는font-feature-settings에 의존하는 경우, 높이 계산이 렌더링된 출력과 달라질 것입니다. - 서버 측 렌더링: Pretext.js는 Canvas API에 의존하는데,
node-canvas와 같은 폴리필 없이는 Node.js에서 사용할 수 없습니다. 서버 측 지원은 로드맵에 있지만 아직 출시되지 않았습니다. - 작고 정적인 목록: 목록에 50개 항목이 있다면 DOM 측정은 5ms 미만이 걸립니다. 이 정도 최적화는 의존성을 추가할 가치가 없습니다.
자주 묻는 질문
Pretext.js는 프로덕션에 사용할 준비가 되었나요?
이 라이브러리는 2026년 3월에 출시되어 며칠 만에 14,000개 이상의 GitHub 별을 얻었습니다. 개발자인 Cheng Lou는 수백만 명의 사용자에게 서비스를 제공하는 프로덕션 시스템인 Midjourney의 프런트엔드를 운영하고 있습니다. 이 라이브러리의 테스트 스위트는 수십 가지 언어와 엣지 케이스를 다룹니다. 그렇긴 하지만, 새로운 릴리스입니다. 사용 버전을 고정하고 특정 폰트 및 콘텐츠에 대해 테스트하십시오.
Pretext.js는 React, Vue, Svelte와 함께 작동하나요?
네. Pretext.js는 프레임워크에 구애받지 않습니다. 두 개의 함수로 구성된 순수 TypeScript 라이브러리입니다. React 훅, Vue 컴포저블, Svelte 스토어 또는 일반 JavaScript 내부 등 텍스트 측정이 필요한 곳이면 어디든 prepare() 및 layout()을 호출할 수 있습니다.
Pretext.js는 웹 폰트를 어떻게 처리하나요?
prepare() 함수는 호출 시점에 브라우저가 로드한 폰트를 사용하여 텍스트를 측정합니다. 웹 폰트가 아직 로드되지 않았다면, 측정은 대체 폰트를 사용하고 잘못된 결과를 생성할 것입니다. prepare()를 호출하기 전에 폰트가 로드되었는지 확인하십시오. Font Loading API (document.fonts.ready)를 사용하여 이를 확인할 수 있습니다.
Canvas 또는 SVG 렌더링에 Pretext.js를 사용할 수 있나요?
네. 이 라이브러리는 렌더링 대상에 구애받지 않는 텍스트 레이아웃을 계산합니다. 계산된 높이와 줄 바꿈을 사용하여 Canvas 2D, WebGL, SVG 또는 DOM에서 텍스트를 배치할 수 있습니다. Pretext.js 웹사이트에는 이러한 모든 렌더링 대상의 예시가 나와 있습니다.
RTL (오른쪽에서 왼쪽으로 쓰는) 언어를 지원하나요?
네. Pretext.js는 아랍어, 히브리어 및 기타 RTL 스크립트를 올바른 양방향 텍스트 지원과 함께 처리합니다. 또한 혼합 방향 텍스트(예: 아랍어 텍스트에 포함된 영어 단어)도 올바르게 처리합니다.
번들 크기는 얼마인가요?
의존성 없이 15KB로 축소(minified)됩니다. 폴리필은 필요 없습니다. 이 라이브러리는 표준 브라우저 API(Canvas measureText() 및 가능한 경우 Intl.Segmenter)만 사용합니다.
DOM 측정과 비교하여 얼마나 정확한가요?
대부분의 텍스트 콘텐츠에 대해 Pretext.js는 1-2픽셀 내에서 DOM 레이아웃과 일치합니다. 정확도는 사용하는 폰트 및 CSS 속성에 따라 달라집니다. letter-spacing 및 word-spacing과 같은 속성은 고려되지 않으므로, 이러한 속성을 사용하는 경우 더 큰 차이를 예상해야 합니다. 몇 픽셀의 오류가 눈에 띄지 않는 가상 스크롤링의 경우, 정확도는 충분히 높습니다.
Pretext.js는 스타일이 적용된 텍스트(굵게, 기울임꼴, 혼합된 크기)를 측정할 수 있나요?
각 prepare() 호출은 단일 폰트 사양을 받습니다. 스타일이 혼합된 텍스트(일반 텍스트 내의 굵은 단어)의 경우, 텍스트를 직접 분할하고 각 스타일 실행에 대해 별도의 핸들을 생성해야 합니다. 이는 향후 버전에서 해결될 수 있는 알려진 한계입니다.
결론
Pretext.js는 브라우저가 30년 동안 무시해왔던 문제, 즉 DOM 리플로우 없이 빠르고 정확한 텍스트 측정을 해결합니다. 가상 스크롤러, 채팅 UI, 데이터 그리드 또는 수천 개의 텍스트 블록을 측정해야 하는 모든 인터페이스를 구축하는 개발자에게 이 라이브러리는 두 개의 함수 호출로 전체 종류의 해결책을 대체합니다.
이 라이브러리가 만능 해결책은 아닙니다. 폰트 사양 외의 CSS 텍스트 속성을 지원하지 않고, 미세한 서브픽셀 정확도 차이가 있으며, 아직 서버 측에서는 작동하지 않습니다. 그러나 가상화된 목록을 위한 텍스트 높이 사전 계산이라는 주요 사용 사례에 있어서는 다른 어떤 것도 이에 필적할 수 없습니다.
더 빠른 텍스트 중심 UI를 구축할 준비가 되셨나요? 먼저 Apidog로 API 엔드포인트를 테스트하여 데이터 계층이 견고한지 확인한 다음, Pretext.js를 렌더링 파이프라인에 추가하십시오.
