NextPDF Laravel 패키지 문제 해결
한눈에 보기
섹션 제목: “한눈에 보기”이 페이지는 패키지에서 관찰되는 각 실패 모드를 소스에서 검증한 근본 원인과 연결합니다. 각 항목에는 증상, 원인, 해결 방법을 명시합니다.
composer require nextpdf/laravelphp artisan vendor:publish --tag=nextpdf-config개념 개요
섹션 제목: “개념 개요”보고된 문제의 대부분은 발견, 컨테이너 해석, 서명, 큐 작업, HTTP 파일 이름이라는 다섯 그룹으로 나뉩니다. 이 패키지는 설계상 실패가 명확하게 드러나도록 합니다. 구성되지 않은 선택적 기능은 null을 반환하고, 안전하지 않은 입력은 형식화된 예외를 발생시킵니다. 따라서 증상은 대개 원인을 직접 가리킵니다.
API 표면 — 증상에서 원인으로
섹션 제목: “API 표면 — 증상에서 원인으로”발견과 부팅
섹션 제목: “발견과 부팅”| 증상 | 검증된 원인 | 해결 방법 |
|---|---|---|
| 설치 후 프로바이더가 등록되지 않음 | 애플리케이션이 extra.laravel.dont-discover로 해당 패키지를 옵트아웃함 | 패키지를 dont-discover에서 제거하거나, NextPdfServiceProvider를 bootstrap/providers.php에 수동으로 등록합니다 |
config('nextpdf')가 비어 있음 | 선언된 바인딩이 해석되지 않아 구성이 병합되지 않음(지연 프로바이더) | 해석해야 할 항목은 provides()에 포함된 항목 중 하나입니다. 발견을 확인하려면 php artisan package:discover --ansi |
config/nextpdf.php가 publish로 생성되지 않음 | publish 태그 불일치 | 정확한 태그를 사용합니다: php artisan vendor:publish --tag=nextpdf-config |
| RuntimeException: “NextPDF requires the ext-mbstring/ext-zlib PHP extension” 발생 | 필수 PHP 확장이 런타임에 누락됨 | 활성화해야 할 확장은 mbstring과 zlib입니다. 설치하거나 php.ini에서 활성화합니다 |
컨테이너 해석
섹션 제목: “컨테이너 해석”| 증상 | 검증된 원인 | 해결 방법 |
|---|---|---|
app(SignerInterface::class)가 null을 반환함 | nextpdf.signature에서 서명이 비활성화되었거나 인증서가 비어 있음 | signature.enabled = true와 유효한 signature.certificate를 설정합니다. 서명자 구상 구현을 사용하려면 nextpdf/premium을 설치합니다 |
app(TsaClient::class)가 반환하는 값이 null | nextpdf.tsa.url이 비어 있음 | 구성해야 할 항목은 tsa.url입니다(필요에 따라 credentials/pins도) |
| PDF/A 버전 유형에 대한 클래스를 찾을 수 없음 | nextpdf.pdfa가 null이 아니지만 nextpdf/premium이 설치되지 않음 | 설치해야 할 패키지는 nextpdf/premium입니다. 또는 pdfa 값을 null로 다시 설정합니다 |
| 전자 인보이스 계약을 해석하는 중 클래스를 찾을 수 없음 | 바인딩은 등록되었지만 Premium 구상 구현이 없음 | 설치해야 할 패키지는 nextpdf/premium입니다. 전자 인보이스 계약은 지연 해석되어 Premium이 없으면 처음 해석할 때만 오류가 발생합니다 |
| 두 논리적 작업에서 동일한 문서가 변경됨 | 문서 바인딩은 팩토리인데, 해석된 인스턴스 하나를 재사용함 | 문서마다 새 PdfDocumentInterface를 해석합니다 |
컨테이너에 항목이 없으면 get()에서 not-found 예외가 발생합니다(PSR-11 §1.1.2). 전자 인보이스 계약은 바인딩되어 있으므로 컨테이너의 has()는 true입니다. 이 오류는 컨테이너 자체가 아니라, 구성 시점에 누락된 Premium 구상 구현 때문에 드러납니다.
큐 작업
섹션 제목: “큐 작업”| 증상 | 검증된 원인 | 해결 방법 |
|---|---|---|
InvalidArgumentException: Path traversal sequences are not allowed | 출력 경로에 .. 세그먼트가 포함됨 | 스토리지 디렉터리 아래에 있는 절대 경로이면서 경로 순회가 없는 경로를 사용합니다 |
InvalidArgumentException: Stream wrappers are not allowed | 경로가 php:// 스킴과 일치함 | 일반 파일 시스템 경로를 사용합니다 |
InvalidArgumentException: Output path contains null bytes | 경로에 \0 바이트가 포함됨 | 디스패치하기 전에 경로를 정제합니다 |
InvalidArgumentException: Output path must end with .pdf extension | 경로가 .pdf로 끝나지 않음(대소문자 구분 없음) | 사용할 접미사는 .pdf(또는 .PDF)입니다 |
| 작업은 실행되지만 파일이 비어 있거나 잘못됨 | 빌더 클로저가 구성된 문서를 반환하지 않음 | 빌더에서 문서를 반환합니다. 반환된 값이 저장됩니다 |
| 작업이 잘못된 큐나 타임아웃을 사용함 | nextpdf.queue.*가 예상대로 설정되지 않음 | 설정해야 할 키는 queue.queue, queue.connection, queue.timeout입니다. tries와 backoff는 서브클래싱이 필요합니다 |
경로 검사는 워커의 handle() 내부에서 실행되므로, 잘못된 경로는 디스패치 시점이 아니라 실행 시점에 실패합니다. 이는 의도된 동작입니다. 큐 전송을 위해 직렬화된 페이로드는 소비되는 지점에서 검증됩니다.
HTTP 응답과 파일 이름
섹션 제목: “HTTP 응답과 파일 이름”| 증상 | 검증된 원인 | 해결 방법 |
|---|---|---|
다운로드 파일 이름이 예상과 달리 document.pdf임 | 빈 파일 이름이 전달되어 팩토리가 기본값을 적용함 | 비어 있지 않은 파일 이름을 전달합니다 |
| 파일 이름에서 경로나 특수 문자가 사라짐 | 파일 이름 정제기가 경로 구분자, 제어 문자, null 바이트를 제거함 | 기본 파일 이름만 전달합니다. 이는 의도된 강화입니다 |
| 일부 클라이언트에서 비 ASCII 파일 이름이 깨져 보임 | 비 ASCII 이름에는 RFC 5987 filename*=가 생성되며, 구형 클라이언트는 ASCII 대체값을 읽습니다 | 의도된 동작입니다. 레거시 클라이언트에서 정확한 일치가 필요하다면 ASCII로 안전한 이름을 제공합니다 |
스트리밍 응답에 Content-Length 헤더가 없음 | 스트리밍 응답은 설계상 Content-Length를 생략합니다(청크 출력) | 의도된 동작입니다. 길이 헤더가 필요하면 스트리밍이 아닌 inline()/download()를 사용합니다 |
코드 예제 — 진단
섹션 제목: “코드 예제 — 진단”# Confirm the provider is discoveredphp artisan package:discover --ansi
# Inspect merged configurationphp artisan tinker --execute="dump(config('nextpdf.queue'));"<?php
declare(strict_types=1);
use NextPDF\Contracts\SignerInterface;
$signer = app(SignerInterface::class);
if ($signer === null) { // Signing not configured, or nextpdf/premium not installed. // Continue without a signature, or fail with a clear message.}엣지 케이스 및 주의 사항
섹션 제목: “엣지 케이스 및 주의 사항”- 지연 프로바이더 때문에 새로 설치한 상태에서는 관련 항목을 처음 해석하기 전까지 “고장 난” 것처럼 보일 수 있습니다. 올바른 성공 신호는
package:discover가 해당 패키지를 나열하는 것입니다. image_cache_mb = null은 50 MB로 대체되며,0만 캐시를 비활성화합니다. “캐시가 비활성화되지 않음”이라는 보고는 대개null을 사용한 경우입니다.signature.level = null은 조용히 PAdES B-B로 대체됩니다. “예상치 못한 B-B” 보고는 대개 레벨을 설정하지 않은 경우입니다.
장기 실행 워커에서 첫 요청이 느리다면, 글꼴 레지스트리가 온디맨드로 구문 분석을 수행하고 있기 때문입니다. nextpdf.preload_fonts를 채워 두면 워커 부팅 시 워밍업이 한 번 실행됩니다. 자세한 내용은 /integrations/laravel/configuration/ 및 /integrations/laravel/boot-and-discovery/를 참고하세요.
보안 참고 사항
섹션 제목: “보안 참고 사항”경로 및 파일 이름 거부는 버그가 아니라 보안 통제입니다. 사전 디코딩하거나 검사를 완화해 이를 우회하지 마세요. 대신 파일 출력을 통제된 스토리지 경로를 통해 라우팅하세요. 자세한 내용은 /integrations/laravel/security-and-operations/를 참고하세요.
적합성
섹션 제목: “적합성”| 주장 | 출처 | 조항 | reference_id (참조 ID) |
|---|---|---|---|
| 컨테이너 항목이 없으면 get()에서 not-found가 발생함 | PSR-11 Container (컨테이너 규격) | §1.1.2 |
함께 보기
섹션 제목: “함께 보기”- /integrations/laravel/install/ — 발견 및 publish 단계
- /integrations/laravel/configuration/ — 모든 키와 기본값
- /integrations/laravel/production-usage/ — DI 및 큐 패턴
- /integrations/laravel/security-and-operations/ — 경로 검사가 존재하는 이유