FEInterview Prep

Velog

잘못된 Pretext 데모를 보고 있습니다

Pretext의 진짜 혁신은 화려한 용 데모가 아니라 prepare()/layout() 2단계로 강제 reflow 없이 DOM 텍스트를 유지하며 측정하는 아키텍처다.

2026-04-09·8분 읽기
성능브라우저아키텍처
원문 보기 ↗

핵심 요약

Pretext의 진짜 가치는 용(dragon) 캔버스 데모가 아니라 prepare()/layout() API에 있습니다. prepare()Intl.Segmenter 로 단어 경계를 나누고 canvas.measureText 로 너비를 캐시해 한 번만 비용을 치르고(~19ms), layout() 은 캐시된 너비로 순수 산술을 돌려 500개 텍스트를 약 0.09ms 에 레이아웃합니다. 이 경로는 DOM을 읽지 않으므로 getBoundingClientRect 의 강제 reflow를 완전히 제거하지만, 텍스트는 DOM에 남겨 접근성 트리·스크린 리더·페이지 내 검색·번역 이 모두 동작합니다. 반대로 용 데모처럼 캔버스로 픽셀을 찍는 경로는 시각적으로 화려하지만 스크린 리더와 네이티브 선택·검색이 통째로 사라집니다. 굿하트의 법칙처럼, 커뮤니티가 시각적 임팩트 라는 대리 지표에 최적화하면 '측정 엔진' 이라는 진짜 가치는 묻혀 버립니다.

이 글은 '화려한 데모에 속지 말고 라이브러리의 핵심 트레이드오프가 어디서 결정되는지 보라' 는 시니어의 렌즈로 읽으면 좋습니다. Pretext를 '캔버스로 렌더하는 또 다른 라이브러리'로 분류하면 본질을 놓칩니다. 실제로는 canvas.measureText측정 엔진으로만 쓰고 렌더는 DOM에 남겨, '성능 vs 접근성' 이라는 오래된 이분법을 깨뜨리는 아키텍처 선언문에 가깝습니다.

프론트엔드 실무에서 virtualization, masonry, shrinkwrap 같은 패턴은 결국 '렌더 전에 텍스트 높이를 알 수 있는가' 에서 막힙니다. Pretext가 제시하는 해법이 왜 중요한가:

  • getBoundingClientRect 없이 높이를 구해 강제 레이아웃 재계산(layout thrashing) 을 제거
  • DOM 텍스트는 그대로 두어 스크린 리더·페이지 내 검색·번역·복사가 모두 동작
  • prepare()/layout() 분리로 리사이즈 같은 반복 이벤트의 비용을 amortize

면접에서 '강제 reflow 를 어떻게 피하는가' 를 물었을 때 막연히 'rAF 로 감싼다' 이상의 구체적 기법을 제시할 수 있는 근거가 됩니다.

학습 포인트

면접 답변으로 연결할 학습 포인트입니다.

강제 reflow의 본질

getBoundingClientRect, offsetHeight, clientWidth 같은 API는 브라우저가 즉시 최신 레이아웃을 계산 하도록 강요합니다. 가상 리스트에서 500번 호출하면 500번의 동기 reflow가 프레임을 먹습니다(layout thrashing). Pretext는 DOM에 묻지 않고 캔버스 측정값으로 답해 이 멈춤을 0으로 만듭니다.

// 전형적 layout thrashing
items.forEach(el => {
  const h = el.getBoundingClientRect().height; // read
  el.style.top = `${offset}px`;                // write → 다음 read에서 또 reflow
  offset += h;
});

rAF 로 감싸면 read/write 순서는 재배열되지만 reflow 자체는 사라지지 않습니다. Pretext 방식은 DOM read를 아예 제거합니다.

layout thrashingforced reflowgetBoundingClientRectcanvas.measureText
자주 하는 오해

