Symfony 개발자 가이드
한눈에 보기
섹션 제목: “한눈에 보기”Symfony 패키지는 서비스 우선 접근 방식을 따릅니다. PdfFactory를 주입하고, 문서별로 create()를 호출하며, 비동기 생성에는 Messenger 빌더를 사용합니다. 호출할 때마다 새 문서가 반환되므로 팩토리는 컨테이너 서비스로 둘 수 있습니다.
컨트롤러, 서비스, Messenger 핸들러 또는 nextpdf/symfony를 둘러싼 번들 수준 확장 지점을 설계할 때 이 가이드를 참고하십시오.
아키텍처 경계
섹션 제목: “아키텍처 경계”| 계층 | 소유 주체 | 책임 | 여기에 두지 말 것 |
|---|---|---|---|
| 컨트롤러 | 애플리케이션 | 요청을 인가하고 입력을 수집하며, PdfResponse를 반환합니다. | 여러 사용 사례에서 공유되는 PDF 레이아웃. |
| 애플리케이션 서비스 | 애플리케이션 | 도메인 데이터를 로드하고 빌더를 선택합니다. | Symfony 컨테이너 컴파일러 로직. |
| 빌더 서비스 | 애플리케이션 | 동기 또는 큐 기반 문서 생성을 위해 PdfBuilderInterface를 구현합니다. | 요청 객체, 엔티티 매니저 또는 직렬화할 수 없는 컨텍스트. |
| Symfony 번들 | nextpdf/symfony | 서비스, 구성 트리, 선택적 확장 패스, 응답 헬퍼 및 Messenger DTO를 등록합니다. | 테넌트별 저장소 정책. |
| 코어 엔진 | nextpdf/nextpdf | 문서를 작성하고 직렬화합니다. | Symfony 응답 또는 Messenger 동작. |
런타임 수명 주기
섹션 제목: “런타임 수명 주기”| 단계 | 동작 | 개발자 작업 |
|---|---|---|
| 번들 부팅 | NextPdfBundle::build()가 선택적 확장 탐지를 등록합니다. | Symfony가 번들을 자동으로 검색하도록 하거나 bundles.php에 등록합니다. |
| 구성 로드 | NextPdfExtension::load()가 nextpdf: 구성을 처리하고 서비스 정의를 로드합니다. | 구성을 명시적이고 환경을 인식하는 형태로 유지합니다. |
| 팩토리 사용 | PdfFactory::create()가 새로 구성된 문서를 반환합니다. | 서비스에 문서를 저장하지 마십시오. |
| 컨트롤러 출력 | PdfResponse가 완성된 문서를 응답으로 변환합니다. | 헤더를 수동으로 조립하는 대신 헬퍼를 사용합니다. |
| Messenger 디스패치 | GeneratePdfMessage는 빌더 클래스, 출력 경로 및 직렬화 가능한 컨텍스트를 전달합니다. | 컨텍스트를 최소한으로 유지하고 스칼라 친화적으로 만듭니다. |
| 메시지 처리 | GeneratePdfHandler는 서비스 로케이터에서 빌더를 해결하고 문서를 저장합니다. | 빌더를 결정론적이며 멱등하게 만듭니다. |
권장 애플리케이션 구조
섹션 제목: “권장 애플리케이션 구조”| 경로 | 용도 |
|---|---|
src/Pdf/Builder/* | 다음 인터페이스를 구현하는 서비스: PdfBuilderInterface. |
src/Pdf/Data/* | 빌더 컨텍스트로 사용하는 소규모 DTO 또는 배열. |
src/Pdf/Storage/* | 저장소 루트 선택 및 출력 파일 이름 정책. |
src/Controller/* | 동기 응답 진입점. |
tests/Pdf/* | 빌더, 응답, Messenger 및 구성 테스트. |
정적 헬퍼 함수보다 빌더 서비스를 우선 사용합니다. 빌더 서비스는 태그를 지정하고, 데코레이트하고, 테스트하며, Messenger에서 사용하기가 간단합니다.
<?php
namespace App\Pdf\Builder;
use NextPDF\Core\Document;use NextPDF\Symfony\Message\PdfBuilderInterface;
final readonly class InvoicePdfBuilder implements PdfBuilderInterface{ public function build(Document $document, array $context): Document { $document->setTitle((string) $context['title']) ->addPage() ->writeHtml((string) $context['html']);
return $document; }}동기 응답 패턴
섹션 제목: “동기 응답 패턴”<?php
namespace App\Controller;
use App\Pdf\Builder\InvoicePdfBuilder;use NextPDF\Symfony\Http\PdfResponse;use NextPDF\Symfony\Service\PdfFactory;
final readonly class InvoiceController{ public function __invoke( PdfFactory $factory, InvoicePdfBuilder $builder, ) { $document = $builder->build($factory->create(), [ 'title' => 'Invoice 1234', 'html' => '<h1>Invoice 1234</h1>', ]);
return PdfResponse::download($document, 'invoice-1234.pdf'); }}컨트롤러 컨텍스트는 작게 유지합니다. 빌더에 여러 도메인 객체가 필요하다면, 오케스트레이션을 애플리케이션 서비스로 옮기고 DTO 또는 정규화된 배열을 빌더에 전달합니다.
Messenger 패턴
섹션 제목: “Messenger 패턴”GeneratePdfMessage는 디스패치 전에 빌더 클래스와 출력 경로를 검증합니다. 핸들러는 실행 시점에 경로를 다시 검증합니다.
<?php
use App\Pdf\Builder\InvoicePdfBuilder;use NextPDF\Symfony\Message\GeneratePdfMessage;
$bus->dispatch(new GeneratePdfMessage( builderClass: InvoicePdfBuilder::class, outputPath: $projectDir . '/var/pdfs/invoice-1234.pdf', builderContext: [ 'title' => 'Invoice 1234', 'html' => '<h1>Invoice 1234</h1>', ],));Doctrine 엔티티, 열린 스트림, 클로저, 요청 객체 또는 서비스 객체를 builderContext에 두지 마십시오.
확장 지점
섹션 제목: “확장 지점”| 확장 지점 | 사용 목적 | 제약 |
|---|---|---|
PdfFactory 서비스 데코레이션 | 문서가 컨트롤러에 도달하기 전에 애플리케이션 기본값을 적용합니다. | 새 문서 시맨틱을 보존해야 합니다. |
PdfBuilderInterface | 큐 기반 또는 재사용 가능한 문서 빌더를 정의합니다. | 반드시 Document를 반환해야 합니다. |
OptionalExtensionPass | 컴파일 타임에 선택적 Artisan 또는 Premium 기능을 활성화합니다. | 가용성은 요청 상태가 아닌 컨테이너 컴파일 상태로 결정됩니다. |
| Symfony 구성 트리 | 기본값, PDF/A, 렌더러 설정, 서명, TSA, Messenger. | 잘못된 구성은 컨테이너 빌드 중에 실패로 처리되어야 합니다. |
GeneratePdfHandler 서비스 연결 | 큐에 적재된 메시지가 접근할 수 있는 빌더를 제한합니다. | 서비스 로케이터는 승인된 빌더 서비스만 노출해야 합니다. |
개발 워크플로
섹션 제목: “개발 워크플로”- 결정론적 입력을 받는 빌더 서비스를 추가합니다.
- 컨트롤러 또는 서비스에서
PdfFactory::create()를 사용합니다. - 파일 이름, 콘텐츠 유형 및 헤더에 대한 응답 테스트를 추가합니다.
- 동일한 문서를 비동기적으로 생성해야 하는 경우 Messenger에 빌더를 등록합니다.
- 클래스 이름, 출력 경로 및 컨텍스트 형태에 대한 잘못된 메시지 테스트를 추가합니다.
- 최소 구성과 프로덕션 구성으로 컨테이너 컴파일 테스트를 추가합니다.
- 프로덕션과 동일한 PHP 설정에서 렌더링 시간과 메모리를 측정합니다.
실패 처리
섹션 제목: “실패 처리”| 실패 | 처리해야 하는 위치 | 권장 대응 |
|---|---|---|
| 잘못된 구성 | 컨테이너 컴파일. | 트래픽이 앱에 도달하기 전에 배포가 실패하게 합니다. |
| 빌더 서비스 누락 | Messenger 핸들러 테스트 및 서비스 태그. | 메시지를 실패 처리하고 담당 팀에 알립니다. |
| 안전하지 않은 출력 경로 | 메시지 생성자 및 저장소 정책. | 디스패치 전에 거부하고, 핸들러 검증을 심층 방어로 유지합니다. |
| 선택적 확장 사용 불가 | 컴파일러 패스 및 팩토리 동작. | 선택적 기능을 비활성화하거나 설치를 명시적으로 요구합니다. |
| 서비스 변환 또는 렌더링 실패 | 빌더 경계. | 사용 사례에 문서화된 대체 수단이 없는 한 닫힌 상태로 실패하게 합니다. |
안전한 기본값
섹션 제목: “안전한 기본값”| 관심사 | 기본값 | 재정의 시점 |
|---|---|---|
| 팩토리 수명 | 컨테이너 서비스. | 이 설정을 유지하십시오. 팩토리는 문서를 생성하므로 안전합니다. |
| 문서 수명 | 하나의 작업 단위. | 요청이나 메시지 간에 절대 공유하지 마십시오. |
| 출력 경로 검증 | 메시지 생성자 및 핸들러. | 애플리케이션 코드에 테넌트 또는 저장소 루트 제약을 추가합니다. |
| 응답 파일 이름 | document.pdf. | 정제된 비즈니스 식별자로 재정의합니다. |
| Messenger 전송 | async. | PDF 작업이 무거운 경우 전용 전송을 사용합니다. |
테스트 체크리스트
섹션 제목: “테스트 체크리스트”- 컨테이너 테스트는 최소 구성과 프로덕션 구성으로 번들을 컴파일합니다.
- 응답 테스트는 보안 헤더와 파일 이름 처리를 검증합니다.
- Messenger 테스트는 잘못된 경로와 잘못된 빌더 클래스 이름이 디스패치 전에 실패 처리되는지 검증합니다.
- 핸들러 테스트는 실제 빌더 서비스와 임시 출력 디렉터리를 사용합니다.
- 빌더 테스트는 대표 문서를 렌더링하고 프로덕션과 유사한 파일 시스템 권한으로 저장합니다.
- 선택적 확장 테스트는 Artisan 사용 불가, Premium 사용 불가 및 구성된 PDF/A 프로파일 동작을 다룹니다.