콘텐츠로 이동

NextPDF 디자인 철학

Spec: ISO/IEC 25010 Spec: ISO 32000-2 Evidence: Design principle

이 페이지는 모든 NextPDF API 결정이 따라야 하는 원칙을 명시합니다. 원칙은 의도적으로 적게 두었습니다. 외워서 말할 수 없는 원칙은 압박 속에서 적용할 수 없기 때문입니다.

가장 먼저 읽어야 할 페이지입니다. 다른 Insider_ 페이지들은 이 원칙들이 구체적인 지점에서 어떻게 작동하는지 보여 줍니다. 이 페이지는 그 원칙들에 이름을 붙여 나머지 페이지들이 의미를 갖게 합니다.

PDF는 명확한 입장을 가질 만큼 오래되었고, 추측을 그냥 넘기지 않을 만큼 엄격합니다. 서명은 자신이 포함하는 바이트만 정확히 포함합니다. 폰트는 임베드되었거나 임베드되지 않았거나, 둘 중 하나입니다. 보존용 프로파일은 유효하거나, 그렇지 않으면 몇 달 뒤 달가워하지 않을 누군가 앞에서 감사를 통과하지 못합니다.

입력이 모호할 때 라이브러리에는 선택지가 있습니다. 추측하고 조용히 넘어갈 수도 있고, 멈추고 그 사실을 알릴 수도 있습니다. 첫 번째 방식은 데모에서는 더 친절해 보입니다. 그 대가로 무엇이 잘못되었는지 흔적도 없는 운영 장애를 떠안게 됩니다. NextPDF는 두 번째 방식을 선택합니다. 변호할 수 있는 결과를 얻기 위해 덜 편안한 첫인상을 받아들입니다. 소프트웨어 품질 표준은 이 절충을 직접적으로 명명합니다. 페일세이프(fail-safe) 동작은 제품이 실패할 때 정의되지 않은 상태로 계속 진행하지 않고 안전한 상태로 되돌아가는 능력입니다( Spec: ISO/IEC 25010, §3 ).

NextPDF는 우선순위에 따라 다섯 가지 원칙 위에 세워져 있습니다.

  1. 명시적인 것이 암묵적인 것을 이깁니다. 의도가 중요하다면, 그것을 명시합니다. 엔진은 서명 수준, 출력 모드, 적합성 대상을 문맥에서 추론하지 않습니다.
  2. 빠르게 실패하고, 명확하게 실패하고, 일찍 실패합니다. 유효하지 않은 입력은 단 한 바이트가 기록되기 전에, 원인을 명시하는 메시지와 함께 거부됩니다.
  3. 오류는 API 표면입니다. 실패는 구체적이고, 타입이 지정되어 있으며, 구조화된 컨텍스트를 담습니다. 우연히 생긴 것이 아니라 설계된 것입니다.
  4. 경계는 발견되는 것이 아니라 명시되는 것입니다. 모든 주장은 어디에서 멈추는지 밝힙니다. “필요하지만 충분하지는 않다”는 NextPDF가 의도적으로 명시하는 표현입니다.
  5. 아무것도 조용히 저하된 상태로 넘어가지 않습니다. 엔진은 완성된 것처럼 보이지만 절반만 올바른 산출물을 반환하지 않습니다.

그 밖의 모든 것 — 플루언트 빌더, 일회용 문서, 엄격한 타이핑 —은 이 원칙들에서 파생됩니다.

이 원칙들은 포스터가 아닙니다. 소스 코드에서 구체적인 형태로 드러나며, 서로를 강화합니다.

아래 표는 각 원칙을 엔진의 어느 부분에서 볼 수 있는지, 그리고 그 원칙이 없을 때 어떤 대가를 치르는지와 대응시킵니다.