requestAnimationFrame 으로 감싸면 된다고 생각하는 것. rAF는 읽기/쓰기 스케줄링을 도울 뿐, 브라우저가 최신 레이아웃을 계산해야 한다는 사실 자체는 그대로입니다.

DOM 유지가 곧 접근성 유지

텍스트를 캔버스로 그리면 다음이 모두 멈춥니다.

기능DOM 텍스트Canvas 픽셀
스크린 리더 (VoiceOver/NVDA/JAWS)OX
네이티브 텍스트 선택/복사OX
페이지 내 검색 (Cmd+F)OX
브라우저/확장 번역OX
키보드 탭 네비게이션O단일 탭 스톱

Pretext는 높이만 수학적으로 예측 하고 실제 텍스트는 DOM에 그대로 두므로 접근성 트리가 온전합니다. CodeMirror·Monaco·ProseMirror 같은 주요 에디터가 캔버스로 가지 않은 이유가 바로 이것입니다.

accessibility treescreen readerDOM textcanvas rendering
자주 하는 오해

'어차피 소수 사용자니까' 라며 접근성을 비용으로 보는 것. 번역·검색·복사는 모든 사용자 가 매일 쓰는 기능입니다.

2단계 아키텍처: 무거운 준비 + 빠른 경로

prepare()layout() 을 분리하는 것이 Pretext의 핵심 설계입니다.

단계하는 일비용호출 빈도
prepare()단어 분할(Intl.Segmenter) + 너비 측정(measureText) + 캐싱~19ms / 500개1회
layout()캐시된 너비를 누적해 줄바꿈 찾기 (순수 산술)~0.09ms / 500개리사이즈마다

리사이즈·스크롤처럼 자주 발생하는 이벤트는 전부 빠른 경로로 처리되므로 전체 비용이 amortize 됩니다.

const prepared = pretext.prepare(items); // 한 번
window.addEventListener('resize', () => {
  const boxes = pretext.layout(prepared, container.width); // 매 프레임 OK
});
amortized costIntl.Segmenterprepare/layoutfast path
자주 하는 오해

prepare 비용만 보고 '500배 빠르다는 비교는 불공정' 이라며 가치를 깎아내리는 것. 비용이 1회, 빠른 경로가 N회 라는 분할상환 구조를 놓치면 판단이 왜곡됩니다.

굿하트의 법칙과 데모 편향

측정이 목표가 되면, 그것은 더 이상 좋은 측정이 아니다. — Goodhart's Law

GitHub 스타와 소셜 임프레션은 '유용함' 의 대리 지표 일 뿐입니다. 이를 목표로 삼으면 시각적으로 인상적인 용 데모가 라이브러리의 정체성을 결정해 버리고, 진짜 가치인 '측정 엔진' 은 '잘 돌아가는 웹페이지처럼 보이기 때문에' 눈에 띄지 않습니다. 평가자에게 측정 가능한 숫자(reflow 횟수, 프레임 예산) 를 기준으로 제시해야 허상의 데모에 휘둘리지 않습니다.

Goodhart's Lawproxy metricvanity metric
자주 하는 오해

별 수가 높으니 품질이 높다고 결론짓는 것. 별은 초기 데모의 시각적 임팩트를 반영할 뿐, 아키텍처의 정합성과는 상관이 약합니다.

