2026년 3월 28일, 프론트엔드 개발 커뮤니티에 하나의 라이브러리가 공개되며 거대한 반향을 일으켰다. 이름은 Pretext. React의 핵심 애니메이션 라이브러리 react-motion을 만들었고, Facebook Messenger와 ReasonML 개발을 이끌었으며, 현재 Midjourney 프론트엔드 엔지니어로 일하는 Cheng Lou가 내놓은 오픈소스 프로젝트다. 공개 이틀 만에 GitHub 스타가 1만 개를 넘었고, Hacker News 최상위에 오르며 전 세계 개발자의 관심을 끌었다.
Pretext가 해결하는 문제는 단순하면서도 근본적이다. 웹에서 텍스트의 높이가 얼마인지 알려면, 브라우저는 화면 전체를 다시 계산해야 한다. 이 과정을 레이아웃 리플로우(layout reflow)라고 부르는데, 웹이 태어난 이후 30년간 이 구조는 바뀌지 않았다. Pretext는 브라우저에 묻지 않고 텍스트 높이를 직접 계산한다. DOM을 건드리지 않으며, CSS도 사용하지 않는다. 순수한 JavaScript 산술 연산만으로 작동하고, 그 결과 기존 방식 대비 300~600배 빠른 성능을 보여준다.
이 문서에서는 Pretext가 등장한 기술적 맥락, 내부 작동 원리, API 구조, 실제 활용 사례, 그리고 웹 프론트엔드의 방향성에 미치는 영향까지 상세하게 다룬다.
1. 브라우저 텍스트 측정의 구조적 한계
웹 브라우저에서 텍스트의 렌더링 크기를 알아내는 전통적인 방법은 getBoundingClientRect()나 offsetHeight 같은 DOM API를 호출하는 것이다. 문제는 이 API들이 호출되는 순간, 브라우저가 동기적으로 전체 레이아웃을 재계산한다는 점이다. Paul Irish(Chrome 팀)가 정리한 유명한 문서에 따르면, offsetHeight, scrollTop, getComputedStyle() 등 수십 개의 속성과 메서드가 레이아웃 리플로우를 강제한다.
1.1 리플로우가 비싼 이유
- 브라우저는 단일 요소만 다시 계산하는 것이 아니라, 해당 요소가 속한 문서 전체의 기하 정보를 재계산한다. 평균 웹 페이지의 DOM 요소는 약 800개 수준이며, 최악의 경우 모든 요소의 위치와 크기를 다시 잡는다.
- 이 작업은 메인 스레드를 차단한다. 자바스크립트 실행이 멈추고, 사용자 인터랙션에 응답할 수 없게 된다. 모바일 기기에서 단일 강제 리플로우는 10~100ms를 소비한다고 Google의 web.dev 문서가 명시한다.
- 읽기-쓰기 인터리빙(read-write interleaving)이 발생하면 문제가 증폭된다. DOM에 쓰고(write), 바로 읽으면(read), 브라우저는 쓰기를 반영하기 위해 리플로우를 즉시 실행한다. 500개 텍스트 블록의 높이를 개별적으로 측정하면 500번의 리플로우가 연쇄적으로 발생하며, 이를 레이아웃 스래싱(layout thrashing)이라 부른다.
1.2 실제 영향
채팅 앱에서 수백 개의 메시지를 동시에 렌더링하는 상황을 떠올려 보자. 각 메시지 버블의 높이를 알아야 스크롤 위치를 잡을 수 있는데, 메시지마다 DOM을 측정하면 프레임 드랍이 발생하고 화면이 버벅인다. 가상 스크롤(virtualization)을 구현하려 해도 텍스트 높이를 미리 알 수 없으니 추정치에 의존해야 하고, 추정이 틀리면 스크롤바가 점프하거나 레이아웃이 불안정해진다.
Google Core Web Vitals 중 하나인 CLS(Cumulative Layout Shift) 역시 이 문제와 직결된다. 2024년 기준 전체 웹사이트의 약 51%만이 CLS 기준(0.1 이하)을 통과했다. 텍스트 높이를 정확히 예측할 수 없는 구조가 레이아웃 시프트의 주요 원인 중 하나다.
핵심 포인트: 기존 웹에서 텍스트 높이 측정은 DOM 리플로우를 강제하며, 이는 단일 호출에 10~100ms, 대량 호출 시 프레임당 30ms 이상을 소비한다. Pretext는 이 병목을 근본적으로 우회한다.
2. Cheng Lou — Pretext를 만든 개발자의 이력
Pretext의 의미를 이해하려면 만든 사람의 맥락을 알아야 한다. Cheng Lou는 프론트엔드 생태계에서 여러 핵심 도구를 만들어 온 엔지니어다.
2.1 주요 경력과 프로젝트
- react-motion — React 생태계에서 물리 기반 애니메이션의 사실상 표준이었던 라이브러리다. GitHub 스타 21,700개 이상, npm 주간 다운로드 최대 90만 건을 기록했다. CSS 트랜지션의 한계를 스프링 물리학으로 넘어선 접근이었다.
- ReasonML / ReScript — Facebook에서 OCaml 기반 언어 ReasonML을 주도했다. Messenger.com 코드베이스의 50% 이상이 Reason으로 전환되었고, 전환 후 연간 버그 리포트가 수백 건에서 10건 미만으로 급감했다.
- Facebook Messenger — 웹 버전 Messenger의 핵심 엔지니어로 참여했다.
- Midjourney — 현재 AI 이미지 생성 플랫폼 Midjourney에서 프론트엔드를 이끌고 있다.
Cheng Lou의 X(구 Twitter) 프로필에는 "Worked on: ReactJS, Messenger, ReasonML, ReScript & Midjourney"라고 적혀 있다. 그가 Pretext 공개 트윗에서 "지옥에서 기어 올라왔다(crawled through depths of hell)"고 표현한 것은, 이 라이브러리가 단순한 실험이 아니라 오랜 기간 축적된 문제의식에서 나온 결과물임을 시사한다.
2.2 AI를 활용한 개발 방식
Pretext 개발에서 주목할 점은 Claude Opus를 활용한 반복 테스트 방식이다. Cheng Lou는 실제 브라우저 렌더링 결과를 ground truth로 삼고, AI에게 측정 코드를 반복적으로 생성하고 검증하게 했다. GitHub README에도 "very AI-friendly iteration method"라는 표현이 명시되어 있다. 수십 개 언어, 이모지, RTL(오른쪽에서 왼쪽) 텍스트까지 커버하는 방대한 엣지 케이스를 사람이 일일이 테스트하기는 현실적으로 어렵다. AI가 이런 반복적이고 지루한 정확도 검증 작업에 투입된 사례로, Pretext는 AI 활용 개발의 구체적 레퍼런스가 되고 있다.
3. Pretext의 작동 원리
Pretext의 핵심 아이디어는 Canvas API의 measureText()를 활용해 글자 폭을 측정하되, 줄바꿈과 높이 계산은 자체 알고리즘으로 처리하는 것이다.
3.1 2단계 처리 구조
prepare()단계 — 텍스트를 받아Intl.Segmenter로 세그먼트(단어, 이모지, CJK 문자 등)를 분리한다. 각 세그먼트의 폭을canvas.measureText()로 측정하고 캐시한다. 이모지 보정이 필요한 경우 폰트당 한 번만 DOM 캘리브레이션을 수행한다. 이 단계는 텍스트당 한 번만 실행한다.layout()단계 — 캐시된 폭 데이터를 기반으로 주어진 최대 너비(maxWidth)와 줄 높이(lineHeight)에 맞춰 줄바꿈 위치를 결정하고 전체 높이를 계산한다. 순수 산술 연산만 수행하며 DOM을 전혀 건드리지 않는다. 창 크기가 변할 때마다 이 단계만 다시 실행하면 된다.
3.2 왜 빠른가
canvas.measureText()는 브라우저의 폰트 셰이핑 엔진에 직접 접근하되, 레이아웃 리플로우를 발생시키지 않는 읽기 전용 API다. Pretext는 이 API로 한 번 측정한 뒤, 이후 모든 레이아웃 연산을 캐시된 숫자의 덧셈·비교만으로 처리한다. 500개 텍스트 블록 기준, prepare()는 약 19ms(전체 배치), layout()은 약 0.09ms(전체 배치)로 동작한다. 텍스트 하나당 layout() 호출 비용은 약 0.0002ms다.
| 항목 | 기존 DOM 측정 | Pretext |
|---|---|---|
| 500개 텍스트 블록 처리 | 15~30ms + 500회 리플로우 | 0.09ms + 0회 리플로우 |
| 속도 비교 | 1x | 300~600x |
| 메인 스레드 차단 | 예 (동기적) | 아니오 |
| DOM 의존성 | 전체 DOM 트리 | Canvas 컨텍스트만 |
| 번들 크기 | 해당 없음 | 15KB (gzip 시 약 5KB) |
| Web Worker 호환 | 불가 | 가능 |
| 서버사이드 실행 | 불가 (DOM 필요) | 가능 |
3.3 다국어·이모지·양방향 텍스트 지원
Pretext는 한국어, 아랍어, 중국어, 일본어는 물론 이모지와 혼합 양방향(bidi) 텍스트까지 처리한다. Intl.Segmenter를 사용해 유니코드 세그멘테이션 규칙을 따르며, 브라우저별 이모지 렌더링 차이도 캘리브레이션으로 보정한다. GitHub 저장소의 corpora 디렉터리에는 다양한 언어의 장문 테스트 문서가 포함되어 있고, 이를 바탕으로 99.4% 정확도를 달성했다고 발표되었다.
핵심 포인트: Pretext는
canvas.measureText()로 폰트 셰이핑 엔진에 직접 접근하되 리플로우를 우회하고, 이후 모든 줄바꿈·높이 계산을 순수 산술로 처리한다. prepare()는 한 번, layout()은 리사이즈마다 실행하는 2단계 구조가 성능의 핵심이다.
4. API 구조와 사용 패턴
Pretext는 크게 두 가지 사용 패턴을 제공한다.
4.1 높이만 빠르게 알고 싶을 때
prepare()와 layout() 조합을 사용한다. prepare('텍스트 내용', '16px Inter')로 텍스트를 사전 처리하고, layout(prepared, maxWidth, lineHeight)를 호출하면 height와 lineCount가 반환된다. 창 크기가 변할 때는 layout()만 다시 호출하면 되므로 리사이즈 반응이 사실상 즉시 이루어진다. prepare()에 whiteSpace: 'pre-wrap' 옵션을 넘기면 textarea처럼 공백과 탭, 줄바꿈을 그대로 유지하는 모드로 전환된다.
4.2 줄 단위로 직접 렌더링하고 싶을 때
prepareWithSegments()를 사용하면 더 풍부한 줄 정보를 얻을 수 있다.
| API | 용도 | 반환값 |
|---|---|---|
layoutWithLines() |
고정 너비에서 모든 줄 정보 반환 | lines 배열 (각 줄의 text, width, start/end 커서) |
walkLineRanges() |
줄 폭과 커서만 빠르게 순회 | 줄 텍스트 문자열을 생성하지 않아 탐색적 레이아웃에 적합 |
layoutNextLine() |
줄마다 다른 너비 적용 가능 | 이미지 주변으로 텍스트가 흐르는 에디토리얼 레이아웃 구현에 핵심 |
layoutNextLine()은 특히 주목할 API다. 줄을 하나씩 소비하면서 각 줄의 최대 너비를 다르게 지정할 수 있다. 이 구조 덕분에 이미지나 도형 주변으로 텍스트가 자연스럽게 흐르는 레이아웃을 순수 자바스크립트로 구현할 수 있다. 기존 CSS의 shape-outside 속성보다 훨씬 유연하고 프로그래밍적 제어가 가능하다.
walkLineRanges()는 줄 텍스트를 실제로 생성하지 않고 폭과 커서 정보만 순회하므로, 이진 탐색으로 최적 너비를 찾는 데 유용하다. 채팅 버블에서 텍스트가 차지하는 실제 최대 줄 폭을 구한 뒤 컨테이너를 딱 맞게 줄이는 shrink-wrap 레이아웃이 대표적인 활용례다. 이 shrink-wrap은 웹에서 오랫동안 불가능했던 기능으로, W3C CSS Working Group 이슈(csswg-drafts#191)에서도 수년간 논의되어 온 문제다.
5. 실제 활용 사례와 데모
Pretext 공개와 함께 커뮤니티에서 다양한 데모가 쏟아졌다. 공식 데모 페이지(chenglou.me/pretext)와 커뮤니티 데모(somnai-dreams.github.io/pretext-demos)에서 확인할 수 있다.
5.1 대표 데모와 기술적 의미
- 120fps 가상화(Virtualization) — 10만 개 이상의 텍스트 박스를 가상 스크롤로 처리한다. 각 항목의 높이를 추정치가 아닌 정확한 계산값으로 미리 알 수 있기 때문에 스크롤바 점프나 레이아웃 시프트가 발생하지 않는다.
- 드래곤 데모 — 80개 세그먼트로 구성된 용 모양 오브젝트 주변으로 텍스트가 실시간으로 리플로우된다.
layoutNextLine()API를 활용해 각 줄의 가용 너비를 동적으로 변경하며, 60fps로 동작한다. 전통적인 DOM 측정으로는 한 프레임(16.67ms)에 텍스트 측정만으로도 15~30ms를 소비하므로 불가능한 연출이다. - 채팅 버블(Bubbles) — 텍스트 버블이 내용물의 실제 최대 줄 폭에 딱 맞게 줄어드는 shrink-wrap 레이아웃을 구현한다.
text-wrap: balance나pretty로도 해결되지 않는 문제를 Pretext로 정확히 처리한다. - 에디토리얼 엔진 — 잡지 스타일의 다단 텍스트 레이아웃을 자바스크립트로 구현한다. "The Future of Text Layout Is Not CSS"라는 제목으로 공개된 이 데모는 Pretext의 비전을 가장 직접적으로 보여주는 예시다.
- 아코디언 애니메이션 — 텍스트 높이를 미리 정확히 알 수 있으므로,
height: auto에서 특정 높이로의 부드러운 전환이 자바스크립트 단에서 가능해진다.
5.2 프론트엔드 개발 실무에서의 적용 가능성
| 적용 영역 | 기존 방식의 문제 | Pretext로 개선되는 점 |
|---|---|---|
| 채팅 앱 메시지 목록 | 높이 추정에 의존, 스크롤 점프 발생 | 정확한 높이 사전 계산, 안정적 가상 스크롤 |
| 데이터그리드 동적 셀 | 텍스트 높이 측정마다 리플로우 | 10만 행 이상의 가변 높이 셀을 즉시 계산 |
| 리치 텍스트 에디터 | 입력마다 레이아웃 재계산 비용 | prepare() 캐시로 입력 반응 속도 향상 |
| SSR/PDF 생성 | DOM 없는 환경에서 텍스트 레이아웃 불가 | Node.js, Deno, Edge Function에서 동작 |
| AI 기반 UI 생성 | 실시간 레이아웃 피드백 불가 | 텍스트 크기를 즉시 계산해 AI 반복 속도 향상 |
6. 기술적 제약과 주의점
Pretext가 만능은 아니다. 현재 버전의 제약 사항을 정확히 이해하고 사용해야 한다.
6.1 지원하는 CSS 모드
현재 Pretext가 타겟으로 삼는 텍스트 설정은 white-space: normal과 white-space: pre-wrap 두 가지뿐이다. word-break: normal, overflow-wrap: break-word, line-break: auto가 기본 설정이며, pre-line 등 다른 white-space 모드는 아직 지원하지 않는다.
6.2 폰트 관련 주의
macOS에서 system-ui 폰트를 사용하면 layout() 정확도가 떨어질 수 있다. 명시적으로 이름이 지정된 폰트(예: 16px Inter)를 사용해야 정확한 결과를 얻는다. prepare()에 전달하는 폰트 문자열은 CSS의 font 축약 선언과 동일한 형식이어야 한다.
6.3 성능 특성
prepare()는 canvas.measureText()를 내부적으로 호출하므로 대량 텍스트에서는 수십 ms가 소요될 수 있다. Hacker News에서 한 개발자(leeoniya, uWrap.js 제작자)가 ASCII 텍스트 기준으로 prepare()가 2,200ms 걸리는 반면 자신의 경량 라이브러리는 80ms라고 지적했다. 다만 Pretext의 설계 의도는 prepare()를 한 번만 실행하고 layout()을 반복 호출하는 것이므로, 정적 텍스트의 반복 레이아웃 시나리오에서 진가를 발휘한다. 10만 행의 서로 다른 텍스트를 한꺼번에 최초 처리하는 경우에는 초기 prepare() 비용이 클 수 있다. 현재 여러 성능 개선 PR이 열려 있어 이 부분은 빠르게 개선될 것으로 보인다.
6.4 최종 렌더링은 여전히 브라우저 담당
Pretext는 텍스트를 실제로 화면에 그리지 않는다. 어디에 어떤 크기로 텍스트를 배치할지를 계산하는 라이브러리이며, 최종 렌더링은 브라우저의 DOM, Canvas, SVG, 또는 WebGL이 담당한다. Pretext가 제공하는 것은 그 계산의 속도와 정확성이다.
7. 웹 프론트엔드의 방향성에 미치는 영향
Pretext는 단순한 유틸리티 라이브러리 이상의 의미를 가진다. Cheng Lou가 GitHub에 올린 thoughts.md에는 이런 문장이 있다: "80% of CSS spec could be avoided if userland had better control over text." 텍스트에 대한 제어권을 사용자 영역(userland)으로 가져오면 CSS 스펙의 대부분이 불필요해진다는 주장이다.
7.1 CSS의 편의성이 약해지는 이유
Cheng Lou는 CSS의 편의성이 다음 두 가지 이유로 점차 약해지고 있다고 분석한다. 첫째, CSS에 표현력을 더할수록 CSS 자체의 성능이 나빠진다. 위원회와 사용자 모두 원치 않는 결과지만, 스펙의 복잡도가 성능을 잡아먹고 있다. 둘째, AI가 하드코딩된 CSS 설정의 필요성을 줄이고 있다. CSS가 사전(dictionary)처럼 변해가는 반면, 조합(compositional)하기는 어려워지고 있다.
7.2 Pretext가 열어주는 가능성
웹 UI의 패턴은 오랫동안 몇 가지 틀 안에 갇혀 있었다. 랜딩 페이지, 블로그 글, SaaS 대시보드, 모바일 앱의 몇 개 직사각형. Pretext 같은 도구가 텍스트 제어를 프로그래밍 영역으로 가져오면, 잡지처럼 텍스트가 이미지를 감싸며 흐르는 레이아웃, 줄마다 폭이 달라지는 자유로운 타이포그래피, 글자 하나하나가 물리적으로 반응하는 인터랙션 같은 표현이 CSS 해킹 없이 가능해진다.
Hacker News의 한 댓글은 이 흐름을 웹 발전의 반복 패턴으로 요약했다: "(1) 웹의 요구가 복잡해진다 → (2) 해키한 JS/CSS 우회책이 등장한다 → (3) 결국 CSS 표준으로 편입된다." Pretext는 이 패턴에서 "해키하지 않은 2단계"에 해당한다. 브라우저 벤더와 W3C가 텍스트 레이아웃 측정을 네이티브 Web API로 제공할 가능성도 제기되고 있다.
핵심 포인트: Pretext는 텍스트 측정이라는 기본 기능을 유저랜드로 가져옴으로써, CSS 스펙 의존도를 낮추고 프로그래밍적 UI 표현의 자유도를 높이는 방향을 제시한다. 이 접근은 장기적으로 웹 표준에도 영향을 줄 수 있다.
8. 마무리
위에서 살펴본 Pretext의 핵심 내용을 정리하면 다음과 같습니다.
핵심 요약:
- Pretext는 DOM 리플로우 없이 텍스트 높이와 줄바꿈을 순수 산술 연산으로 계산하는 15KB TypeScript 라이브러리다
prepare()로 한 번 측정,layout()으로 반복 계산하는 2단계 구조가 300~600배 성능 향상의 핵심이다- React 핵심 개발자이자 Midjourney 엔지니어인 Cheng Lou가 Claude Opus를 활용한 AI 반복 테스트로 99.4% 정확도를 달성했다
- 채팅 앱 가상화, 에디토리얼 레이아웃, 데이터그리드, SSR 등 DOM 없는 텍스트 측정이 필요한 모든 영역에서 활용 가능하다
- 한국어, 아랍어, 이모지, 양방향 텍스트를 포함한 다국어를 지원한다
- CSS 스펙에 의존하지 않는 프로그래밍적 텍스트 레이아웃이라는 새로운 방향을 제시한다
도입을 검토한다면, 먼저 자신의 프로젝트에서 텍스트 높이 측정이 성능 병목인지를 확인하는 것이 출발점이다. 채팅, 가상 리스트, 동적 텍스트 레이아웃처럼 대량의 텍스트 크기를 반복적으로 알아야 하는 상황에서 가장 큰 효과를 발휘한다. npm install @chenglou/pretext로 설치한 뒤, 공식 데모 페이지의 예제를 기반으로 자신의 사용 사례에 맞는 프로토타입을 만들어 보는 것을 권한다.