원칙NextPDF에서 나타나는 방식반대로 할 때의 대가
명시적인 것이 암묵적인 것을 이긴다setSignature(certInfo:, level:)는 PAdES 수준을 필수 명명 인자로 받습니다 — “auto” 수준은 없습니다의무가 요구하지 않은 프로파일로 서명된 문서를 검증 시점에서야 발견하게 됨
빠르게 실패하고, 명확하게 실패한다save()는 렌더링 전에 스트림 래퍼 경로나 널바이트 경로를 거부합니다. setSignature() 뒤에 save()를 호출하면 서명되지 않은 파일을 내보내는 대신 예외를 던집니다경로 탐색을 통한 쓰기, 또는 아카이브에 “서명되지 않았지만 서명되었다고 믿는” PDF
오류는 API 표면이다하나의 추상 기반 예외와 구체적으로 타입이 지정된 하위 클래스들이 있으며, 각각 로그와 APM을 위해 구조화된 getContext()를 노출합니다막연한 스택 트레이스와 추측으로 허비하는 긴 오후
경계는 명시된다프로세스 내 적합성 검사는 발견 사항을 반환하며, 최종 판정은 독립적인 검증기의 몫이라고 명시합니다감사관이 반증하는 “예외가 없으니 적합한 것이 틀림없다”는 결론
아무것도 조용히 저하된 상태로 넘어가지 않는다보존용 타임스탬프 경로는 필수 딕셔너리가 빠진 프로파일을 내보내는 대신, 절반만 작성된 프로파일을 반환하기를 거부합니다장기 검증 프로파일처럼 보이지만 조용히 그렇지 않은 결과물

이 원칙들을 함께 읽으면 하나의 입장이 드러납니다. 엔진은 자신만만한 “아마도”보다 정직한 “아니오”를 제공하려 합니다. 이는 비관주의가 아닙니다. PDF가 흔히 법적 산출물이라는 인식입니다. 잘못된 법적 산출물은 아예 만들어지지 않은 것보다 더 나쁩니다.

이 페이지는 Evidence: Design principle 성격의 진술입니다. 이 원칙들은 의도적인 결정으로, 측정된 것이 아니라 논증된 것입니다. 어떤 원칙이 외부 분야에서 이미 이름 붙여진 경우, 이 페이지는 그 추론이 단지 사내 의견에 그치지 않도록 그것에 근거를 둡니다.

  • “정의되지 않은 상태로 계속하기보다 거부한다”는 입장은 Spec: ISO/IEC 25010 §3의 페일세이프(fail-safe) 품질 속성입니다 — 실패 시 스스로를 안전한 상태에 두는 제품입니다. 같은 계열의 결함 허용(fault tolerance) 은 시스템이 결함에도 불구하고 의도한 대로 계속 동작하는 정도입니다. NextPDF는 그 노력을 결함을 감추는 데가 아니라, 결함을 감지하고 멈추는 데 돌립니다.
  • “채택 전에 경계를 명시한다”는 입장은 적절성 인식 가능성(appropriateness recognizability) ( Spec: ISO/IEC 25010, §3.26 )입니다. 문서와 첫인상으로 적합성을 판단할 수 있는 능력입니다.
  • 이 모든 것이 중요한 PDF 특화 이유는 Spec: ISO 32000-2, §12.8 입니다. 서명의 바이트 범위는 그것이 포함하는 바이트만 정확히 보호하며 그 이상은 보호하지 않으므로, 서명된 문서를 “친절하게” 다시 쓰거나 그 주변을 추측하는 엔진은 전혀 도움이 되지 않습니다.

개별 원칙들은 각자의 페이지에서 실제 엔진 소스를 대상으로 입증됩니다 — 추측을 거부하는 API기능으로서의 오류 페이지가 Evidence: Code-backed 증거를 담고 있습니다. 이 페이지는 ‘왜’이고, 그 페이지들은 ‘무엇’을 다룹니다.

이 원칙들은 평범한 사용 코드 몇 줄 안에서도 드러납니다. 서명 호출은 의도를 명시적으로 밝힙니다. 엔진은 오해를 부르는 무언가를 내보내기보다 일찍 거부합니다.

<?php
declare(strict_types=1);
use NextPDF\Core\Document;
use NextPDF\Exception\NotImplementedException;
use NextPDF\Security\Signature\CertificateInfo;
use NextPDF\Security\Signature\SignatureLevel;
$document = Document::createStandalone();
$document->setTitle('Service Agreement 2026-0042');
$document->addPage();
$document->setFont('helvetica', '', 12);
$document->cell(0, 10, 'This agreement is configured for a PAdES signature.', newLine: true);
// Explicit beats implicit: the PAdES level is a required, named argument.
// There is no inferred or "auto" level.
$document->setSignature(
certInfo: new CertificateInfo(
certificate: $certificatePem,
privateKey: $privateKeyPem,
),
level: SignatureLevel::PAdES_B_B,
);
try {
// Fail fast, no silent degradation: rather than emit an UNSIGNED file
// that the caller believes setSignature() signed, the high-level path
// refuses and names the supported route.
$document->save('/srv/output/agreement.pdf');
} catch (NotImplementedException $e) {
// The message identifies the feature and the follow-up, not a stack
// trace: "... is not implemented in this release. <actionable follow-up>"
error_log($e->getMessage());
}