읽는 순서

  1. 1이론

    Chrome DevTools Performance 탭에서 'Forced reflow' 경고가 찍히는 조건을 직접 재현하세요. 루프 안에서 getBoundingClientRectstyle.height 를 번갈아 호출하는 예제와, rAF 로 read/write 를 분리한 예제의 플레임 차트를 비교합니다.

  2. 2구현

    canvas.measureText 로 특정 폰트·크기의 단어 너비를 재고, 주어진 container width 에서 줄바꿈 지점을 계산하는 함수를 직접 작성하세요. Intl.Segmenter 로 단어 분할까지 붙이면 Pretext의 layout() 이 하는 일을 체득할 수 있습니다.

  3. 3실무

    담당 프로젝트에서 가장 무거운 리스트(채팅, 피드, 메이슨리) 를 골라 getBoundingClientRect 호출을 세고, 측정 경로를 Pretext 식 2단계 구조로 바꿀 수 있는지 설계 문서를 써 보세요. 트레이드오프(웹폰트 swap, 국제화 텍스트) 를 함께 기록합니다.

  4. 4설명

    '성능 개선과 접근성은 트레이드오프다' 라는 주장에 Pretext 사례로 반박하는 5분짜리 설명을 준비하세요. 용 데모를 일부러 비교군으로 넣고, 어느 사용자 시나리오에서 무엇이 깨지는지 표로 제시합니다.

면접 연결 질문

hard가상 리스트(virtual scrolling)에서 각 항목의 높이를 미리 알아야 할 때, DOM 측정 없이 높이를 구하는 방법과 그 트레이드오프는?
힌트

[감점 답변] '고정 높이를 가정한다' 에서 멈추면 감점. [좋은 답변] canvas.measureText 기반 사전 측정 + 단어 경계(Intl.Segmenter)·양방향 텍스트·웹폰트 로드 타이밍 같은 엣지 케이스까지 언급하고, DOM 텍스트를 유지해 접근성을 보존하는 경로(Pretext 방식)를 '500개 ~0.09ms' 같은 구체 수치 와 함께 설명. 트레이드오프로 폰트·letter-spacing·커닝 차이로 측정 오차가 있어 텍스트 CSS를 폭넓게 허용하기 어렵다는 점을 덧붙이면 만점.

medium캔버스로 텍스트를 렌더링해 60fps를 달성한 데모를 본 동료가 '이걸 프로덕션에 도입하자' 고 합니다. 어떻게 반박하시겠어요?
힌트

[감점 답변] '느려서 안 된다' 같은 사실 오인은 감점. [좋은 답변] 스크린 리더가 캔버스를 건너뛰고, 네이티브 선택·Cmd+F 검색·번역 확장·복사가 사라지며 canvas 가 단일 탭 스톱이 되는 접근성 손실 을 구체적으로 나열. CodeMirror·Monaco·ProseMirror 같은 주요 에디터가 DOM에 남은 이유, 그리고 '측정은 canvas, 렌더는 DOM' 이라는 Pretext 식 절충안을 제시.

medium프로파일러 없이도 layout thrashing 을 의심할 수 있는 코드 패턴은?
힌트

[감점 답변] '성능이 느리면 재계산' 수준은 감점. [좋은 답변] 다음 패턴을 구체적으로:

  • 루프 안에서 read(getBoundingClientRect/offsetHeight) → write(style/classList) → read 가 섞이는 경우
  • ResizeObserver 콜백 내 동기 DOM write
  • 스크롤 핸들러에서 측정/수정이 혼재

해결로 rAF 기반 read/write 분리, FastDOM 같은 배치 라이브러리, Pretext 식 사전 측정(아예 DOM read 제거) 을 단계적으로 제시.

자기 점검

Pretext의 `prepare`/`layout` 2단계가 왜 '공정한 비교' 인지 amortized cost 관점에서 설명해 보세요.
amortizedpreparefast pathresize
자주 하는 오해

prepare 비용이 19ms 라서 이득이 적다고 판단하는 것. 1회 치르는 고정 비용이 이후 모든 layout 호출로 나눠지므로 장기적 단가는 매우 낮습니다.

'DOM 텍스트를 유지한 채 높이를 예측' 하는 것이 '캔버스로 텍스트를 그리는 것' 보다 사용자 가치가 큰 이유를 3가지 사용자 시나리오로 설명해 보세요.
스크린 리더번역페이지 내 검색키보드 네비게이션
자주 하는 오해

접근성을 소수 사용자만의 문제로 축소하는 것. 번역·검색·복사는 모든 사용자의 일상 기능입니다.