클라우드 Mac에서 iOS PDF 내보내기 검증하기

클라우드 Mac에서 iOS PDF 내보내기 검증하기

iOS 앱에서 보고서의 ‘내보내기’를 눌렀고 공유 시트에도 파일이 나타났지만, 받는 사람이 열어 보니 한 페이지가 빠져 있다. 이런 문제는 내보내기 함수가 성공을 반환했다는 사실만으로는 잡아낼 수 없다. 테스트 작업에서 최종 PDF 파일을 확보해 기계적으로 판정할 수 있는 항목을 검사하고, 나머지는 시각적으로 검토해야 한다. 아래 절차는 클라우드 Mac에서 시뮬레이터 테스트를 실행할 때 적합하며, 로컬에서도 그대로 적용할 수 있다.

검증 가능한 결과 파일부터 정의하기

명세 두 건과 명확한 보고서 제목처럼 입력 데이터를 고정한다. 테스트에는 예상 파일 이름, 최소 페이지 수, 본문에서 안정적으로 추출할 수 있는 문구가 정해져 있어야 한다. 생성 시각, 임의 번호, 합계 금액을 그대로 문자열 단언에 넣지는 말자. 테스트 데이터에 따라 값이 달라질 수 있기 때문이다. 입력 데이터를 고정하고 동적 필드에는 별도의 명확한 형식 규칙을 적용하는 편이 더 안정적이다.

테스트용 앱이 PDF를 앱의 Documents/export.pdf에 쓰도록 한다. 검증을 쉽게 하기 위한 경로일 뿐, 정식 제품에서도 같은 경로로 파일을 제공해야 한다는 뜻은 아니다. 매 실행 전에 이전 파일을 삭제하고, 내보내기가 끝난 뒤 파일이 존재하며 비어 있지 않은지 확인한다. 그렇지 않으면 이전 실행의 결과물 때문에 이번 테스트가 잘못 통과할 수 있다.

파일의 존재 여부, 파싱 가능 여부, 내용의 정확성은 각각 별개의 검사다. 앞선 검사를 통과했다고 다음 검사를 생략할 수는 없다.

시뮬레이터에서 이번 실행의 결과물 가져오기

먼저 테스트 작업에 지정된 시뮬레이터 식별자로 앱을 실행하고 내보내기를 수행한다. 작업이 끝나면 같은 식별자에 해당하는 기기에서 앱 컨테이너를 조회한다. booted로 대상을 짐작하지 말자. 시뮬레이터가 여러 대 실행 중일 때 특히 잘못된 기기를 선택하기 쉽다. 아래의 식별자와 앱 번들 ID는 테스트 설정에서 제공해야 한다.

UDID="替换为测试使用的模拟器标识"
BUNDLE_ID="替换为测试应用的包名"
CONTAINER="$(xcrun simctl get_app_container "$UDID" "$BUNDLE_ID" data)" || exit 1
mkdir -p artifacts
test -s "$CONTAINER/Documents/export.pdf" || exit 1
cp "$CONTAINER/Documents/export.pdf" artifacts/export.pdf
file artifacts/export.pdf

get_app_container를 사용하려면 지정한 시뮬레이터에 앱이 설치되어 있어야 한다. 조회에 실패하면 디스크 전체를 검색하는 대신, 테스트에서 실제 사용한 기기 식별자와 번들 ID, 앱 설치 결과를 먼저 확인한다. 복사 후에는 작업 디렉터리의 파일을 검사하고, 이후의 파싱과 보관에도 이 사본을 사용한다. 그래야 앱이 다시 파일을 쓰더라도 검사 대상이 바뀌지 않는다. file 출력은 문제 진단에 도움이 되지만 PDF 파싱을 대신하지는 못한다.

PDFKit으로 구조와 텍스트 검사하기

다음 Swift 코드를 scripts/check-pdf.swift로 저장한다. 파일 경로와 예상 문구를 인수로 받아 각 페이지의 경계를 검사하고, 적어도 한 페이지에 해당 문구가 있는지 확인한다. 종료 코드가 0이 아니면 빌드 작업에서 이번 자동 검사가 실패했다고 판정할 수 있다.

import Foundation
import PDFKit

guard CommandLine.arguments.count == 3,
      let pdf = PDFDocument(url: URL(fileURLWithPath: CommandLine.arguments[1])),
      pdf.pageCount > 0 else {
    fputs("PDF is missing or cannot be parsed\n", stderr)
    exit(2)
}

let expected = CommandLine.arguments[2]
var extracted = ""
for index in 0..<pdf.pageCount {
    guard let page = pdf.page(at: index) else {
        fputs("PDF page is missing\n", stderr)
        exit(2)
    }
    let media = page.bounds(for: .mediaBox)
    let crop = page.bounds(for: .cropBox)
    guard media.width > 0, media.height > 0,
          crop.width > 0, crop.height > 0,
          media.contains(crop) else {
        fputs("PDF page bounds are invalid\n", stderr)
        exit(2)
    }
    extracted += page.string ?? ""
}
guard extracted.contains(expected) else {
    fputs("Expected text was not extracted\n", stderr)
    exit(2)
}
print("Checked \(pdf.pageCount) PDF page(s)")

