콘텐츠로 이동
getnextpdf.com

PDF 엔진이 사이드카가 아니라 PHP 안에 있어야 하는 이유

Spec: ISO/IEC 25010:2023, §3.7Spec: ISO 32000-2, §7

PDF가 만들어질 수 있는 곳은 두 군데입니다: 당신의 PHP 프로세스 안, 아니면 당신이 운영해야 하는 다른 어딘가. NextPDF는 그것을 안에서 만듭니다. 이 페이지는 그 선택에 대한 논거입니다 — 왜 프로세스 내 엔진이 대개 옳은 기본값인지, 그리고 “다른 어딘가” 패턴이 일단 프로덕션에 들어가면 실제로 무엇을 비용으로 치르는지.

이것은 프레임워크 측면이 아니라 아키텍처 측면입니다. 같은 엔진이 어떻게 Laravel, Symfony, CodeIgniter, 그리고 독립 코드에 도달하는지는 별개의 이야기이며, 하나의 엔진, 모든 프레임워크에서 다룹니다.

PDF 기능은 좀처럼 당신이 운영하는 시스템으로 시작하지 않습니다. 그것은 컨트롤러 속의 한 줄로 시작합니다: 이 인보이스를 렌더링하라, 저 보고서를 반환하라. 사이드카 패턴은 그 한 줄을 인프라로 바꿉니다. 문서를 그리려면 당신은 이제 두 번째 무언가 를 실행합니다 — 외부 바이너리, 헤드리스 브라우저, 별개의 마이크로서비스 — 그리고 그 두 번째 것이 필요로 하는 모든 것이 당신의 문제가 됩니다: 그것의 버전, 그것의 메모리, 그것의 컨테이너, 그것의 네트워크, 그것의 실패 모드, 새벽 2시의 그것의 온콜 호출.

그 비용은 데모에서는 보이지 않고 프로덕션에서는 피할 수 없습니다. 당신의 프로세스 안에 사는 문서 엔진은 그중 아무것도 없습니다. 질문은 “사이드카가 PDF를 만들 수 있는가”가 아닙니다 — 물론 만들 수 있습니다. 그것은 “거기에 도달하기 위해 당신이 운영하기로 서명한 것이 무엇이고, 당신에게 그것이 필요했는가”입니다.

  • 프로세스 내라는 것은 두 번째 런타임이 없다는 뜻입니다. NextPDF는 요청을 처리한 바로 그 PHP 워커 안에서 PDF를 그립니다. 생성할 하위 프로세스도, 배포할 서비스도, 살려 둘 추가적인 것도 없습니다.
  • 사이드카는 당신에게 없던 운영 표면을 더합니다. 번들된 브라우저나 외부 바이너리는 저마다의 버전, 저마다의 보안 발자국, 저마다의 컨테이너를 가져오며 — 당신은 이제 그 모두를 패치하고 모니터링합니다.
  • 프로세스 경계는 일이 잘못되는 곳입니다. 콜드 스타트, 타임아웃, 깨지기 쉬운 프로세스 간 배관, 그리고 당신의 프로세스를 떠나는 데이터는 프로세스 내 호출에는 그냥 없는 실패 모드입니다.
  • 프로세스 내는 테스트 가능하고 결정적입니다. 엔진은 당신이 유닛 테스트하고, 모킹하고, 추론할 수 있는 타입이 지정된 PHP입니다 — 실행해서 출력을 들여다봐야만 탐색할 수 있는 불투명한 렌더러가 아닙니다.
  • 진짜 브라우저에도 여전히 진짜 쓰임새가 있습니다. 임의의 현대 웹페이지를 픽셀에 충실하게 렌더링하려면 헤드리스 브라우저가 정직한 도구이며 — NextPDF는 의도적으로 그것에 위임할 수 있습니다. 그것은 기본값이 아니라 이음매입니다.

