NextPDF Symfony 빠른 시작
한눈에 보기
섹션 제목: “한눈에 보기”여기서는 PdfFactory를 주입하고 Document를 빌드한 다음 PdfResponse로 반환합니다. 백그라운드에서 PDF를 생성하려면 GeneratePdfMessage를 Messenger 전송으로 디스패치합니다.
1 단계 — 컨트롤러에서 PDF 생성
섹션 제목: “1 단계 — 컨트롤러에서 PDF 생성”먼저 NextPDF\Symfony\Service\PdfFactory를 주입합니다. 이때 create() 메서드는 새로운 NextPDF\Core\Document를 반환합니다. 구성된 기본값(작성자 도구, 저자, 언어)은 이미 적용되어 있습니다. 이 문서를 NextPDF\Symfony\Http\PdfResponse로 반환합니다.
<?php
declare(strict_types=1);
namespace App\Controller;
use NextPDF\Symfony\Http\PdfResponse;use NextPDF\Symfony\Service\PdfFactory;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Routing\Attribute\Route;
final class InvoiceController{ #[Route('/invoice/{number}', name: 'invoice_pdf')] public function download(PdfFactory $pdf, string $number): Response { $doc = $pdf->create(); $doc->addPage(); $doc->cell(0, 10, "Invoice #{$number}", newLine: true); $doc->cell(0, 10, 'Thank you for your business.');
return PdfResponse::download($doc, "invoice-{$number}.pdf"); }}PdfResponse::download()은 Symfony\Component\HttpFoundation\Response를 반환합니다. 여기에는 Content-Type: application/pdf, attachment disposition, Content-Length와 번들의 고정 보안 헤더가 포함됩니다. Symfony는 표준 Response 클래스와 그 헤더 모델을 문서화합니다(https://symfony.com/doc/current/components/http_foundation.html).
2 단계 — PDF 를 인라인으로 표시
섹션 제목: “2 단계 — PDF 를 인라인으로 표시”브라우저가 PDF를 다운로드하지 않고 표시하게 하려면 inline()을 사용합니다:
return PdfResponse::inline($doc, 'preview.pdf');disposition은 inline이 됩니다. 다른 모든 헤더는 동일하게 유지됩니다.
3 단계 — 대용량 PDF 스트리밍
섹션 제목: “3 단계 — 대용량 PDF 스트리밍”대용량 문서의 경우 스트리밍 변형은 PDF를 64 KB 청크 단위로 내보냅니다. 이렇게 하면 최대 메모리 사용량을 줄일 수 있습니다. 이 변형들은 Symfony\Component\HttpFoundation\StreamedResponse를 반환하며 Content-Length를 생략합니다.
return PdfResponse::streamDownload($doc, 'annual-report.pdf');streamInline()은 인라인 버전에 해당합니다. Symfony는 StreamedResponse 콜백 계약을 문서화하며, 이는 출력을 플러시하는 void 콜러블입니다(https://symfony.com/doc/current/components/http_foundation.html).
4 단계 — PDF 를 비동기로 생성
섹션 제목: “4 단계 — PDF 를 비동기로 생성”symfony/messenger가 설치되어 있으면 생성 작업을 요청 스레드에서 분리할 수 있습니다.
4a — 빌더 구현
섹션 제목: “4a — 빌더 구현”먼저 NextPDF\Symfony\Message\PdfBuilderInterface를 구현합니다. 핸들러는 새로 생성되어 사전 구성된 Document를 전달합니다. 메시지에는 직렬화 가능한 컨텍스트도 전달합니다.
<?php
declare(strict_types=1);
namespace App\Pdf;
use NextPDF\Core\Document;use NextPDF\Symfony\Message\PdfBuilderInterface;
final class InvoicePdfBuilder implements PdfBuilderInterface{ public function build(Document $document, array $context): Document { $document->addPage(); $document->setFont('dejavusans', '', 12); $document->cell(0, 10, 'Invoice #' . $context['invoice_id']);
return $document; }}4b — 로케이터에 빌더 등록
섹션 제목: “4b — 로케이터에 빌더 등록”핸들러는 클래스 이름을 키로 사용하는 PSR-11 서비스 로케이터에서 빌더를 확인합니다. 따라서 등록된 빌더에만 접근할 수 있습니다. config/services.yaml의 로케이터에 빌더를 추가합니다:
services: App\Pdf\InvoicePdfBuilder: ~
nextpdf.pdf_builder_locator: class: Symfony\Component\DependencyInjection\ServiceLocator arguments: - 'App\Pdf\InvoicePdfBuilder': '@App\Pdf\InvoicePdfBuilder' tags: ['container.service_locator']
NextPDF\Symfony\Message\GeneratePdfHandler: arguments: $builderLocator: '@nextpdf.pdf_builder_locator'핸들러는 로케이터에 클래스 문자열 ID로 빌더를 요청합니다. PSR-11 컨테이너 식별자는 항목을 고유하게 식별하는 문자열입니다(PSR-11 §1.1.2).
4c — 메시지 디스패치
섹션 제목: “4c — 메시지 디스패치”먼저 Symfony\Component\Messenger\MessageBusInterface를 주입한 다음 메시지를 디스패치합니다:
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Pdf\InvoicePdfBuilder;use NextPDF\Symfony\Message\GeneratePdfMessage;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\Messenger\MessageBusInterface;use Symfony\Component\Routing\Attribute\Route;
final class ReportController{ #[Route('/invoice/{id}/queue', name: 'invoice_queue')] public function queue(MessageBusInterface $bus, int $id): Response { $bus->dispatch(new GeneratePdfMessage( builderClass: InvoicePdfBuilder::class, outputPath: '/var/storage/invoices/' . $id . '.pdf', builderContext: ['invoice_id' => $id], ));
return new Response('PDF generation queued.', 202); }}GeneratePdfMessage는 readonly DTO입니다. 생성자는 비어 있거나 .pdf가 아닌 출력 경로, 경로 탐색 세그먼트, 스트림 래퍼 스킴, 널 바이트를 거부합니다. 또한 builderClass는 구문상 유효한 클래스 이름이어야 합니다. 핸들러는 쓰기 전에 실행 시점에 출력 경로를 다시 검증합니다. 따라서 디스패치 시점에는 안전했지만 소비 시점에는 안전하지 않은 경로도 여전히 거부됩니다. #[AsMessageHandler] 어트리뷰트와 MessageBusInterface 디스패치 계약은 표준 Symfony Messenger 모델을 따릅니다(https://symfony.com/doc/current/messenger.html).
4d — 메시지 라우팅 및 워커 실행
섹션 제목: “4d — 메시지 라우팅 및 워커 실행”다음으로 config/packages/messenger.yaml에서 메시지를 전송으로 라우팅합니다:
framework: messenger: transports: async: '%env(MESSENGER_TRANSPORT_DSN)%' routing: NextPDF\Symfony\Message\GeneratePdfMessage: async그런 다음 워커를 실행합니다:
php bin/console messenger:consume async동작 확인
섹션 제목: “동작 확인”php bin/console debug:container --tag=container.service_locatorphp bin/console messenger:consume async --limit=1 -vv첫 번째 명령은 빌더 로케이터가 등록되었는지 확인합니다. 두 번째 명령은 큐에 있는 단일 메시지를 소비하고 핸들러의 진행 상황을 출력합니다.
다음 단계
섹션 제목: “다음 단계”- /integrations/symfony/configuration/ — 기본값, 글꼴, 문서 서비스를 조정합니다.
- /integrations/symfony/production-usage/ — 워커 안전성과 부하 상황에서의 스트리밍.
- /integrations/symfony/troubleshooting/ — 일반적인 부팅 및 런타임 문제 해결.
적합성
섹션 제목: “적합성”각 행은 이 페이지에서 제시하는 규범적 주장을 나타내며, 게이트된 SDO 코퍼스의 전체 64자리 16진수 reference_id에 고정되어 있습니다. 출처(코퍼스 매니페스트, 검색 전송)는 _sidecars/rag-citations.yaml에 있습니다.
| 사양 | 조항 | reference_id (참조 ID) | 주장 |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p4 | 컨테이너 has()/get() 식별자 계약 |
참고 항목
섹션 제목: “참고 항목”- /integrations/symfony/overview/ — 기능 요약.
- /integrations/symfony/install/ — 설치 및 등록.
- /integrations/symfony/integration/ — 엔드투엔드 연결 참조.