PDF 변환을 API로 자동화하기 — 인증·에러·재시도 설계 가이드

PDF API자동화에러 처리인증

PDF 변환을 손으로 하나씩 처리하다 보면 어느 순간 한계가 옵니다. 매일 수백 건의 청구서를 이미지로 바꿔야 하거나, 사용자가 업로드한 문서를 실시간으로 병합·압축해야 하거나, 야간 배치로 보고서 수천 장을 만들어야 할 때입니다. 이런 상황에서 웹 페이지에 파일을 드래그하는 방식은 더 이상 답이 아닙니다. 변환을 코드로 호출하는 API 자동화가 필요해지죠. 그런데 API를 붙이는 일은 단순히 엔드포인트에 파일을 던지는 것으로 끝나지 않습니다. 인증을 어떻게 안전하게 관리할지, 실패했을 때 어떻게 복구할지, 한도에 걸렸을 때 어떻게 대기할지를 설계해야 비로소 운영에서 살아남는 자동화가 됩니다. 이 글은 그 설계 전체를 다룹니다.

언제 API가 맞고, 언제 과한가

모든 PDF 작업에 API가 정답은 아닙니다. 도구를 잘못 고르면 오히려 비용과 복잡도만 늘어납니다. 판단 기준은 '반복성'과 '통합 필요성' 두 가지입니다. 한 달에 몇 번, 그것도 사람이 직접 보면서 처리하는 작업이라면 웹 도구나 데스크톱 프로그램이 더 빠르고 안전합니다. 굳이 API 키를 발급받고 에러 핸들링 코드를 짤 이유가 없습니다.

반면 같은 변환이 정해진 규칙으로 반복되고, 그 결과물이 다른 시스템(데이터베이스, 스토리지, 메일 발송, 사용자 화면)으로 흘러 들어가야 한다면 API가 압도적으로 유리합니다. 사람이 개입하는 단계가 사라지면 처리량이 곧 코드의 처리량이 되기 때문입니다. 상황별로 정리하면 다음과 같습니다.

  • 일회성·소량·시각적 확인이 필요한 작업: 웹 도구(브라우저에서 바로)나 오피스 프로그램의 '다른 이름으로 저장 → PDF'가 가장 빠릅니다.
  • 사내에서 정기적으로 같은 양식을 처리하지만 외부 노출은 없는 작업: 로컬에 설치하는 명령줄 도구(예: pdftoppm, qpdf, Ghostscript)를 cron이나 스크립트로 묶는 편이 데이터 유출 걱정이 없고 비용도 들지 않습니다.
  • 서비스의 일부로 변환 기능이 들어가야 하거나, 처리량이 사람의 손을 넘어서거나, 여러 서버에서 동시에 호출해야 하는 작업: 이때가 변환 API를 도입할 진짜 시점입니다.

여기서 흔한 함정 하나. 명령줄 도구를 직접 서버에 설치하는 길을 택했다가, Ghostscript나 Poppler의 버전·폰트·의존성 문제로 발목 잡히는 경우가 많습니다. 특히 한글 폰트가 깔리지 않은 컨테이너 환경에서 PDF를 이미지로 렌더링하면 한글이 통째로 깨지거나 네모(□)로 나옵니다. API를 쓰면 이 의존성 관리를 서비스 쪽에 위임할 수 있다는 점이 실무에서 생각보다 큰 이점입니다.

인증 설계: 키를 코드에 박지 마라

대부분의 변환 API는 API 키를 헤더에 실어 보내는 방식을 씁니다. 단순해서 좋지만, 단순한 만큼 실수가 잦은 부분이기도 합니다. 가장 흔한 사고는 키를 소스 코드에 그대로 적어 깃 저장소에 올리는 것입니다. 공개 저장소든 사내 저장소든, 한번 커밋 히스토리에 들어간 키는 사실상 노출된 것으로 간주하고 즉시 폐기·재발급해야 합니다.

안전한 기본 원칙은 세 가지입니다. 첫째, 키는 환경 변수나 시크릿 매니저로만 주입하고 코드에는 변수 이름만 남깁니다. 둘째, 키는 서버 사이드에서만 사용합니다. 브라우저에서 직접 변환 API를 호출하면 개발자 도구만 열어도 키가 그대로 보이므로, 프런트엔드는 자체 백엔드를 거치게 하고 그 백엔드가 키를 들고 외부 API를 호출하는 구조로 만듭니다. 셋째, 가능하면 용도별로 키를 분리하고 주기적으로 교체합니다. 한 키가 유출돼도 영향 범위를 줄일 수 있습니다.