두 아키텍처를 나란히 놓아 보세요. 프로세스 내 경로는 함수 호출입니다. 사이드카 경로는 축소판 분산 시스템입니다 — 그리고 그 상자들 사이의 모든 화살표는 당신의 코드와 독립적으로 실패하는 지점입니다.

  1. In-process: call the enginewriteHtml() or the document API runs inside the current PHP worker — no subprocess, no socket.
  2. In-process: receive PDF bytesThe engine returns native PDF content directly; nothing left the process.
  3. Sidecar: serialize and shipMarkup or a request is marshalled out of your process to a binary, browser, or remote service.
  4. Sidecar: cross the boundaryA process spawn or network hop — with a cold start, a timeout, and an IPC contract that can break.
  5. Sidecar: run a second runtimeAn external renderer with its own version, memory profile, and security surface to operate and patch.
  6. Sidecar: deserialize backMarshal the result back in and translate the renderer’s errors into yours.
The in-process path versus the sidecar path. In-process, the PDF is produced by a typed call inside the same PHP worker and returned directly. The sidecar path adds a serialization step, a process or network boundary, an external runtime with its own version and footprint, and a deserialization step back — each a distinct failure mode the in-process call does not have.

운영할 두 번째 런타임이 없습니다. 사이드카 패턴은 하나의 기능이라는 의상을 입은 두 개의 시스템입니다. 번들된 wkhtmltopdf, 헤드리스 Chromium 서비스, 별개의 렌더 마이크로서비스 — 각각은 저마다의 릴리스 주기와 저마다의 버그를 가진 런타임입니다. 당신은 그 모두를 물려받습니다. 프로세스 내 엔진은 Composer 의존성 으로 배포됩니다. 그것은 당신의 composer.json 속 다른 모든 라이브러리와 같은 방식으로 업그레이드되며, 당신의 배포에 추가되는 데몬도, 이미지도, 소켓도 없습니다.

버전 드리프트와 더 넓은 보안 표면. 번들된 브라우저는 보안 권고가 꾸준히 흐르는, 크고 빠르게 움직이는 코드베이스입니다. 그것을 고정하면 썩고, 추적하면 요동칩니다. 어느 쪽이든 그것은 문서 하나를 먹이기 위해 당신의 공급망 안에 앉아 있는 렌더러의 전체 웹 플랫폼입니다. 프로세스 내 PHP 엔진은 당신이 읽을 수 있는, 초점이 분명한 코드 라이브러리입니다. 그 보안 표면은 당신이 이미 실행하는 PHP이지, 당신이 이제 또한 실행하는 두 번째 플랫폼이 아닙니다.

데이터는 당신의 프로세스 경계 안에 머뭅니다. 셸 아웃할 때 문서 내용 — 이것은 흔히 정확히 PDF가 담기 위해 존재하는 민감한 데이터입니다 — 은 경계를 넘습니다. 그것은 파이프, 인자, 임시 파일, 또는 서비스로 가는 네트워크 소켓에 쓰입니다. 그 하나하나가 새거나, 실수로 로깅되거나, 남겨질 수 있는 지점입니다. 프로세스 내에서는 데이터가 그것을 소유한 워커를 결코 떠나지 않습니다. 폭발 반경은 함대가 아니라 하나의 프로세스입니다.

깨지기 쉬운 배관, 콜드 스타트, 타임아웃. 프로세스 간 호출과 네트워크 호출은 함수 호출이 그럴 수 없는 방식으로 실패합니다: 시작되지 않은 하위 프로세스, 멈춰 버린 소켓, 당신이 잘못 짐작한 타임아웃, 트래픽 급증 아래의 콜드 스타트. 각각 재시도 정책, 서킷 브레이커, 예산을 필요로 합니다. 프로세스 내 렌더는 바이트를 반환하거나, 당신이 다음 줄에서 잡는 타입이 지정된 예외를 던지거나 둘 중 하나입니다. 조정해야 할 부분적 네트워크 상태가 없습니다.

