콘텐츠로 이동

CodeIgniter 4 프로덕션 사용

프로덕션 컨트롤러는 구체적인 NextPDF 서비스에 의존합니다. 컨트롤러는 문서화된 예외 계층 구조를 명시적으로 처리하고 관측성 신호를 내보냅니다. 오래 실행되는 PDF 작업은 CodeIgniter 4 Queue를 통해 요청 처리에서 분리됩니다.

CodeIgniter 4는 로케이터를 통해 패키지의 서비스를 확인합니다. 서비스 로케이터 패턴은 객체가 스스로 의존성을 가져올 수 있도록 컨테이너를 객체에 넘기는 방식입니다. 이 패턴은 권장되지 않습니다(PSR-11 §1.3, SHOULD NOT 표현). 이 지침을 따르려면 각 NextPDF 서비스를 컨트롤러 경계에서 한 번만 확인하고, 내부에는 구체적인 객체를 전달하십시오. Services 클래스나 컨테이너를 도메인 코드에 전달하지 마십시오.

모든 PHP 샘플에서는 declare(strict_types=1);를 별도 줄에 선언합니다(PSR-12 §x1.x3.p34).

프로덕션 관심사검증된 표면
서비스 확인Services::pdf(false), Services::pdfDocument(false), Services::documentFactory()
응답 빌드PdfResponse::download() / inline()DownloadResponse
실패 포착NextPDF\Exception\NextPdfException (에코시스템의 기본 형식)
비동기 생성GeneratePdfJob을 등록하는 Config\Queue::$jobHandlers
경로 / 콜러블 가드GeneratePdfJob이 발생시키는 InvalidArgumentException

프로덕션 컨트롤러 — 오류 처리 및 관측성

섹션 제목: “프로덕션 컨트롤러 — 오류 처리 및 관측성”

코어 엔진은 모두 NextPDF\Exception\NextPdfException을 확장하는 예외를 발생시킵니다. 이 단일 형식만 포착하면 코어와 확장 기능의 실패를 모두 처리할 수 있습니다. 여기서 catch 블록은 컨텍스트와 함께 로그를 기록하고 정의된 오류 응답을 반환하며, 결코 빈 catch가 아닙니다.

<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\DownloadResponse;
use CodeIgniter\HTTP\ResponseInterface;
use NextPDF\CodeIgniter\Config\Services;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final class InvoiceController extends BaseController
{
public function download(int $id): DownloadResponse|ResponseInterface
{
/** @var LoggerInterface $logger */
$logger = \service('logger');
$start = \hrtime(true);
try {
$pdf = Services::pdf(false);
$pdf->document()->addPage();
$pdf->document()->cell(0, 10, "Invoice #{$id}");
$response = $pdf->download("invoice-{$id}.pdf");
$logger->info('pdf.invoice.generated', [
'invoice_id' => $id,
'elapsed_ms' => (\hrtime(true) - $start) / 1_000_000,
]);
return $response;
} catch (NextPdfException $e) {
$logger->error('pdf.invoice.failed', [
'invoice_id' => $id,
'exception' => $e::class,
'message' => $e->getMessage(),
]);
return $this->response
->setStatusCode(ResponseInterface::HTTP_INTERNAL_SERVER_ERROR)
->setJSON(['error' => 'pdf_generation_failed', 'invoice_id' => $id]);
}
}
}

Services::pdf(false)는 호출할 때마다 새 라이브러리와 새 기반 문서를 반환합니다. 따라서 동시 요청은 결코 문서 상태를 공유하지 않습니다. 패키지 기능 테스트가 이 동작을 검증합니다.

글꼴 및 이미지 레지스트리는 설계상 프로세스 수명 동안 유지되는 싱글턴입니다. 글꼴 레지스트리는 한 번 워밍업된 뒤 잠깁니다. 이미지 레지스트리는 크기가 제한된 LRU(least-recently-used) 캐시입니다. 이는 수명이 긴 워커(CodeIgniter spark 서버, RoadRunner 방식 러너 또는 큐 워커)에서 의도된 동작입니다. 비용이 큰 레지스트리는 유지되고, 모든 문서는 새로 생성됩니다. 요청 코드나 작업 코드에서 공유 문서(Services::pdfDocument(true))를 요청하지 마십시오. 이 옵션은 테스트 리셋 전용이며 요청 간에 콘텐츠를 공유하게 됩니다.

CodeIgniter Queue를 사용한 비동기 생성

섹션 제목: “CodeIgniter Queue를 사용한 비동기 생성”

GeneratePdfJobcodeigniter4/queue를 통해 요청 처리와 분리된 상태에서 PDF 생성을 실행합니다. 큐 런타임은 두 가지 규칙을 강제하며, 두 가지 모두 올바르게 구성해야 합니다.

큐는 클래스 문자열이 아니라 이름 키로 작업을 식별합니다. 큐 핸들러는 푸시된 작업 이름을 Config\Queue::$jobHandlers의 키와 대조하여 검증합니다. 알 수 없는 이름은 CodeIgniter\Queue\Exceptions\QueueException으로 거부됩니다. app/Config/Queue.php에 작업을 등록하십시오.