운영 키와 테스트 키를 반드시 분리하세요. 로컬 개발이나 CI에서 운영 키를 쓰면, 디버깅 중 무심코 던진 수백 건의 요청이 운영 한도를 갉아먹거나 청구 금액을 키웁니다.

에러를 분류하라: 재시도할 것과 하지 말 것

자동화에서 진짜 실력이 드러나는 부분은 정상 경로가 아니라 실패 경로입니다. 변환 API가 돌려주는 에러는 크게 두 부류로 나뉘고, 이 둘을 구분하지 못하면 자동화가 조용히 망가집니다.

재시도해도 소용없는 에러 (4xx 계열)

클라이언트 측 문제입니다. 같은 요청을 다시 보내도 결과는 똑같습니다. 인증 실패(401), 권한 없음(403), 잘못된 파라미터나 손상된 파일(400/422)이 여기 속합니다. 이런 에러는 재시도 대상이 아니라 로그를 남기고 알림을 띄워 사람이 봐야 할 신호입니다. 예를 들어 401이 떴는데 무한 재시도를 걸어두면, 만료된 키로 영원히 헛발질하면서 로그만 쌓입니다. 깨진 파일을 보낸 422도 마찬가지로, 재시도 대신 '이 파일은 변환 불가'로 따로 격리해야 합니다.

재시도하면 풀릴 수 있는 에러 (429·5xx 계열)

일시적 문제입니다. 한도 초과(429), 서버 일시 오류(500/502/503), 네트워크 타임아웃이 여기 해당합니다. 이런 에러는 잠시 기다렸다 다시 보내면 성공할 가능성이 높습니다. 다만 무작정 바로 재시도하면 서버에 부하를 더 주고 한도를 더 빨리 소진할 뿐입니다. 그래서 '지수 백오프(exponential backoff)'를 씁니다. 1초, 2초, 4초처럼 대기 시간을 점점 늘려가며 재시도하고, 여기에 약간의 무작위 지연(지터)을 더해 여러 클라이언트가 동시에 몰리는 것을 막습니다.

특히 429 응답에는 보통 Retry-After 헤더가 함께 옵니다. 이 헤더는 '몇 초 뒤에 다시 오라'는 서버의 명시적 지시입니다. 자체 백오프 계산값보다 이 헤더를 우선해서 따르는 것이 정석입니다. 헤더를 무시하고 계속 두드리면 한도 위반이 누적돼 더 긴 차단을 받을 수도 있습니다.

멱등성과 중복 처리

재시도를 도입하면 새로운 문제가 따라옵니다. 요청은 실제로 처리됐는데 응답만 네트워크에서 유실된 경우, 클라이언트는 실패로 알고 다시 보냅니다. 그러면 같은 파일이 두 번 변환되거나 두 번 저장될 수 있습니다. 결제처럼 민감한 작업이 아니더라도, 같은 보고서가 사용자에게 두 번 메일로 가면 곤란하죠.

해결책은 작업 단위마다 고유 식별자를 만들어 두는 것입니다. 파일 내용의 해시나 업무상 고유 ID를 키로 삼아, 변환 결과를 저장할 때 '이 키로 이미 처리됐는지'를 먼저 확인합니다. 이미 있으면 변환을 건너뛰고 기존 결과를 반환합니다. 이 단순한 한 줄의 점검이 재시도가 만드는 중복을 대부분 막아줍니다.

동기 vs 비동기, 그리고 대용량 파일

작은 파일은 요청을 보내고 변환된 결과를 그 자리에서 응답으로 받는 동기 방식이 편합니다. 하지만 수백 페이지짜리 PDF나 고해상도 이미지 변환은 응답이 오기까지 수십 초가 걸릴 수 있고, 그 사이 HTTP 연결이 게이트웨이 타임아웃(보통 30초나 60초)에 걸려 끊기기 쉽습니다. 이때 클라이언트는 실패로 보고 재시도하지만, 서버는 멀쩡히 변환을 끝내고 있어 자원만 낭비됩니다.

