콘텐츠로 이동

TCPDF 6.x 코드베이스를 NextPDF로 마이그레이션하기

nextpdf/compat-legacy 패키지는 NextPDF\Compat\Tcpdf\TCPDF 어댑터를 통해 NextPDF 코어 엔진 위에서 TCPDF 6.x의 공개 메서드 이름, 매개변수 순서, 기본값을 제공합니다. 마이그레이션은 다음 순서로 진행합니다. 먼저 최소한의 변경으로 엔진 위로 옮기고, 이미 동작하는 부분을 확인한 뒤, 엄격 모드를 켜 동작하지 않는 부분을 나열하고, 호출 지점을 하나씩 수정한 다음, 어댑터를 제거하고 최신 API로 전환합니다. 어댑터는 마이그레이션을 위한 발판일 뿐, 최종 목적지가 아닙니다.

먼저 확인할 전제 조건은 다음과 같습니다.

  • NextPDF 코어와 nextpdf/compat-legacy가 설치되어 있습니다.
  • 테스트 스위트를 갖춘 기존 TCPDF 6.x 코드베이스가 있습니다. 이 테스트 스위트는 아래의 모든 단계에서 안전망 역할을 합니다.

이 문서는 사용 방법 안내서입니다. 개별 TCPDF 호출의 메서드별 동작은 메서드 커버리지 페이지를 참고하세요. 코드까지 포함한 전체 파일별 전략은 업스트림 마이그레이션 페이지를 참고하세요. 두 페이지 모두 함께 보기 섹션에 링크되어 있습니다.

코어와 함께 어댑터를 설치합니다. 아직 실제 TCPDF 라이브러리는 제거하지 마세요. 둘을 모두 유지하면 마이그레이션 중 출력을 비교할 수 있습니다.

Terminal window
composer require nextpdf/compat-legacy

코드를 변경하기 전에 엔진 의존성이 올바르게 해석되고(nextpdf/core ^3.0) 테스트 스위트가 여전히 실행되는지 확인하세요.

어댑터는 호환성 계층이며, TCPDF의 포크도 아니고 바이트 단위로 동일한 복제본도 아닙니다. 조사한 TCPDF 6.x 공개 메서드 약 120개 중 약 94개가 NextPDF\Core\Document 작업에 직접 매핑되며, 문서화된 매개변수 범위에서는 호환되게 동작합니다. 명확히 정의된 일부 메서드는 엔진이 적용하지 않는 레거시 매개변수를 받아들이거나(자동 무시), 아예 출력을 생성하지 않습니다(미구현 또는 해당 없음). 테스트로 검증된 공식 커버리지 매트릭스는 패키지 저장소의 docs/TCPDF_COVERAGE.md에 있습니다. 이 가이드와 해당 매트릭스가 서로 다를 경우에는 매트릭스가 우선합니다.

마이그레이션 전반에서는 다음 두 가지 사실이 중요합니다.

  • 출력 바이트가 다릅니다. 엔진은 독립적인 PDF 2.0 구현이므로, 겉보기 결과가 같아 보여도 렌더링된 바이트는 TCPDF 출력과 다릅니다. PDF 바이트의 정확한 일치를 단언하는 테스트는 렌더링된 콘텐츠나 구조적 속성을 기준으로 기준선을 다시 설정해야 합니다.
  • 엄격 모드는 감사 도구입니다. 엄격 모드가 꺼져 있으면(기본값), TCPDF 동작을 재현할 수 없는 메서드는 조용히 저하됩니다. 엄격 모드가 켜져 있으면, 해당 호출은 무시된 매개변수와 마이그레이션 힌트를 정확히 명시하는 TcpdfNotImplementedException을 던집니다. 엄격 모드는 전용 감사 단계에서만 사용하고, 프로덕션에서는 절대 실행하지 마세요.

어댑터는 getDocument()을 통해 래핑된 엔진 문서도 노출하며, 이는 NextPDF\Core\Document를 반환합니다. 이것이 탈출구입니다. 어댑터를 제거할 수 있을 때까지 호출 지점을 하나씩 최신 API로 마이그레이션하세요.

관심사표면
생성new NextPDF\Compat\Tcpdf\TCPDF('P', 'mm', 'A4')
선택적 전역 별칭NextPDF\Compat\Tcpdf\LegacyBootstrap::enableAliases()
감사 활성화TCPDF::setStrictMode(true)
감사 예외NextPDF\Compat\Tcpdf\Exception\TcpdfNotImplementedException
최신 API로의 탈출구TCPDF::getDocument(): NextPDF\Core\Document
출력TCPDF::Output(string $name, string $dest)S, F, E, I, D