관측 가능성과 테스트는 경계를 넘으면 더 어려워집니다. 사이드카의 실패는 종료 코드, 잘린 로그 한 줄, 또는 당신이 제어하지 않는 서비스로부터의 500으로 도착합니다. 그것을 재현하려면 그 환경 전체를 재현해야 합니다. 프로세스 내 엔진은 당신이 이미 쓰는 도구로 관측 가능합니다 — 스택 트레이스, 디버거, 프로파일러 — 그리고 그것은 당신의 나머지 PHP와 같은 방식으로 테스트 가능합니다. 그 테스트 가능성은 이름 붙은 소프트웨어 품질 속성입니다: ISO/IEC 25010은 그것을 유지보수성 아래에 둡니다 (Spec: ISO/IEC 25010:2023, §3.7). 그리고 프로세스 내 라이브러리는, 실행해야만 행사할 수 있는 렌더러보다 그것을 훨씬 직접적으로 충족합니다.

그 테스트가 단언하는 PDF는 블랙박스가 아니라 정의된 구조입니다. PDF 파일은 지정된 객체 및 파일 레이아웃을 가지며(Spec: ISO 32000-2, §7), 프로세스 내 엔진은 그 구조를 당신이 읽을 수 있는 코드로부터 내보냅니다 — 그래서 골든 파일 또는 구조 테스트는 당신이 관측만 할 수 있는 외부 프로그램의 출력이 아니라, 알려진 함수가 생성한 바이트를 검사합니다.

전체 요점은 몇 줄에 들어갑니다. 클라이언트도, 베이스 URL도, 헬스 체크도, 재시도 정책도 없습니다 — 두 번째 시스템이 없기 때문입니다.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
// The engine runs inside this very process. No subprocess is spawned,
// no socket is opened, and the report data never leaves the worker.
$document = Document::createStandalone();
$document->setTitle('Quarterly Report');
$document->addPage();
$html = <<<'HTML'
<h1 style="color: #1E3A8A;">Quarterly Report</h1>
<p>Rendered <strong>in-process</strong> by PHP — no browser, no sidecar.</p>
HTML;
$document->writeHtml($html);
// PDF bytes are returned directly. There is no boundary to marshal across,
// so there is no timeout, cold start, or deserialization step to handle.
$bytes = $document->getPdfData();

사이드카 버전의 형태 — 그 코드가 아니라 그 운영적 형태 — 와 견주어 보세요. 그것은 바이너리나 서비스가 설치되고 도달 가능해야 하고, 직렬화되어 전송되는 요청, 선택된 타임아웃, 렌더러가 콜드이거나 다운되었을 때를 위한 실패 경로, 그리고 다시 마셜링되는 결과를 필요로 합니다. 위 스니펫에는 그 어느 것도 없습니다. 엔진이 라이브러리일 때는 그 어느 것도 존재하지 않기 때문입니다.

흔한 가정은 “진짜” PDF 렌더링은 반드시 브라우저를 뜻하므로 프로세스 내는 장난감 버전임에 틀림없다는 것입니다. 그것은 트레이드오프를 거꾸로 본 것입니다. 브라우저는 임의의 현대 웹 콘텐츠를 정확하고 픽셀에 충실하게 렌더링해야 할 때 옳은 도구입니다. 그것은 대부분의 팀이 실제로 하는 문서 형태의 작업 — 인보이스, 보고서, 명세서, 계약서 — 에는 잘못된 기본값입니다. 그 작업에서는 레이아웃이 알려져 있고, 데이터가 당신의 것이며, 정확성이 눈이 아니라 검증기로 확인됩니다. 그런 작업에서 사이드카의 운영적 무게는 프로세스 내 엔진이 이미 주지 않는 무엇도 사 주지 않으면서, 위 섹션들의 모든 것을 당신에게 비용으로 치르게 합니다.