대용량·장시간 작업이 많다면 처음부터 비동기 패턴을 고려하세요. 요청을 보내면 즉시 작업 ID를 받고, 이후 상태를 폴링하거나 웹훅으로 완료 통보를 받는 방식입니다. 폴링은 구현이 쉽지만 간격 조절을 잘못하면 불필요한 요청이 쌓이고, 웹훅은 효율적이지만 수신 엔드포인트를 공개하고 검증하는 부담이 있습니다. 처리량과 운영 역량에 맞춰 고르면 됩니다. 타임아웃 설정도 잊지 마세요. 클라이언트 타임아웃이 서버의 실제 처리 시간보다 짧으면, 정상 작업까지 실패로 오인하게 됩니다.

한글 문서에서 자주 터지는 문제들

한국 사용자 환경에서 변환 API를 쓸 때 유독 자주 만나는 함정이 있습니다. 미리 알아두면 디버깅 시간을 크게 아낄 수 있습니다.

  • 한글 폰트 임베딩 누락: 원본 PDF가 폰트를 내장하지 않고 시스템 폰트에 의존하면, 변환 환경에 해당 폰트가 없을 때 글자가 깨집니다. 문서를 만들 때 폰트를 임베드하거나, 변환 서비스가 한글 폰트를 갖추고 있는지 확인하세요.
  • 파일명 인코딩: multipart 업로드에서 한글 파일명이 깨지는 경우가 있습니다. 가급적 파일명은 영문·숫자로 정규화해 보내고, 원본 이름은 별도 메타데이터로 전달하는 편이 안전합니다.
  • 스캔 PDF의 텍스트 부재: 스캔본은 사실상 이미지라 텍스트 추출이 안 됩니다. 텍스트가 필요하면 OCR 단계를 먼저 거쳐야 한다는 점을 자동화 흐름에 반영하세요.
  • 세로쓰기·표 깨짐: 복잡한 레이아웃은 변환 후 어긋날 수 있으므로, 양식이 고정적이라면 변환 결과를 샘플로 검수한 뒤 배치에 올리는 것이 안전합니다.

참고로 All-of-PDF의 변환 API도 한글 폰트와 multipart 업로드를 기본 지원하도록 만들어 두었지만, 어떤 서비스를 쓰든 위 항목들은 '원본 PDF 자체의 품질' 문제인 경우가 많습니다. 변환 전에 원본을 한 번 점검하는 습관이 가장 확실한 예방책입니다.

실전 트러블슈팅 체크리스트

자동화가 기대대로 안 돌 때, 아래 순서로 좁혀가면 대부분의 원인을 빠르게 찾을 수 있습니다.

  1. 인증부터 확인: 401이 나면 키 자체보다 헤더 이름·공백·따옴표 같은 사소한 형식 오류를 먼저 의심하세요.
  2. 요청 형식 확인: 415나 400이 나면 Content-Type과 필드 이름(file vs files), 파라미터 철자를 점검합니다.
  3. 한도 확인: 429가 나면 Retry-After 헤더 값을 읽고, 분당·일·월 한도 중 어디에 걸렸는지 응답 본문의 에러 코드로 구분하세요.
  4. 파일 점검: 422나 변환 결과 이상이면 원본 PDF를 로컬 뷰어로 열어 손상·암호·폰트 상태를 확인합니다.
  5. 타임아웃 점검: 큰 파일에서만 실패하면 클라이언트와 게이트웨이 타임아웃 값을 의심하고, 비동기 전환을 검토합니다.
  6. 멱등성 점검: 결과가 중복으로 쌓이면 재시도 로직과 중복 방지 키가 제대로 동작하는지 확인합니다.

정리하면, PDF 변환 API 자동화의 성패는 변환 그 자체가 아니라 그 주변 설계에 달려 있습니다. 키를 안전하게 다루고, 에러를 재시도 가능 여부로 분류하고, 백오프와 Retry-After를 존중하며, 중복을 막고, 대용량은 비동기로 넘기는 것. 이 다섯 가지를 갖추면 어떤 변환 서비스를 쓰더라도 운영에서 흔들리지 않는 자동화를 만들 수 있습니다. 아래는 가장 기본적인 동기 변환 호출 예시로, 여기서부터 위의 설계를 한 겹씩 덧붙여 나가면 됩니다.

curl -X POST "https://your-domain/api/v1/convert/pdf-to-image" \
  -H "X-API-Key: $PDF_API_KEY" \
  -F "file=@invoice.pdf" \
  -F "format=png" \
  --max-time 60 \
  --retry 3 --retry-delay 2 --retry-connrefused