LegacyBootstrap::enableAliases()는 멱등적입니다. 즉, 해당 클래스가 아직 없을 때만 \TCPDF, \TCPDF_STATIC, \TCPDF_FONTS, \TCPDF_COLORS, \TCPDF_IMAGES를 등록합니다. 메서드별 전체 커버리지와 출력 대상 동작은 함께 보기에 링크된 메서드 커버리지 및 빠른 시작 페이지에서 확인할 수 있습니다.

여기서는 import만 변경하고 TCPDF 스타일 호출은 그대로 유지한 채 PDF를 생성합니다.

quickstart-first.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF('P', 'mm', 'A4');
$pdf->SetCreator('Quickstart');
$pdf->SetTitle('First Document');
$pdf->SetFont('helvetica', '', 12);
$pdf->AddPage();
$pdf->Cell(0, 10, 'Hello from the NextPDF engine', 1, 1, 'C');
$pdf->Output(__DIR__ . '/quickstart.pdf', 'F');

Output($name, 'F')는 파일을 쓰고 빈 문자열을 반환합니다. 레거시 TCPDF와 달리, 어댑터의 Output()는 활성 출력 버퍼에 내용을 내보내지 않으므로 자체 응답을 제어하는 큐 워커나 HTTP 핸들러 내부에서 안전하게 호출할 수 있습니다.

전역 네임스페이스에서 new \TCPDF(...)를 생성하는 호출 지점을 수정할 수 없는 경우에는, 부팅 시 선택적 별칭을 한 번 활성화하세요.

quickstart-alias.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\LegacyBootstrap;
LegacyBootstrap::enableAliases();
// Legacy code now resolves \TCPDF to the adapter:
$pdf = new \TCPDF('P', 'mm', 'A4');
$pdf->AddPage();
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, 'Legacy call site, modern engine');
$pdf->Output(__DIR__ . '/aliased.pdf', 'F');

실제 TCPDF 라이브러리가 여전히 오토로드될 수 있는 상태에서는 별칭을 활성화하지 마세요. \TCPDF 클래스가 이미 존재하면 별칭은 건너뛰므로, 의도치 않게 레거시 TCPDF를 계속 사용하게 될 수 있습니다. 마이그레이션 중에는 파일별 import를 사용하는 것이 좋습니다.

마이그레이션에서 안전하게 진행할 수 있는 단계는 엄격 모드 감사입니다. 엄격 모드를 켠 상태로 대표적인 프로덕션 경로 또는 테스트 스위트를 실행하고 모든 TcpdfNotImplementedException을 수집하세요. 각각은 하나의 작업 항목입니다. 예외는 메서드, 무시된 매개변수, 힌트를 명시합니다.

migration-audit.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\Exception\TcpdfNotImplementedException;
use NextPDF\Compat\Tcpdf\TCPDF;
function renderInvoice(TCPDF $pdf): void
{
// ... your existing rendering code, unchanged ...
}
$pdf = new TCPDF('P', 'mm', 'A4');
$pdf->setStrictMode(true);
try {
renderInvoice($pdf);
$pdf->Output(__DIR__ . '/audit.pdf', 'F');
} catch (TcpdfNotImplementedException $exception) {
// Each message names the method, the ignored parameters, and a hint.
fwrite(STDERR, 'MIGRATION GAP: ' . $exception->getMessage() . "\n");
}

각 차이에 대해 비용이 가장 낮은 올바른 수정 방법을 선택하세요. 실제로 의존하지 않았던 매개변수를 제거하거나, getDocument()를 통해 최신 API로 의도를 다시 표현하세요. 탈출구를 사용하면 TCPDF 표면으로 표현할 수 없는 모든 작업을 처리할 수 있습니다.

migration-escape-hatch.php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF();
$pdf->AddPage();
// Legacy path stays for the parts that already work:
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, 'Header line', 0, 1);
// Modern path for what the TCPDF surface cannot express here —
// for example a clickable image (the legacy Image() link parameter
// is one of the silently ignored parameters):
$document = $pdf->getDocument();
$document->image('logo.png', 10, 30, 40, 0);
$document->link(10, 30, 40, 20, 'https://example.com');