거울이 되는 오해는 이 페이지가 범하지 않으려고 조심하는 것입니다: 프로세스 내 엔진이 브라우저처럼 “웹 전체”를 렌더링한다고 주장하는 것. 그것은 그렇지 않으며, NextPDF는 그런 척하지 않습니다. 그것의 프로세스 내 HTML 파이프라인은 문서 레이아웃에 초점을 맞춘, 문서화된 경계를 가진 명세 정렬 부분집합입니다 — 정직한 범위는 HTML 파이프라인에 펼쳐져 있습니다. 당신에게 정말로 완전한 브라우저 충실도가 필요할 때, 그것은 조용한 폴백이 아니라 의도적이고 옵트인된 위임입니다.

프로세스 내가 옳은 기본값입니다. 그것은 하위 프로세스가 결코 정당화되지 않는다는 보편적 주장이 아닙니다. 문서가 프로세스 내 엔진이 다루지 않는 임의의 현대 CSS의 정확한 렌더링을 정말로 요구하는 경우, 헤드리스 브라우저에 위임하는 것이 옳은 선택입니다 — 그리고 NextPDF는 그 경로를, 네트워크 접근을 제한한 채로, 기본값이 아니라 이음매로서 의도적으로 지원합니다. 둘은 경쟁자가 아닙니다. 그것들은 서로 다른 작업을 위한 서로 다른 도구입니다.

이 페이지는 CSS 지원 행렬이 아니라 아키텍처를 논합니다. 프로세스 내 파이프라인이 정확히 어떤 HTML과 CSS를 다루는지는 엔진의 코드와 그 적합성 테스트로 정의되며, 그 파이프라인과 함께 문서화됩니다 — 여기서 약속되는 것이 아닙니다. “프로세스 내”는 기본 렌더링 경로를 기술하는 것이지, 가능한 모든 경로가 하위 프로세스를 피한다는 주장이 아닙니다.

역량 표면은 단순하게 유지됩니다: 프로세스 내 엔진은 Core이고, 브라우저 위임 경로는 에디션과 무관한 선택적 확장입니다.

Where the PDF is rendered — edition availability
EditionAvailability
CoreCore renders PDF in-process in PHP — no subprocess, binary, or sidecar by default.
ProThe headless-browser delegation path is an optional add-on extension, independent of edition tier.
EnterpriseThe headless-browser delegation path is an optional add-on extension, independent of edition tier.
  • HTML 파이프라인 — 프로세스 내 엔진의 정직한 범위, 그리고 브라우저에 위임하는 것이 옳은 정확한 시점.
  • 하나의 엔진, 모든 프레임워크 — 보완적 축: 스택마다 다른 라이브러리 없이 같은 프로세스 내 엔진이 어떻게 모든 PHP 프레임워크에 도달하는지.
  • 프로덕션에서 NextPDF 운영하기 — 운영할 추가 런타임 없이, 프로세스 내 엔진을 운영하는 일이 매일 어떤 모습인지.
  • 메모리와 스트리밍 — 엔진이 부하 아래에서 프로세스 내 생성을 어떻게 한정된 상태로 유지하는지.
  • 프로세스 내 생성 — 요청을 처리하는 바로 그 PHP 워커 안에서 PDF를 생성하는 것으로, 하위 프로세스, 소켓, 또는 외부 서비스가 없습니다.
  • 사이드카 — 한 가지 일을 하기 위해 당신의 애플리케이션 곁에서 실행되는 별개의 런타임; 여기서는 PDF를 당신의 프로세스 밖에서 렌더링하는 외부 바이너리, 헤드리스 브라우저, 또는 마이크로서비스.
  • 콜드 스타트 — 하위 프로세스나 서비스가 첫 요청을 처리하기 전에 무에서 시작되어야 할 때 발생하는 지연과 자원 급증.
  • IPC — 프로세스 간 통신: 별개의 프로세스로 데이터를 주고받는 데 쓰이는 파이프, 소켓, 임시 파일, 또는 네트워크 호출이며, 깨지기 쉽고 디버깅하기 어려운 실패의 되풀이되는 원천.
  • 브라우저 위임 이음매 — 정확한 충실도를 위해 렌더를 헤드리스 브라우저에 넘기는, 하위 리소스 네트워크 접근이 차단된 선택적 옵트인 경로; 기본값이 아니라 의도적인 선택.