swift scripts/check-pdf.swift artifacts/export.pdf "Quarterly Report"를 실행한다. 예시 문구는 고정된 테스트 데이터에 실제로 들어 있는 텍스트로 바꿔야 한다. 요구사항이 정확히 두 페이지라면 pdf.pageCount == 2도 단언에 추가한다. 페이지 수가 0보다 큰지만 검사해서는 페이지 구성이 달라진 것을 발견할 수 없다. 페이지 크기도 제품 요구사항에 맞춰 각각 확인할 수 있지만, 사전 합의 없이 유효한 모든 용지 규격을 오류로 처리해서는 안 된다.

텍스트 추출은 시각적 검증이 아니다

page.string은 추출 가능한 텍스트를 검사할 뿐, 글자가 잘리지 않았거나 차트·이미지·색상이 올바르다는 것을 보장하지 않는다. 반대로 스캔 문서나 이미지로 그린 글자는 화면상으로 멀쩡해 보여도 텍스트가 추출되지 않을 수 있다. 텍스트 단언에 실패하면 원본 PDF를 보존하고, 내용이 실제로 누락된 것인지 아니면 해당 페이지에 텍스트 추출 방식이 맞지 않는 것인지 판단한다. 테스트를 통과시키려고 단언을 곧바로 삭제해서는 안 된다.

실패 지점을 단계별로 파악하기

매 작업마다 내보내기 작업의 결과, 파일 복사 결과, PDFKit 종료 코드를 따로 기록하는 것이 좋다. 실패했을 때는 앱이 내보내기를 실행했는지, 컨테이너에 이번 실행의 파일이 생성됐는지, 사본을 파싱할 수 있는지, 페이지 수·경계·텍스트가 예상과 맞는지 순서대로 확인할 수 있다. ‘PDF 테스트 실패’라는 메시지만 남기면 원인을 찾기 위해 전체 절차를 다시 실행해야 한다.

증상 우선 확인할 항목
컨테이너를 찾을 수 없음 시뮬레이터 식별자, 번들 ID, 설치 결과
파일이 비어 있음 이전 파일 삭제 여부, 쓰기 완료 여부
파싱할 수 없음 복사 과정, 최종 파일 형식
텍스트가 일치하지 않음 고정 입력 데이터, 페이지 내용, 텍스트 추출의 한계

실패하면 artifacts/export.pdf와 테스트 로그를 보관하되, 먼저 보고서에 실제 사용자 데이터가 없는지 확인한다. 민감한 정보가 들어 있을 수 있다면 합성 샘플을 사용하고 결과물 접근을 제한하며 팀에서 정한 절차에 따라 삭제한다. 운영 환경의 보고서를 공개 파이프라인의 결과물로 업로드하지 않는다.

재현 가능한 실행으로 마무리하기

검증 절차를 제출하기 전에 같은 고정 입력으로 두 번 연속 실행한다. 두 번 모두 새로 내보내 대상 파일을 덮어쓰고, 페이지 수와 텍스트 검사 결과가 일치해야 한다. 그런 다음 예상 문구를 일부러 제거해 스크립트가 실패하는지 확인한다. 테스트 앱이 파일을 생성하지 않도록 해서 복사 단계도 실패하는지 확인한다. 이 두 가지 역방향 검사는 단언이 아예 실행되지 않는 상황을 걸러낸다.

자동 검사를 통과한 뒤에도 대표 샘플 PDF를 열어 페이지가 나뉘는 지점, 긴 텍스트의 줄바꿈, 차트의 가장자리를 살펴봐야 한다. 기계는 일관된 구조·텍스트 요건을 검사하고, 사람은 실제 표시 결과를 확인한다. 두 결과를 따로 기록해야 다음 내보내기에 문제가 생겼을 때 비교할 기준이 분명해진다.

자주 묻는 질문

PDFKit에서 텍스트를 추출하지 못하면 내보내기가 실패한 건가요?

반드시 그렇지는 않습니다. 이미지로 그린 글자나 일부 글꼴은 추출되지 않을 수 있으므로 파일을 보관하고 화면도 확인해야 합니다.

시뮬레이터의 PDF를 검증 작업으로 가져오려면 어떻게 하나요?

앱의 Documents에 저장한 뒤 simctl get_app_container로 데이터 디렉터리를 찾아 작업 결과 폴더로 복사합니다.

다음 빌드는 전용 클라우드 Mac에 맡겨 보세요.

ViewMini의 기종, 지역, 이용 기간을 확인하고 워크플로에 맞는 물리 노드를 구성하세요.

클라우드 Mac 구성하기

기기와 이용 기간을 다시 확인하세요.

세 가지 기종을 일간·주간·월간·분기별 요금으로 이용할 수 있습니다. 주문 전에 전체 요금제를 확인하세요.

기종 및 요금 보기 클라우드 Mac 구성하기