콘텐츠로 이동

TCPDF 6.x에서 NextPDF로 마이그레이션

마이그레이션은 명확한 순서대로 진행합니다. 먼저 가능한 한 작은 변경으로 NextPDF 엔진으로 전환합니다. 이미 동작하는 부분을 입증하고, 동작하지 않는 부분을 점검합니다. 호출 지점마다 하나씩 수정합니다. 그런 다음 어댑터를 제거합니다. 호환성 계층은 2 단계부터 4 단계까지를 지원하지만, 최종 목적지는 아닙니다.

이 페이지에서는 전략을 다룹니다. 개별 메서드의 정확한 동작은 /integrations/tcpdf-compat/method-coverage/와 저장소의 권위 있는 매트릭스인 docs/TCPDF_COVERAGE.md를 함께 확인하십시오.

TCPDF 6.x codebase

Swap dependency: install compat-legacy

Run existing suite unchanged

Strict-mode audit: enumerate behavioral gaps

Fix call sites: drop ignored params or move to modern API

Re-baseline byte-level test assertions

Remove the TCPDF dependency

Incrementally retire the adapter onto Document

Diagram

애플리케이션은 모든 단계에서 출시 가능한 상태를 유지합니다. 모든 것을 한 번에 전환하는 일괄 컷오버는 전혀 필요하지 않습니다.

nextpdf/compat-legacy를 설치합니다(/integrations/tcpdf-compat/install/ 참조). 아직 tecnickcom/tcpdf를 제거하지 마십시오 — 둘을 모두 유지하면 비교 테스트를 실행할 수 있습니다.

레거시 호출 지점에서 클래스를 어떻게 해석할지 선택합니다:

  • 권장: 각 파일에서 use/requireuse NextPDF\Compat\Tcpdf\TCPDF;로 변경합니다. 명시적이며 grep으로 찾기도 쉽습니다.
  • 아직 호출 지점을 건드릴 수 없을 때: 부팅 시 한 번 LegacyBootstrap::enableAliases()로 옵트인 전역 별칭을 활성화합니다(/integrations/tcpdf-compat/boot-and-discovery/ 참조). 이렇게 하면 \TCPDF와 네 개의 헬퍼 클래스가 어댑터로 해석됩니다.

두 전략은 실무에서 상호 배타적입니다. 실제 TCPDF 라이브러리가 여전히 오토로드 가능하고 전역 별칭을 활성화하면, \TCPDF 클래스가 이미 존재할 때 별칭이 건너뛰어집니다. 그러면 의도치 않게 레거시 TCPDF를 계속 사용하게 될 수 있습니다. 1 단계 동안에는 파일별 임포트를 선호하여 각 호출 지점이 정확히 어떤 클래스를 사용하는지 명확히 하십시오. 참조: /integrations/tcpdf-compat/troubleshooting/.

2 단계 — 기존 스위트를 변경 없이 실행

섹션 제목: “2 단계 — 기존 스위트를 변경 없이 실행”

다른 코드 변경 없이 어댑터를 대상으로 전체 테스트 스위트를 실행합니다. 위임된 메서드 대부분(조사한 약 120 개 중 94 개)은 호환 방식으로 동작합니다. 다음 두 가지 유형의 예측 가능한 실패가 예상됩니다:

  1. 바이트 수준 단언. 정확한 PDF 바이트를 비교하는 테스트는 엔진이 별도의 구현이므로 실패합니다. 이는 결함이 아니라 예상되는 결과입니다. 이러한 테스트는 4 단계로 미룹니다.
  2. 반환값 분기. 일부 메서드는 계산된 값 대신 호환성 플레이스홀더를 반환합니다 — 가장 대표적으로 MultiCell()1을 반환하고, Write()0을 반환합니다. 이러한 반환값에 따라 분기하는 코드는 조정이 필요합니다.

모든 실패를 목록화합니다. 각 실패를 byte-baseline, return-value, 또는 진짜 동작 격차 중 하나로 분류합니다.

이 단계는 마이그레이션을 안전하게 만드는 핵심입니다. 엄격 모드를 활성화한 상태로 스위트(또는 대표적인 프로덕션 경로)를 실행합니다:

examples/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 $e) {
// Each message names the method, the ignored parameters, and a hint.
fwrite(STDERR, 'MIGRATION GAP: ' . $e->getMessage() . "\n");
}

모든 TcpdfNotImplementedException은 하나의 작업 항목입니다. 메시지에는 메서드, 무시된 매개변수의 정확한 목록, 그리고 마이그레이션 힌트가 담겨 있습니다. 예외를 던지는 메서드 집합은 tests/Unit/Compat/Tcpdf/TcpdfStrictModeTest.php에 나열되어 있으며 테스트로 단언됩니다. 각각의 근거는 docs/TCPDF_COVERAGE.md에 있습니다.