핵심은 서명이 어떻게 작동하는지가 아닙니다. 핵심은 하나의 스니펫에서 세 가지 원칙을 볼 수 있다는 점입니다. 의도가 명시되고(level:), 실패는 이르고 이름이 붙으며, 엔진은 자신의 상태에 대해 거짓말하는 문서를 만들기를 거부합니다.

가장 흔한 오해는 이 원칙들이 NextPDF를 “사용하기 더 어렵게” 만든다는 생각입니다. 이 원칙들은 잘못 사용하기 더 어렵게 만듭니다. 필수 인자는 예상 밖의 암묵적 기본값을 하나 줄여 줍니다. 이른 예외는 아카이브에 손상된 산출물을 하나 줄여 줍니다. 이 마찰은 실수의 비용이 큰 곳 — 운영 환경, 감사, 법정 —이 아니라, 실수의 비용이 작은 곳 — 호출 지점, 개발 단계 —에 의도적으로 배치됩니다.

두 번째 오해는 “의견이 강한”이 “융통성이 없는”을 뜻한다는 생각입니다. 그렇지 않습니다. 엔진은 여러분의 문서가 아니라 정확성과 의도에 대해 의견을 가집니다. 레이아웃, 콘텐츠, 폰트, 구조는 여전히 여러분이 완전히 제어합니다. 그 의견은 추측이 안전하지 않은 곳에서 여러분을 대신해 추측하지 않겠다는 입장입니다.

이 페이지는 설계 의도를 명시합니다. 이 페이지 자체가 동작 명세는 아닙니다. 이 원칙들은 결정이 내려지는 방식을 설명할 뿐, 특정 메서드를 보장하지는 않습니다. 각 메서드의 정확한 계약은 레퍼런스와 증거 수준이 명시된 해당 Insider_ 페이지에서 확인해야 합니다.

이 원칙들은 또한 절대적인 물리 법칙도 아닙니다. 판단과 함께 적용되는 우선순위입니다. 두 원칙이 충돌하는 경우(더 엄격한 거부 대 더 관대한 기본값), 위의 우선순위 순서가 우열을 가리는 기준이 됩니다. 특정 모듈은 그럼에도 근거 있는 예외를 문서화할 수 있습니다. 그럴 때 그 예외는 가정되는 것이 아니라 명시적으로 기록됩니다.

끝으로, 여기서 “설계 원칙”을 증거 수준으로 두는 것은 의도적입니다. 이 페이지는 논증합니다. 벤치마크하지는 않습니다. 수치, 테스트 또는 조항으로 뒷받침해야 하는 주장은 여기가 아니라 그 증거를 보유한 페이지에서 제시됩니다.

  • 추측을 거부하는 API — 명시적 의도 원칙과 빠른 실패 원칙을 실제 API를 대상으로 보여 줍니다.
  • 기능으로서의 오류 — 설계된 표면으로서의 타입 지정 예외 계층입니다.
  • PHP 8.4 기반 — 이 원칙들이 바람으로만 그치지 않고 강제될 수 있게 해 주는 언어 기능입니다.
  • 설계 원칙(증거 수준) — 그 주장이 의도적인 설계 결정이며, 벤치마크나 단일 테스트로 측정된 것이 아니라 의도와 이를 뒷받침하는 표준을 근거로 논증된 페이지입니다.
  • 페일세이프(fail-safe) — 소프트웨어 품질 속성입니다. 실패 시 제품이 정의되지 않은 상태로 계속 진행하지 않고 안전한 상태로 되돌아갑니다. NextPDF가 추측하기보다 거부하는 이유입니다.
  • 빠른 실패(fail fast) — 계속 진행하다가 나중에 모호하게 실패하는 대신, 유효하지 않은 입력을 가능한 가장 이른 지점에서 명확한 원인과 함께 거부하는 것입니다.
  • PAdES — PDF Advanced Electronic Signatures, PDF 문서 서명을 위한 ETSI 프로파일 계열입니다(B-B, B-T, B-LT, B-LTA). 여기서는 처음 사용될 때 풀어서 설명하며, 서명 페이지에서 깊이 있게 다룹니다.
  • 필요하지만 충분하지는 않다 — 프로세스 내 검사가 실질적인 신호이기는 하지만 적합성 판정은 아닐 때 사용하는 의도적인 표현입니다. 권위 있는 판정은 독립적인 검증기에 속합니다.