콘텐츠로 이동
getnextpdf.com

안정성: 베타

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

마이그레이션은 명확한 순서대로 진행합니다. 먼저 가능한 한 작은 변경으로 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/require를 use 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 충돌 및 기타 함정