엄격 모드는 프로덕션이 아니라 전용 CI 작업으로 실행하십시오. 핵심은 격차를 드러내는 것이지, 프로덕션에서 예외가 발생하게 하는 것이 아닙니다.

각 격차에 대해 비용이 가장 낮은 올바른 수정 방법을 선택합니다:

격차 패턴수정
무시된 매개변수가 중요하지 않은 경우(e.g. 의존한 적 없는 TCPDF $align)매개변수를 제거합니다. 호출은 그대로 호환됩니다.
무시된 매개변수가 중요했던 경우(e.g. 클릭 가능한 Image() 링크)최신 API로 다시 표현합니다. 이미지를 그린 다음, 해당 사각형 위에 Document::link()를 추가합니다.
메서드가 구현되지 않은 경우(setSignature(), endPage())endPage() / Open(): 호출을 제거합니다. 서명: /integrations/tcpdf-compat/security-and-operations/ 참조 — 상용 에디션이 필요합니다.
해당 없는 메서드(setPDFVersion(), setUserRights())제거합니다. 출력은 항상 PDF 2.0 입니다. user-rights는 PDF 2.0 에서 더 이상 사용되지 않습니다.
반환값 분기값을 직접 계산하거나, 해당 로직을 최신 API로 옮깁니다.

TCPDF API 표면으로 표현할 수 없는 항목에는 모두 이스케이프 해치를 사용합니다:

examples/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 as-is for the parts that work:
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, 'Header line', 0, 1);
// Modern path for what the TCPDF surface cannot express here:
$document = $pdf->getDocument();
$document->image('logo.png', 10, 30, 40, 0);
$document->link(10, 30, 40, 20, 'https://example.com');

정확한 바이트 단언은 실제로 중요한 항목에 대한 단언으로 바꿉니다:

  • 출력이 %PDF로 시작하며 파싱됩니다(스모크 수준).
  • 렌더링된 텍스트 콘텐츠가 존재합니다(텍스트를 추출해 단언).
  • 구조적 속성(페이지 수, 페이지 크기, 아웃라인 존재 여부)이 일치합니다.

이는 일회성 비용이며, 향후 엔진 업그레이드 후에도 유지되는 테스트를 만들어 줍니다.

엄격 모드 점검이 통과하고, 프로덕션에서는 엄격 모드를 상태로 유지하며, 재기준화된 단언으로 스위트도 통과하면, tecnickcom/tcpdf를 제거합니다:

Terminal window
composer remove tecnickcom/tcpdf

스위트를 다시 실행합니다. 여전히 실제 TCPDF 클래스로 해석되는 호출이 있다면 1 단계의 별칭 주의사항이 적용된 것입니다 — 남은 호출 지점이 어댑터를 명시적으로 임포트하도록 수정하십시오.

어댑터는 마이그레이션 보조 도구이지, 영구 계층이 아닙니다. TCPDF가 사라지고 엔진 동작이 입증되면, 어댑터를 점진적으로 폐기합니다:

  1. 각 모듈에서 new TCPDF(...)를 최신 NextPDF\Core\Document 생성으로 교체합니다.
  2. TCPDF 메서드 호출을 동등한 최신 API로 교체합니다(4 단계에서 이미 추가한 getDocument() 호출이 그 예입니다).
  3. 모듈이 더 이상 어댑터를 참조하지 않으면, 해당 호환성 임포트를 삭제합니다.
  4. 어떤 모듈도 어댑터를 참조하지 않으면, composer.json에서 nextpdf/compat-legacy를 제거합니다.

이렇게 하면 호환성 계층 없이 최신 PDF 2.0 API 위에서 동작하게 됩니다.

  • nextpdf/compat-legacy 설치됨; 엔진 연결 확인됨.
  • 호출 지점이 어댑터를 명시적으로 임포트함(또는 별칭을 활성화하고 오토로드 경로에서 실제 TCPDF를 제거함).
  • 어댑터를 대상으로 전체 스위트를 실행함; 실패를 분류함.
  • 엄격 모드 CI 작업을 추가함; 모든 격차를 목록화함.
  • 각 격차를 수정함(매개변수 제거 / 최신 API / 호출 제거).
  • 바이트 수준 단언을 content/structure로 재기준화함.
  • tecnickcom/tcpdf 제거됨; 스위트 통과.
  • 어댑터를 모듈 단위로 폐기함; 의존성 제거됨.
  • /integrations/tcpdf-compat/method-coverage/ — 메서드별 동작 및 대체 안내
  • docs/TCPDF_COVERAGE.md — 권위 있고 테스트로 검증된 매트릭스
  • /integrations/tcpdf-compat/configuration/ — 구성을 전역 상수에서 옮기기
  • /integrations/tcpdf-compat/security-and-operations/ — 마이그레이션 중 암호화 및 서명
  • /integrations/tcpdf-compat/troubleshooting/ — alias/real-TCPDF 충돌 및 기타 함정