고급 기능
기본 HTML-to-PDF 렌더링 외에도 Artisan 패키지는 문서 병합, 전역 스타일 주입, 스크린샷 캡처, Chrome 동작 세부 조정을 위한 유틸리티를 제공합니다.
PDF 병합
PdfMerger 클래스는 여러 HTML 소스를 단일 PDF 문서로 결합합니다. 각 소스는 별도의 섹션으로 렌더링되며 결과는 순서대로 연결됩니다.
use Yeeefang\TcpdfNext\Artisan\PdfMerger;
use Yeeefang\TcpdfNext\Artisan\RenderOptions;
$merger = PdfMerger::create();
$merger
->addHtml('<h1>Cover Page</h1><p>Annual Report 2026</p>')
->addFile('/templates/chapter-1.html')
->addFile('/templates/chapter-2.html')
->addUrl('https://charts.example.com/annual-summary')
->addHtml('<h1>Appendix</h1><p>Supporting data tables.</p>');
$merger->save('/reports/annual-2026.pdf');섹션별 옵션
각 섹션은 자체 RenderOptions를 가질 수 있습니다. 예를 들어, 표지는 가로 방향이고 챕터는 세로 방향입니다.
$coverOptions = RenderOptions::create()
->setPageSize('A4')
->setLandscape(true)
->setPrintBackground(true);
$chapterOptions = RenderOptions::create()
->setPageSize('A4')
->setLandscape(false)
->setDisplayHeaderFooter(true)
->setFooterTemplate('
<div style="font-size: 8px; text-align: center; width: 100%; color: #888;">
Page <span class="pageNumber"></span>
</div>
');
PdfMerger::create()
->addHtml('<h1>Cover</h1>', options: $coverOptions)
->addFile('/templates/chapter-1.html', options: $chapterOptions)
->addFile('/templates/chapter-2.html', options: $chapterOptions)
->save('/reports/merged.pdf');CSS 주입
StyleInjector 클래스는 렌더링된 페이지에 CSS 규칙을 삽입합니다. 이는 제어할 수 없는 템플릿에 전역 브랜드 스타일시트를 적용할 때 유용합니다.
use Yeeefang\TcpdfNext\Artisan\HtmlRenderer;
use Yeeefang\TcpdfNext\Artisan\StyleInjector;
$injector = StyleInjector::create()
->addCss('
body {
font-family: "Inter", "Noto Sans TC", sans-serif;
font-size: 11pt;
line-height: 1.6;
color: #333;
}
h1 { color: #1a237e; }
')
->addCssFile('/styles/brand.css');
HtmlRenderer::create()
->loadFile('/templates/report.html')
->withStyleInjector($injector)
->save('/output/branded-report.pdf');다중 스타일 레이어
스타일은 추가된 순서대로 주입됩니다. 나중 규칙이 이전 규칙을 재정의하며, 표준 CSS 명시도를 따릅니다.
$injector = StyleInjector::create()
->addCssFile('/styles/reset.css')
->addCssFile('/styles/brand.css')
->addCss('table { page-break-inside: avoid; }'); // 재정의스크린샷
ScreenshotCapture 클래스는 PDF 대신 이미지 형식으로 HTML을 렌더링합니다. 썸네일, 소셜 미디어 미리보기 또는 비주얼 회귀 테스트 생성에 유용합니다.
use Yeeefang\TcpdfNext\Artisan\ScreenshotCapture;
// 전체 페이지 스크린샷을 PNG로
ScreenshotCapture::create()
->loadHtml('<h1>Preview</h1><p>This will be a PNG image.</p>')
->fullPage(true)
->format('png')
->save('/output/preview.png');품질이 지정된 JPEG
ScreenshotCapture::create()
->loadUrl('https://example.com/dashboard')
->format('jpeg')
->quality(85)
->save('/output/dashboard.jpg');뷰포트 구성
ScreenshotCapture::create()
->loadFile('/templates/email.html')
->viewport(width: 1200, height: 800)
->deviceScaleFactor(2) // 레티나
->fullPage(false)
->save('/output/email-preview.png');Chrome 구성
사용자 정의 바이너리 경로
Artisan은 일반적인 OS 경로에서 Chrome을 자동 감지합니다. chromePath로 재정의합니다.
use Yeeefang\TcpdfNext\Artisan\HtmlRenderer;
$renderer = HtmlRenderer::create(
chromePath: '/opt/google/chrome/chrome',
);또는 CHROME_PATH 환경 변수를 전역으로 설정합니다.
헤드리스 모드 플래그
Artisan은 기본적으로 --headless=new를 전달합니다(Chrome 112+). 특정 환경에 대한 추가 플래그를 추가할 수 있습니다.
$renderer = HtmlRenderer::create(
chromeFlags: [
'--no-sandbox', // Docker에서 필수
'--disable-gpu', // Docker에서 권장
'--disable-dev-shm-usage', // /dev/shm 문제 방지
'--font-render-hinting=none',
],
);연결 풀링
고처리량 시나리오에서는 여러 렌더링 간에 단일 Chrome 인스턴스를 재사용합니다. 이는 각 후속 렌더링에 대한 시작 비용(약 300--500ms)을 제거합니다.
use Yeeefang\TcpdfNext\Artisan\ChromeBridge;
use Yeeefang\TcpdfNext\Artisan\HtmlRenderer;
// 영구 브리지 생성
$bridge = ChromeBridge::create(chromePath: '/usr/bin/chromium');
// 여러 렌더링 간에 재사용
foreach ($reports as $report) {
HtmlRenderer::createWithBridge($bridge)
->loadHtml($report->html)
->save("/output/{$report->id}.pdf");
}
// 완료 시 명시적으로 닫기
$bridge->close();오류 처리
모든 Artisan 예외는 Yeeefang\TcpdfNext\Artisan\Exceptions\ArtisanException을 확장합니다.
use Yeeefang\TcpdfNext\Artisan\Exceptions\RenderException;
use Yeeefang\TcpdfNext\Artisan\Exceptions\ChromeNotFoundException;
use Yeeefang\TcpdfNext\Artisan\Exceptions\TimeoutException;
try {
HtmlRenderer::create()->loadUrl($url)->save($path);
} catch (ChromeNotFoundException $e) {
// Chrome 설치 또는 CHROME_PATH 설정
} catch (TimeoutException $e) {
// 타임아웃 증가 또는 네트워크 확인
} catch (RenderException $e) {
// $e->getMessage()로 상세 정보 확인
}| 예외 | 일반적인 원인 |
|---|---|
ChromeNotFoundException | Chrome 미설치, CHROME_PATH 잘못됨 |
TimeoutException | 느린 페이지, 해결되지 않은 JS 프로미스, 네트워크 문제 |
RenderException | 잘못된 HTML, Chrome 충돌, 디스크 쓰기 실패 |
성능 팁
- Chrome 인스턴스 재사용 -- 배치 작업에
ChromeBridge를 사용합니다. - JavaScript 최소화 -- Chrome이 실행해야 할 JS가 적을수록 렌더링이 빨라집니다.
- 크리티컬 CSS 인라인화 -- 가능한 경우 외부 스타일시트 가져오기를 피합니다.
- 짧은 타임아웃 설정 -- 큐를 차단하는 것보다 깨진 페이지에서 빠르게 실패합니다.
setPrintBackground(false)사용 -- 배경 렌더링이 필요하지 않을 때 건너뜁니다.