엄격 모드를 전용 CI 작업으로 실행한 다음, 끄고 감사가 끝난 코드 경로를 배포하세요. 리팩터링 중 회귀를 잡아낼 수 있도록 주기적인 엄격 모드 CI 작업을 유지하세요.

  • MultiCell()1을 반환하고, Write()0을 반환합니다. 이는 계산 결과가 아니라 호환성을 위한 자리표시자입니다. 그 반환값을 기준으로 분기하는 코드가 있다면 모두 조정하세요.
  • Error()die()를 호출하는 대신 예외를 던집니다. 어댑터는 RuntimeException을 발생시킵니다. 프로세스 종료에 의존하던 코드는 예외를 잡아야 합니다.
  • 자동으로 무시되는 매개변수. Image(), writeHTML(), SetProtection(), Bookmark()과 같은 메서드는 무시되는 레거시 매개변수를 받아들입니다. 이를 찾으려면 엄격 모드를 사용하세요. 클릭 가능한 이미지를 만들려면 이미지를 그린 다음, 같은 사각형 영역 위에 Document::link()를 추가하세요.
  • 구현되지 않은 메서드. setSignature(), addEmptySignatureAppearance(), endPage()은 엄격 모드에서 예외를 던지는 no-op이며, Open()은 절대 예외를 던지지 않는 안전한 no-op입니다. endPage()Open()을 제거하세요. 서명에는 최신 서명 API를 통한 상용 NextPDF 에디션이 필요합니다.
  • PDF 버전은 고정되어 있습니다. setPDFVersion()은 더 낮은 레거시 PDF 버전을 대상으로 지정할 수 없으며, 출력은 항상 PDF 2.0입니다. setUserRights()은 PDF 2.0에서 더 이상 사용되지 않으며, 알림을 남기고 무시됩니다.
  • 별칭 충돌. tecnickcom/tcpdf를 제거한 후에도 여전히 실제 TCPDF 클래스로 해석되는 호출이 있다면, 별칭 주의 사항이 적용된 것이므로 해당 호출 지점에서 어댑터를 명시적으로 import하세요.

어댑터는 엔진에 위임하며, 문서 빌드 비용은 어댑터 계층이 아니라 콘텐츠에 비례합니다. 어댑터의 Output()은 출력 버퍼에 쓰지 않으므로 큐 워커 내부에서 안전합니다. 모든 NextPDF 생성 작업과 마찬가지로, 무거운 TCPDF 스타일 생성은 요청 스레드에서 분리하세요. 바이트 수준 테스트의 기준선을 렌더링된 콘텐츠 기준으로 다시 잡는 작업은 일회성 비용이며, 향후 엔진 업그레이드 후에도 유지되는 테스트를 만들어 줍니다.

  • 암호화. SetProtection()은 레거시 modepubkeys 매개변수를 무시하며, 엔진은 표준 핸들러에 AES-256을 사용합니다. 인증서 기반 암호화의 경우, 레거시 매개변수가 아니라 어댑터에 노출된 최신 공개 키 암호화 진입점을 사용하세요.
  • 서명은 제한되어 있습니다. 기본 서명 지원은 인증서 값 객체와 함께 최신 서명 API를 통해 접근하는 상용 에디션 기능이며, 레거시 setSignature()은 no-op입니다. 이 가이드는 어떤 에디션에 대해서도 장기 검증 또는 타임스탬프 서명 프로필에 관해 어떠한 주장도 하지 않습니다.
  • 감사 중에는 명시적으로 실패하세요. 엄격 모드는 조용한 매개변수 손실을 드러내므로, 호출자는 자신의 의도가 반영되지 않았음을 알 수 있습니다. 수집된 예외는 프로덕션 동작이 아니라 마이그레이션 작업 목록으로 취급하세요.
  • catch 블록을 절대 작성하지 마세요. 감사 예제는 TcpdfNotImplementedException을 잡아 정의된 작업 항목 라인을 기록합니다.

마이그레이션 중 전체 암호화 및 서명 태세는 compat-legacy 보안 및 운영 페이지에서 확인할 수 있습니다.

이 가이드는 그 자체로 어떠한 규범적 표준 주장도 하지 않습니다. 어댑터는 PDF 2.0(ISO 32000-2) 출력을 작성하며, 더 낮은 이전 버전을 대상으로 지정할 수 없습니다. 해당 동작과 관련 조항은 업스트림 메서드 커버리지 페이지에 고정되어 있으며, 그 페이지에는 엄격 모드의 기반이 되는 OWASP 명시적 실패 원칙과 커버리지 감사의 ISO/IEC 25023 기능 완전성 관점도 기록되어 있습니다. 이 쿡북 페이지는 사용법만 다시 설명하고, 해당 인용은 그 페이지에 맡깁니다.