핵심 개념

Imposia를 만든 이유

브라우저 퍼블리싱이 반복해서 부딪히는 문제와, 거기에 Imposia가 내린 설계 선택을 설명합니다.

Imposia는 재현하기는 쉽고 고치기는 어려운 실패에서 출발했습니다. 사용자가 내용을 편집하고 인쇄하는 앱을 만들면, "같은 문서"를 주장하는 문서가 세 개 생깁니다.

같은 문서를 주장하는 세 개의 문서

전형적인 브라우저 퍼블리싱 기능은 이 순서로 자랍니다.

먼저 에디터가 내용을 렌더링해 사람이 작업할 수 있게 합니다. 다음에는 인쇄 결과를 미리 보고 싶다는 요청이 들어와 미리보기가 추가됩니다. 대개 내용을 다른 컨테이너에 복제하고 다른 CSS를 입히는 방식입니다. 마지막으로 인쇄가 실제로 동작해야 하므로 인쇄 경로가 생기는데, 이 경로는 내용을 또 한 번 — 흔히 서버나 헤드리스 브라우저에서 — 다시 조립합니다.

이제 각 표면이 각자의 트리를 측정합니다. 서로 일치할 이유가 없으므로 어긋나기 시작합니다.

  • 미리보기는 12페이지인데 PDF는 13페이지입니다.
  • 표 머리글이 화면에서는 반복되고 종이에서는 반복되지 않습니다.
  • "7페이지를 보세요"라는 참조가 한 산출물에서는 7페이지를, 다른 산출물에서는 8페이지를 가리킵니다.
  • 같은 버그가 어떤 사용자에게만 재현됩니다. 어긋남이 글꼴, 타이밍, 어느 표면이 먼저 렌더링됐는지에 따라 달라지기 때문입니다.

이것들은 레이아웃 버그가 아닙니다. 레이아웃 권한이 세 곳에 있는데 어느 것이 참인지 정하는 규칙이 없어서 생기는 결과입니다.

기존 해법과 각각의 대가

이 분야에는 좋은 도구들이 있습니다. 대부분은 권한 하나를 골라 잡고, 그 대가로 무언가를 포기하는 방식으로 충돌을 해소합니다.

접근하는 일포기하는 것
서버 렌더러(헤드리스 브라우저 또는 전용 엔진)클라이언트 밖에 권한 있는 렌더러 하나를 둡니다.미리보기는 다른 곳에서 만든 결과의 그림이 되어, 사용자가 편집 중인 내용과 여전히 어긋날 수 있습니다.
자체 컴포넌트를 가진 별도 문서 렌더러직접 제어하는 레이아웃 트리에서 결정적인 출력을 만듭니다.기존 HTML과 CSS가 적용되지 않아 내용을 두 번 작성하게 됩니다.
화면 래스터화화면에 있는 것이 그대로 산출물이 됩니다.진짜 페이지네이션이 아니고, 텍스트가 텍스트이기를 멈춥니다.

이 트레이드는 합리적이고, 많은 제품에는 맞는 선택입니다. 서버에서 생성한 PDF 바이트가 필요하다면 헤드리스 브라우저가 Imposia보다 나은 답이며, 이 문서는 그 사실을 숨기지 않습니다.

Imposia의 선택

Imposia는 다른 트레이드를 택합니다. 브라우저가 소유한 문서 하나를 두고, 모든 표면이 그 문서를 바라보게 합니다.

그 결과 Imposia에는 조판 엔진이 없습니다. 조판은 대략 네 층으로 나뉩니다.

  1. 글리프 셰이핑 — 커닝, 리거처, 문자 체계별 결합
  2. 줄바꿈 — 줄이 끝나는 위치, 하이픈 넣기, CJK 규칙
  3. 블록 레이아웃 — 여백, float, flex, grid, 표
  4. 페이지 분할 — 용지가 끝날 때 내용을 자르는 위치

1–3층은 브라우저가 수십 년에 걸쳐 다듬어 온 영역입니다. 4층은 브라우저가 거의 구현하지 않은 층입니다. 종이가 있을 때만 존재하는 문제이기 때문입니다.

그래서 Imposia는 4층만 구현하고 나머지는 브라우저에 맡깁니다. 내용을 실제 브라우저 프레임에서 렌더링하고, 브라우저가 실제로 만들어 낸 결과를 측정해, 그 측정값으로 페이지를 자릅니다.

이 방식이 보장하는 것

한국어·일본어 줄바꿈 규칙, 아랍 문자 셰이핑, 이모지 시퀀스, 가변 글꼴, 그리고 사용 중인 브라우저가 지원하는 모든 CSS 레이아웃 기능이 그대로 동작합니다. Imposia가 이것들을 구현해서가 아니라, 애초에 브라우저에서 빼앗지 않았기 때문입니다.

치르는 대가

이 선택에는 실제 단점이 있습니다. 이를 감추면 이 문서의 나머지도 믿기 어려워집니다.

  • 측정은 공짜가 아닙니다. 자를 위치를 정하려면 내용을 붙이고, 브라우저에 기하 정보를 묻고, 때로는 되돌려야 합니다. 닫힌 모델에서 레이아웃을 계산하는 것보다 느립니다.
  • 엔진은 브라우저의 것입니다. 줄바꿈이 Chromium, Firefox, WebKit마다 다르므로 페이지 수도 다를 수 있습니다. 구조 기준은 Chromium입니다. 나머지 엔진에서는 API, 격리, 수명 주기, 내보내기 동작을 검증하며, 동일한 출력은 검증 대상이 아닙니다.
  • CSS 분할은 완전히 풀린 문제가 아닙니다. 표, flex, grid, 다단 레이아웃은 문서화된 부분 집합으로 지원됩니다. 부분 집합 밖의 내용은 조용히 근사되는 대신 통째로 유지되고, 코드가 있는 경고를 남깁니다.

Imposia가 하지 않는 일

이 경계는 제품의 빈틈이 아니라 제품의 일부입니다.

  • PDF 바이트를 만들지 않습니다. print()는 완성된 페이지를 브라우저 자체의 인쇄 창에 넘기고, 사용자는 거기서 프린터나 PDF로 저장을 선택합니다. PDF 렌더러를 추가하면 나머지 셋과 어긋날 수 있는 네 번째 문서가 생기는데, 그것이 바로 이 라이브러리가 없애려는 문제입니다.
  • Node·CLI 렌더러와 서버 내보내기가 없습니다. 런타임 경계는 브라우저입니다.
  • 엔진 간 동일한 페이지 수를 약속하지 않습니다. 1–3층을 브라우저가 소유하는 한 지킬 수 없는 약속이므로, 하지 않습니다.

이 도구가 맞는지 판단하세요

적합한 경우

사용자가 내용을 보고 편집하는 React 앱이 있고, 인쇄나 PDF 결과가 지금 보고 있는 것과 일치해야 합니다.

적합하지 않은 경우

아무도 브라우저에서 미리보지 않는 내용으로, 서버에서 PDF 바이트를 만들어야 합니다.

앞의 경우가 여러분의 제품이라면 다음 단계는 첫 페이지 만들기입니다. 단일 문서 규칙이 런타임에서 실제로 어떻게 지켜지는지 보려면 퍼블리싱 모델을 읽어 보세요.

On this page