Pretext의 진짜 혁신은 화려한 canvas 데모가 아니라 DOM을 읽지 않고도 텍스트 높이를 예측해 접근성과 성능을 함께 지키는 측정 엔진에 있다는 글입니다.
신간 도서: Performance Engineering in Practice (Manning)를 지금 얼리 액세스로 이용할 수 있습니다. 지금 읽기 →


Den Odell 저자. Canva의 Staff Engineer
2026년 3월 30일·6분
Cheng Lou의 새로운 JavaScript 라이브러리 Pretext는 처음 3일 만에 GitHub 스타 7,000개를 넘겼습니다. 그동안 프론트엔드 엔지니어링 커뮤니티 근처에 있었다면 데모를 봤을 겁니다. 물을 가르듯 텍스트를 가르는 드래곤, 타이포그래피 ASCII로 렌더링된 유동적인 연기, 문자 그리드를 통해 그려진 와이어프레임 토러스, 60fps로 애니메이션 오브가 텍스트를 밀어내는 다단 편집 레이아웃까지. 이것들은 시각적으로 놀랍고, 라이브러리가 입소문을 탄 이유이기도 합니다.
하지만 이 라이브러리가 중요한 이유는 그것이 아닙니다.
Pretext가 중요하게 해내는 일은 DOM에서 읽지 않고도 텍스트 블록의 높이를 예측하는 것입니다. 이는 단 한 번의 레이아웃 재계산도 유발하지 않고 텍스트 노드를 배치할 수 있다는 뜻입니다. 텍스트는 DOM 안에 그대로 남아 있으므로 스크린 리더가 읽을 수 있고, 사용자는 선택하고, 복사하고, 번역할 수 있습니다. 접근성 트리는 그대로 유지되고, 성능 향상은 실질적이며, 사용자 경험도 모두를 위해 보존됩니다. 이것이 프로덕션 웹 애플리케이션이 텍스트를 다루는 방식을 바꾸게 될 기능이고, 거의 아무도 시연하지 않는 기능입니다.
커뮤니티는 3일 동안 드래곤을 만들었습니다. 이제는 채팅 인터페이스를 만들어야 합니다. 그리고 드래곤은 입소문을 탔는데 측정 엔진은 주목받지 못했다는 사실은 프론트엔드 커뮤니티가 도구를 평가하는 방식에 대해 중요한 점을 말해줍니다. 우리는 우리가 볼 수 있는 것을 최적화하지, 우리가 만드는 것을 사용하는 사람들에게 가장 중요한 것을 최적화하지 않습니다.
문제는 브라우저가 계속 진행하기 전에 멈춰서 페이지 레이아웃을 다시 측정해야 하는 강제 레이아웃 재계산입니다. UI 컴포넌트가 텍스트 블록의 높이를 알아야 할 때, 일반적인 접근은 DOM에서 그것을 측정하는 것입니다. getBoundingClientRect()를 호출하거나 offsetHeight를 읽으면, 브라우저는 답을 주기 위해 동기적으로 레이아웃을 계산합니다. 가상 리스트에서 텍스트 블록 500개에 대해 이렇게 하면 이런 멈춤을 500번 강제한 셈입니다. _layout thrashing_이라고 불리는 이 패턴은 여전히 복잡한 웹 애플리케이션에서 시각적 끊김의 주요 원인입니다.
Pretext의 통찰은 canvas.measureText()가 DOM 렌더링과 동일한 폰트 엔진을 사용하지만, 브라우저의 레이아웃 과정과는 완전히 분리되어 동작한다는 점입니다. canvas를 통해 단어를 측정하고 너비를 캐시하면, 그 시점부터 레이아웃은 순수한 산술이 됩니다. 캐시된 너비를 순회하고, 누적 줄 너비를 추적하며, 컨테이너의 최대치를 넘을 때 줄바꿈을 넣는 것입니다. 느린 측정 읽기도 없고, 동기적인 멈춤도 없습니다.
아키텍처는 이를 두 단계로 분리합니다. prepare()는 비용이 큰 작업을 한 번 수행합니다. 공백을 정규화하고, 로캘을 인식하는 단어 경계를 위해 Intl.Segmenter로 텍스트를 분할하고, 양방향 텍스트(예: 영어와 아랍어의 혼합)를 처리하고, canvas로 세그먼트를 측정한 뒤, 재사용 가능한 참조를 반환합니다. 그다음 layout()은 캐시된 너비 위에서 순수 계산만 수행하며, 텍스트 500개 배치 기준으로 약 0.09ms가 걸리고 prepare()는 대략 19ms 정도가 걸립니다. Cheng Lou 본인도 500배 비교는 일회성 prepare() 비용을 제외하므로 “불공정하다”고 말하지만, 그 비용은 한 번만 지불되고 이후 모든 호출에 분산됩니다. 텍스트가 나타날 때 한 번 실행되고, 이후의 모든 리사이즈는 빠른 경로를 타며, 여기서 성능 향상은 실제적이고 상당합니다.
핵심 아이디어는 Meta에서의 Sebastian Markbage의 연구로 거슬러 올라가며, 그곳에서 Cheng Lou는 이전 text-layout 프로토타입을 구현해 canvas 폰트 메트릭이 DOM 측정을 대체할 수 있음을 입증했습니다. Pretext는 그 기반 위에 프로덕션 수준의 국제화, 양방향 텍스트 지원, 그리고 빠른 경로를 매우 빠르게 만드는 2단계 아키텍처를 더합니다. Lou는 이 분야에서 실적이 있습니다. react-motion과 ReasonML 모두, 모두가 당연하게 받아들이던 제약을 찾아내 더 나은 추상화로 제거하는 같은 패턴을 따랐습니다.
Pretext가 제공하는 첫 번째 사용 사례이자, 제가 특히 강조하고 싶은 것은 텍스트 높이를 측정해 브라우저에 실제 높이를 묻지 않고도 DOM 텍스트 노드를 정확한 위치에 렌더링할 수 있게 하는 것입니다. 이것은 타협안이 아니라, 이 라이브러리가 할 수 있는 가장 강력한 일입니다.
채팅 메시지 500개로 이루어진 가상 스크롤 리스트를 생각해봅시다. 보이는 항목만 렌더링하려면, 각 메시지가 뷰포트에 들어오기 전에 높이를 알아야 합니다. 전통적인 접근은 텍스트를 DOM에 넣고, 측정하고, 그다음 배치하는 것이며, 메시지마다 레이아웃 비용을 지불합니다. Pretext를 쓰면 높이를 수학적으로 예측한 뒤, 텍스트 노드를 올바른 위치에 렌더링할 수 있습니다. 텍스트 자체는 여전히 DOM 안에 있으므로, 접근성 모델, 선택 동작, 페이지 내 찾기가 모두 다른 일반 텍스트 노드와 완전히 똑같이 작동합니다.
실제로는 이렇게 보입니다:
const prepared = prepare(message.text, '16px Inter');
const { height } = layout(prepared, containerWidth, 24);
함수 호출 두 번입니다. 첫 번째는 측정하고 캐시하며, 두 번째는 계산을 통해 높이를 예측합니다. 레이아웃 비용은 없지만, 그 뒤에 렌더링하는 텍스트는 완전한 접근성을 갖춘 표준 DOM 노드입니다.
shrinkwrap 데모는 이 경로가 왜 중요한지를 보여주는 가장 분명한 예입니다. CSS width: fit-content는 가장 넓은 줄바꿈 라인에 맞춰 컨테이너 크기를 정하므로 마지막 줄이 짧을 때 공간을 낭비합니다. “정확히 N줄로 줄바꿈되면서도 가장 좁은 너비를 찾아라”라고 말하는 CSS 속성은 없습니다. Pretext의 walkLineRanges()는 수학적으로 최적 너비를 계산하고, 그 결과는 표준 DOM 텍스트 노드로 렌더링되는 더 촘촘한 채팅 버블입니다. 성능 향상은 DOM을 버려서가 아니라 더 똑똑한 측정에서 옵니다. 최종 사용자에게 텍스트 자체는 아무것도 달라지지 않습니다.
Pretext로 높이를 계산하는 아코디언 섹션과 DOM 읽기 대신 높이 예측을 사용하는 메이슨리 레이아웃 역시, 빠른 측정이 표준 DOM 렌더링으로 이어지는 같은 모델을 따릅니다.
알아둘 만한 엣지 케이스도 있습니다. 우선 예측의 정확도는 측정 시점에 사용할 수 있는 폰트 메트릭의 정확도에 달려 있으므로, prepare()가 실행되기 전에 폰트가 로드되어 있어야 결과가 어긋나지 않습니다. 리가처(예: “fi”처럼 두 문자가 하나의 글리프로 합쳐지는 경우), 고급 폰트 기능, 그리고 일부 CJK 조합 규칙은 canvas 측정과 DOM 렌더링 사이에 아주 작은 차이를 만들 수 있습니다. 이것들은 해결 가능한 문제이고 라이브러리도 이미 많은 부분을 다루고 있지만, 이를 인정하는 것은 이 접근을 마법처럼 취급하지 않고 진지하게 받아들인다는 뜻입니다.
Pretext는 Canvas, SVG, WebGL에 렌더링하기 위한 수동 줄 레이아웃도 지원합니다. 이 API들은 정확한 줄 좌표를 제공하므로, DOM이 처리하게 두는 대신 직접 텍스트를 그릴 수 있습니다. 바로 이 경로가 입소문을 탔고, 거의 모든 커뮤니티 쇼케이스를 지배하고 있습니다.
canvas 데모는 인상적이며 DOM으로는 실제로 60fps에서 할 수 없는 일을 해냅니다. 하지만 그것들은 결국 픽셀을 그리는 것이고, 텍스트를 canvas 픽셀로 그리면 브라우저는 그 픽셀이 언어를 표현한다는 사실을 전혀 알지 못합니다. VoiceOver, NVDA, JAWS 같은 스크린 리더는 페이지를 접근성 트리로 이해하는데, 그 트리는 다시 DOM으로부터 만들어지므로 canvas 콘텐츠는 그들에게 보이지 않습니다. 브라우저의 페이지 내 찾기와 번역 도구도 canvas 픽셀은 완전히 건너뜁니다. 네이티브 텍스트 선택은 DOM 텍스트 노드에 묶여 있고 canvas에는 이에 해당하는 기능이 없기 때문에, 사용자는 내용을 선택하거나 복사하거나 키보드로 탐색할 수 없습니다. <canvas> 요소는 또한 하나의 탭 정지점일 뿐이므로, 그 안에 수천 단어가 있어도 키보드 사용자는 단어별이나 문단별로 이동할 수 없습니다. 요컨대, 텍스트를 텍스트답게 만들어주는 모든 것, 즉 텍스트가 아니라 텍스트의 이미지가 아닌 상태를 만들어주는 모든 것이 사라집니다.
그렇다고 canvas 경로가 자동으로 틀렸다는 뜻은 아닙니다. canvas 텍스트 렌더링이 올바른 선택인 정당한 맥락도 있습니다. 게임, 데이터 시각화, 크리에이티브 설치물, 그리고 수년에 걸쳐 canvas 위에 자체 접근성 레이어를 구축해 온 디자인 도구들이 그렇습니다. SVG 렌더링의 경우는 또 다른 절충이 있는데, SVG 텍스트 요소는 접근성 트리에 참여하므로 DOM과 canvas 사이의 중간 지점이 됩니다.
하지만 canvas 경로는 돌파구가 아닙니다. canvas 텍스트 렌더링은 이미 15년 이상 수십 개의 라이브러리에서 존재해 왔기 때문입니다. 그 어떤 것도 레이아웃 비용을 지불하지 않고 DOM 텍스트 레이아웃을 예측하는 방법을 제공하지 못했습니다. Pretext의 prepare()와 layout()은 정확히 그것을 해내며, 이것은 진짜로 새로운 일입니다.
이 패턴은 프론트엔드 생태계 전반에서 자주 반복되고, 그 이유도 이해합니다.
물을 가르듯 텍스트를 나누는 드래곤은 GIF로 녹화해 소셜에 올리고 수천 번의 노출을 얻을 수 있는 종류의 것입니다. 텍스트 높이를 미리 계산하는 가상 스크롤 리스트는 그렇게 하지 않는 리스트와 겉보기에 똑같습니다. 성능 차이는 상당하지만 눈에는 보이지 않습니다. “VoiceOver에서 완벽하게 작동합니다” 또는 “단 한 번의 강제 레이아웃 없이 10,000개의 메시지를 스크롤합니다” 같은 쇼케이스를 만드는 사람은 없습니다. 이런 것들은 겉으로는 아무것도 아닌 것처럼 보이기 때문입니다. 웹 페이지가 원래 그래야 하는 방식대로 작동하는 웹 페이지처럼 보일 뿐입니다.
이것은 웹 성능에 적용된 Goodhart’s Law입니다. 어떤 지표가 목표가 되는 순간, 그것은 더 이상 좋은 측정 수단이 아니게 됩니다. 프레임 속도와 레이아웃 비용은 “이것이 사용자에게 잘 작동하는가”의 대리 지표입니다. GitHub 스타는 “이것이 유용한가”의 대리 지표입니다. 그 대신 대리 지표 자체가 최적화될 때, 이 경우 접근성 측면에서 가장 큰 대가를 치르는 경로를 사용하면서도 시각적으로 인상적인 데모를 만드는 방식으로 최적화될 때, 라이브러리를 중요하게 만드는 실제 신호는 사라집니다. 라이브러리의 정체성은 첫 72시간 동안 가장 시각적으로 인상적인 기능에 의해 규정되고, 프레이밍은 “나는 무언가를 그리고 있다”가 되지 “나는 누구보다 빠르게 무언가를 측정하고 있다”가 되지 않습니다. 일단 그런 프레이밍이 굳어지면 바꾸기 어렵습니다.
웹에서 최고의 텍스트 편집 라이브러리인 CodeMirror, Monaco, ProseMirror는 DOM을 떠나는 편이 더 빨랐을 때조차도 의도적으로 DOM에 남기로 선택했습니다. 접근성 모델은 선택 사항이 아니기 때문입니다. Pretext의 DOM 측정 경로는 이 전통에 속하지만 더 멀리 나아갑니다. 이 편집기들도 무언가의 높이를 알아야 할 때는 여전히 DOM에서 읽습니다. Pretext는 그 단계를 완전히 제거하고, 노드가 렌더링되기 전에 산술을 통해 높이를 예측합니다. 같은 철학의 다음 논리적 단계인 셈입니다. 텍스트는 있어야 할 곳에 두되, 그렇게 하기 위해 측정 비용을 계속 치르지는 않는 것입니다.
다음 글도 놓치지 마세요.
웹 플랫폼 기능, 아키텍처, 성능에 관한 비하인드 스토리.
매월. 스팸 없음.
이메일 주소
Google, Microsoft, Stripe의 개발자들이 읽고 있습니다.
저는 경력의 대부분 동안 성능 엔지니어링을 하나의 분야로 생각해 왔고, Pretext를 보며 특히 인상적인 점은 진짜 혁신이 가장 보기 어려운 것이라는 사실입니다. 텍스트가 페이지에 도달하기 전에 어떻게 배치될지를 예측하면서도 텍스트를 DOM 안에 두고, 텍스트를 접근 가능하게 만드는 모든 것을 보존하는 일은 웹 플랫폼에서 진정으로 새로운 능력입니다. 이는 복잡하고 텍스트가 많은 모든 애플리케이션이 즉시 받아들일 수 있는 종류의 기반적 개선입니다.
이번 주에 Pretext를 사용한다면, 먼저 prepare()와 layout()부터 사용하세요. 텍스트를 DOM에 남겨두고 브라우저에 묻지 않고 높이를 예측하는 무언가를 만드세요. 모든 사용자가 읽고, 선택하고, 검색하고, 탐색할 수 있는 인터페이스를 배포하세요. 아직 아무도 이것을 해내지 못했고, 그렇기 때문에 만들 가치가 있습니다.
성능 엔지니어링이 가장 좋은 상태일 때는, 누구에게도 무언가를 포기하라고 요구하지 않으면서 모두를 돕습니다. 누군가를 멀미 나게 하지 않는 더 빠른 프레임 속도. 운동 기능의 어려움이 있는 누군가에게 페이지가 필요할 때 반응하도록 만드는 더 적은 레이아웃 멈춤. 빠르고, 읽을 수 있고, 선택할 수 있고, 번역할 수 있고, 키보드로 탐색할 수 있고, 스크린 리더가 이해할 수 있는 텍스트.
드래곤은 재미있습니다. 측정 엔진은 중요합니다. 이 둘을 혼동하지 않도록 합시다.
💬 토론에 참여하세요
좋아하는 커뮤니티에서 대화를 이어가세요.
🔗 이 글 공유하기
온라인에서 주로 활동하는 곳 어디에서든 널리 알려 주세요.
Twitter/X↗LinkedIn↗ 링크 복사
메인 스레드는 당신의 것이 아니다
모든 밀리초 동안 JavaScript가 실행될 때마다, 그 시간은 사용자에게서 빌려 온 것입니다. 메인 스레드의 더 나은 손님이 되는 방법을 소개합니다. 더 읽기 →
기본적으로 빠르게
엔지니어링 팀에 더 많은 성능 영웅은 필요하지 않습니다. 빠른 경로를 쉬운 경로로 만드세요. 더 읽기 →
탈출 속도: 프레임워크 중력에서 벗어나기
React가 10년 동안 지배한 뒤, 이제는 프레임워크가 웹 안에서 실행되는 것이지 그 반대가 아니라는 사실을 다시 떠올릴 때입니다. 더 읽기 →프론트엔드 엔지니어링, 기술적 의사결정, 웹 네이티브 패턴. 매월.
Google, Microsoft, Stripe의 개발자들과 함께하세요.
© 2026 Den Odell
Astro와 Tailwind CSS로 제작됨