<?php
declare(strict_types=1);
namespace Config;
use CodeIgniter\Queue\Config\Queue as BaseQueue;
use NextPDF\CodeIgniter\Jobs\GeneratePdfJob;
final class Queue extends BaseQueue
{
/** @var array<string, class-string> */
public array $jobHandlers = [
'generate-pdf' => GeneratePdfJob::class,
];
}

등록된 이름을 두 번째 인수에 넣어 작업을 푸시하십시오. 첫 번째 인수는 큐 이름이고, 세 번째 인수는 작업 데이터 배열입니다.

<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\HTTP\ResponseInterface;
final class InvoiceController extends BaseController
{
public function queueInvoice(int $id): ResponseInterface
{
\service('queue')->push('pdf-queue', 'generate-pdf', [
'builder' => 'App\\PdfBuilders\\InvoiceBuilder::build',
'outputPath' => WRITEPATH . 'pdfs/invoice-' . $id . '.pdf',
'context' => ['invoice_id' => $id],
]);
return $this->response
->setStatusCode(ResponseInterface::HTTP_ACCEPTED)
->setJSON(['status' => 'queued', 'invoice_id' => $id]);
}
}

3. App\PdfBuilders 아래에 빌더 구현

섹션 제목: “3. App\PdfBuilders 아래에 빌더 구현”

이 작업은 빌더 콜러블을 App\PdfBuilders 네임스페이스로 제한하고, 출력 경로를 WRITEPATH/pdfs/로 한정합니다. 빌더는 정적 메서드이며, 새 Document와 컨텍스트 배열을 받아 해당 문서를 반환합니다.

<?php
declare(strict_types=1);
namespace App\PdfBuilders;
use NextPDF\Core\Document;
final class InvoiceBuilder
{
/** @param array<string, mixed> $context */
public static function build(Document $document, array $context): Document
{
$invoiceId = (int) ($context['invoice_id'] ?? 0);
$document->addPage();
$document->cell(0, 10, "Invoice #{$invoiceId}");
return $document;
}
}
Terminal window
php spark queue:work pdf-queue

각 작업 실행은 Services::pdfDocument()를 통해 새 문서로 시작합니다. 그런 다음 빌더를 적용하고 검증된 경로에 저장합니다. 패키지 테스트는 연속된 두 번의 작업 실행이 문서 상태를 공유하지 않음을 검증합니다.

  • 큐는 푸시 시점에 작업 이름으로 사용된 GeneratePdfJob::class를 거부합니다. 등록된 키 'generate-pdf'가 아니기 때문입니다. 항상 jobHandlers 키를 푸시하십시오.
  • 빌더 문자열은 App\PdfBuilders\<Class>::<method>와 정확히 일치해야 합니다. 함수, 다른 네임스페이스 또는 prefixed/suffixed 페이로드는 어떤 코드가 실행되기 전에 InvalidArgumentException을 발생시킵니다.
  • 출력 경로는 WRITEPATH/pdfs/ 내부 경로로 해석되어야 하며 .pdf로 끝나야 합니다(대소문자 구분 없음). 경로 탐색 및 형제 접두사 경로는 거부됩니다.
  • codeigniter4/queue는 패키지의 개발 전용 의존성입니다. 워커를 실행하는 애플리케이션에서는 이를 require 하십시오.

레지스트리는 워커 프로세스당 한 번만 생성됩니다. 문서 빌드 비용은 어댑터가 아니라 콘텐츠에 따라 증가합니다. 대규모 배치 작업에서는 요청 워커가 응답성을 유지하도록 큐 경로를 우선 사용하십시오. 측정 가능한 목표가 있는 모든 레시피에는 performance_budget를 설정하십시오.

큐 작업은 위험도가 가장 높은 표면입니다. 공격자가 브로커에 접근할 수 있으면 큐 페이로드에 영향을 줄 수 있습니다. 콜러블 허용 목록과 경로 한정은 검증된 거부 사례와 함께 /integrations/codeigniter/security-and-operations/에서 다룹니다.

  • 컨트롤러는 PSR-11 §1.3 서비스 로케이터 지침에 따라 컨테이너가 아닌 구체적인 서비스를 받습니다.

NextPDF 코어는 Apache-2.0입니다. 큐 작업에서 서명된 출력 및 PDF/A 출력을 사용하려면 워커 환경에 NextPDF Pro 또는 Enterprise가 설치되어 있어야 합니다. CodeIgniter 패키지는 해당 서비스 메서드를 노출합니다. 해당 Premium 패키지가 설치되기 전까지는 null을 반환합니다. 다음을 참조하십시오: </get-license/?intent=codeigniter-async-signing>.

  • /integrations/codeigniter/quickstart/ — 이 컨트롤러의 최소 버전.
  • /integrations/codeigniter/configuration/ — 서명, TSA, 경로 구성.
  • /integrations/codeigniter/security-and-operations/ — 큐 위협 모델 및 강화.
  • /integrations/codeigniter/troubleshooting/ — 큐 및 디스커버리 실패 모드.
  • /integrations/codeigniter/integration/ — 연동 레퍼런스 및 스모크 테스트.