콘텐츠로 이동

NextPDF Laravel 패키지 프로덕션 사용

프로덕션에서는 생성자 주입으로 문서 계약을 해석합니다. PDF 쓰기 실패는 특정 예외로 처리합니다. 부하가 크거나 배치 형태인 생성 작업은 GeneratePdfJob으로 옮기고, 명시적인 성공 및 실패 콜백을 연결합니다.

Terminal window
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

큐 연결은 config/nextpdf.php에서 구성합니다. queue.connection, queue.queue, queue.timeout 값을 설정합니다. 그런 다음 구성된 연결에서 워커가 실행 중인지 확인합니다.

컨테이너는 NextPDF\Contracts\PdfDocumentInterface를 팩토리 바인딩으로 노출합니다. 해석될 때마다 새 NextPDF\Core\Document를 반환합니다. PSR-11은 바인딩 전략에 따라 컨테이너가 연속된 get() 호출에서 서로 다른 값을 반환하도록 허용합니다(PSR-11 §1.1.2). 이 패키지는 여기서 팩토리 바인딩을 사용하여 요청 범위의 가변 상태가 요청 간에 절대 넘어가지 않도록 합니다. 글꼴 레지스트리와 이미지 레지스트리는 싱글턴입니다. 이렇게 하면 바인딩된 식별자가 등록된 항목으로 해석된다는 계약(PSR-11 §1.1.2)을 유지하면서도, 비용이 큰 리소스를 워커 전체에서 공유할 수 있습니다.

프로덕션 코드에서는 파사드보다 생성자 주입을 사용하는 편이 좋습니다. 이렇게 하면 의존성이 명시적으로 드러나고, 파사드 루트를 부팅하지 않고도 컨트롤러를 단위 테스트할 수 있습니다.

형식화된 오류 처리를 갖춘 DI 컨트롤러

섹션 제목: “형식화된 오류 처리를 갖춘 DI 컨트롤러”
resource: NextPDF\Contracts\PdfDocumentInterface + src/Laravel/Http/PdfResponse.php
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use NextPDF\Contracts\PdfDocumentInterface;
use NextPDF\Laravel\Http\PdfResponse;
use Psr\Log\LoggerInterface;
use Throwable;
final class InvoiceController extends Controller
{
public function __construct(
private readonly PdfDocumentInterface $document,
private readonly LoggerInterface $logger,
) {}
public function show(int $invoiceId): Response
{
try {
$this->document->addPage();
$this->document->cell(0, 10, "Invoice #{$invoiceId}", newLine: true);
$this->document->cell(0, 10, 'Thank you for your business.');
return PdfResponse::download(
$this->document,
"invoice-{$invoiceId}.pdf",
);
} catch (Throwable $exception) {
// Rethrow as an HTTP-meaningful failure; never swallow.
$this->logger->error('Invoice PDF generation failed', [
'invoice_id' => $invoiceId,
'exception' => $exception::class,
]);
return new Response('Could not generate the invoice PDF.', 500);
}
}
}

주입할 때는 PdfDocumentInterface를 사용하고, 구체 타입인 Document는 사용하지 않아 테스트에서 바인딩을 교체할 수 있게 유지합니다. 컨테이너는 컨트롤러를 인스턴스화할 때마다 새 문서를 반환합니다. 하나의 프로세스 내에서 서로 무관한 두 문서에 동일한 컨트롤러 인스턴스를 재사용하지 마십시오.

catch 블록은 스택 추적을 노출하는 대신 예외 클래스를 로깅하고 정의된 HTTP 오류를 반환합니다. Psr\Log\LoggerInterface를 사용하면 컨테이너가 이를 프레임워크 로거로 해석합니다. PSR-3은 플레이스홀더 이스케이프를 구현자에게 맡기며, 호출자에게는 컨텍스트 값을 미리 이스케이프하지 말라고 명시합니다(PSR-3 §1.2). 보간된 문자열이 아니라 구조화된 컨텍스트를 전달하십시오.

성공 및 실패 콜백을 사용하는 큐 기반 생성

섹션 제목: “성공 및 실패 콜백을 사용하는 큐 기반 생성”

GeneratePdfJobShouldQueue 작업입니다. 기본값은 재시도 3 회, 120 초 시간 초과, 10 초 백오프입니다. 이 세 가지는 모두 config/nextpdf.php를 통해 재정의할 수 있습니다. 빌더 클로저는 컨테이너에서 해석된 문서를 받으며, 구성된 문서를 반환해야 합니다.

resource: src/Laravel/Jobs/GeneratePdfJob.php
<?php
declare(strict_types=1);
namespace App\Jobs;
use NextPDF\Contracts\PdfDocumentInterface;
use NextPDF\Laravel\Jobs\GeneratePdfJob;
use Psr\Log\LoggerInterface;
use Throwable;
final class DispatchMonthlyStatement
{
public function __construct(private readonly LoggerInterface $logger) {}
public function __invoke(int $accountId): void
{
// Dispatchable::dispatch() is `public static`: it constructs the
// job from the arguments it receives and returns a PendingDispatch.
// Pass every constructor argument — including the callbacks — to
// the static call. Building an instance and then calling
// `$job->dispatch(...)` would discard that instance (and its
// callbacks) and queue a different job from only the static args.
GeneratePdfJob::dispatch(
storage_path("app/statements/{$accountId}.pdf"),
static fn (PdfDocumentInterface $document): PdfDocumentInterface => $document
->addPage()
->cell(0, 10, "Statement for account {$accountId}", newLine: true),
function (string $path) use ($accountId): void {
$this->logger->info('Statement PDF written', [
'account_id' => $accountId,
'path' => $path,
]);
},
function (Throwable $exception) use ($accountId): void {
$this->logger->error('Statement PDF failed', [
'account_id' => $accountId,
'exception' => $exception::class,
]);
},
);
}
}

GeneratePdfJob::dispatch()는 인수를 생성자 (string $outputPath, callable $builder, ?callable $onSuccess, ?callable $onFailure)에 그대로 전달합니다. 따라서 성공 및 실패 콜백은 큐에 등록되는 바로 그 작업에 연결됩니다. 이는 /integrations/laravel/quickstart/의 위치 인수 형식 GeneratePdfJob::dispatch($path, $builder)과 일치합니다. 성공 콜백은 출력 경로를 받고, 실패 콜백은 Throwable을 받습니다. 또한 이 작업은 체이닝을 위해 작업 자신을 반환하는 플루언트 then()catch() 세터를 노출합니다. 이 세터는 동일한 인스턴스를 유지한 상태로, 예를 들어 dispatch() 헬퍼를 통해 그 인스턴스를 디스패치할 때만 사용하십시오. 이 작업은 failed() 메서드도 노출하며, 큐 러너는 최종 실패 시 이를 호출합니다. 콜백은 직렬화 가능한 클로저로 래핑되어 큐를 통해 전송되어도 보존됩니다.

속성기본값구성 키
tries3구성으로 제어되지 않음. 변경하려면 하위 클래스로 확장
timeout120nextpdf.queue.timeout
backoff10구성으로 제어되지 않음. 변경하려면 하위 클래스로 확장
큐 이름pdfnextpdf.queue.queue
연결기본값(default)nextpdf.queue.connection

triesbackoff는 작업 인스턴스에서 읽는 공용 속성입니다. 기본 제공 작업은 이를 구성에서 가져오지 않습니다. 다른 재시도 정책이 필요해 이를 재정의하려면 GeneratePdfJob을 하위 클래스로 확장하십시오.

  • 빌더 클로저는 PdfDocumentInterface를 반환해야 합니다. 작업은 원래 해석된 인스턴스가 아니라 그 반환 값을 저장합니다. 작업 테스트는 이 계약을 명시적으로 검증합니다.
  • 해석 시 SignerInterfacenull을 반환합니다. 다만 서명이 활성화되어 있고 인증서가 구성되어 있으며 nextpdf/premium이 설치되어 있는 경우는 예외입니다. 서명하기 전에 항상 null 검사를 수행하십시오.
  • 장기 실행 워커(Octane/RoadRunner/Swoole)는 잠긴 글꼴 레지스트리를 공유합니다. 워밍업이 첫 번째 요청이 아니라 워커 부팅 시점에 한 번만 일어나도록 preload_fonts를 구성하십시오.
  • 실패한 작업은 failed()를 호출하며, 이는 tries를 모두 소진한 후에 일어납니다. 시도별 실패는 큐 러너가 최종 실패를 선언하기 전까지는 onFailure를 호출하지 않습니다.

동기식 컨트롤러에서 생성하면 전체 PDF 빌드 동안 요청이 차단됩니다. 여러 페이지 또는 배치 출력의 경우 GeneratePdfJob을 디스패치하고 즉시 반환하십시오. 싱글턴 레지스트리는 글꼴 파싱과 이미지 디코딩 비용을 워커 수명 전체에 걸쳐 분산시킵니다. 그러면 요청당 비용은 문서 구성과 콘텐츠 출력으로 제한됩니다.

DI 컨트롤러는 내부 세부 정보가 로그로 노출되지 않도록 예외 메시지나 추적 정보가 아니라 예외 클래스를 로깅합니다. GeneratePdfJob은 큐 전송 과정에서 변조된 직렬화 페이로드를 완화하기 위해 워커에서 출력 경로를 검증합니다. 전체 내용은 /integrations/laravel/security-and-operations/에 있습니다.

주장출처조항reference_id(참조 ID)
바인딩된 식별자가 등록된 항목으로 해석됨PSR-11 Container 명세§1.1.2
연속된 해석은 바인딩 전략에 따라 달라질 수 있음(팩토리 바인딩)PSR-11 Container 명세§1.1.2

PSR-3 로깅 지침은 PSR-3 명세에 설명되어 있습니다. 이 지침은 플레이스홀더 이스케이프가 구현자의 책임이며 호출자는 구조화된 컨텍스트를 전달한다고 명시합니다. 문서 psr_3_logger §1.2를 참조하십시오.

nextpdf/premium을 통한 서명된 PAdES B-B 출력과 PDF/A 보관은 동일한 DI 표면을 사용합니다. 이는 선택적인 Enterprise 기능입니다. 여기에 문서화된 Core 패키지는 이를 도입하는 데 코드 변경이 필요하지 않습니다. https://nextpdf.dev/get-license/?intent=laravel-signing을 참조하십시오.

  • /integrations/laravel/quickstart/ — 최소 첫 예제
  • /integrations/laravel/configuration/ — 큐, 서명, 글꼴 키
  • /integrations/laravel/security-and-operations/ — 위협 모델 및 강화
  • /integrations/laravel/troubleshooting/ — 일반적인 프로덕션 장애