IT tech Coding/javascript

html2pdf.js PDF가 흐리게 나올 때? 해상도 개선 방법 정리!

Coding Life 2025. 6. 27. 18:47

웹 페이지를 PDF로 저장할 때 자주 사용하는 라이브러리 중 하나가 html2pdf.js입니다.
하지만 많은 분들이 생성된 PDF가 너무 흐리다, 이미지가 깨진다, 텍스트가 뭉개져 보인다는 문제를 겪고 있죠.

이 문제의 핵심은 바로 해상도 설정 부족 때문입니다.
아래 내용을 따라 설정하면 훨씬 선명한 PDF를 생성할 수 있습니다.


📌 문제 요약: PDF가 흐리게 출력되는 이유

html2pdf.js는 내부적으로 html2canvas를 이용해 웹 요소를 이미지로 변환하고 이를 PDF로 저장합니다.
이때 기본 해상도(scale) 값이 1로 설정되어 있어, 확대 출력 시 이미지가 흐릿하게 보이게 됩니다.


✅ 해결 방법: 해상도와 품질 옵션 추가

🔧 고해상도 PDF를 만드는 코드 예제

const element = document.getElementById('pdfTarget'); // PDF로 만들 DOM 요소

const opt = {
  margin:       10,               // 여백 설정 (단위: px)
  filename:     'myfile.pdf',     // 저장할 파일 이름
  image:        { type: 'jpeg', quality: 1 }, // 이미지 타입과 품질
  html2canvas:  {
    scale: 3,           // 💡 해상도 조정: 2~4 사이가 적당
    useCORS: true       // 외부 리소스 깨짐 방지
  },
  jsPDF: {
    unit: 'mm',
    format: 'a4',
    orientation: 'portrait'
  }
};

html2pdf().set(opt).from(element).save();

🔍 주요 옵션 설명

옵션 키 설명

html2canvas.scale 렌더링 해상도. 기본은 1이며, 2~4 사이로 높일수록 선명하지만 용량 증가
image.quality 이미지 품질 설정. 1은 최고 화질 (100%)
useCORS 외부 이미지나 폰트가 깨지는 현상 방지
jsPDF.format A4, A3 등 용지 크기 지정 가능
jsPDF.orientation 세로(portrait) 또는 가로(landscape) 출력 방향 설정

📄 추가 팁

  • scale을 너무 높게 설정하면 메모리 부족, 브라우저 멈춤 등의 문제가 발생할 수 있습니다.
    → 일반적으로 2 또는 3 정도가 가장 안정적입니다.
  • 외부 이미지, 웹폰트를 사용하는 경우 useCORS: true는 필수입니다.
  • 이미지가 아닌 텍스트 기반 PDF를 만들고 싶다면 jsPDF만 사용하는 방식도 고려해볼 수 있습니다.

🧪 직접 비교: scale에 따른 품질 차이

scale 값 이미지 선명도 PDF 용량 속도

1 흐림 작음 빠름
2 적당히 선명 중간 중간
3 선명함 느림
4 이상 매우 선명 매우 큼 매우 느림 (권장 X)

📝 마무리 정리

  • html2pdf()로 만든 PDF가 흐리게 나온다면?
    → html2canvas.scale 옵션을 3 정도로 설정하세요.
  • 이미지 품질은 image.quality: 1로 최상으로 지정하세요.
  • 외부 리소스 깨짐 방지를 위해 useCORS: true도 함께 설정하세요.

📦 보너스: A3 용지로 출력하고 싶다면?

jsPDF: {
  unit: 'mm',
  format: 'a3',         // A3로 변경
  orientation: 'landscape'  // 가로 출력
}

더 선명하고 깨끗한 PDF를 만들고 싶다면 위 설정을 꼭 적용해보세요!
궁금한 점이 있거나 커스터마이징이 필요하면 댓글로 질문 주세요. 😊


필요하다면 이 글에 PDF 예시 파일 첨부도 가능합니다.
html2pdf.js 관련 추가 설정법도 다룰 예정이니 북마크 해두시면 좋아요!

 

📌 태그 추천: #html2pdf, #PDF해상도, #html2canvas, #PDF흐림, #PDF선명도, #웹페이지PDF



html2pdf.js에서 A4 기준으로 페이지가 넘칠 때 내용이 짤리는 현상은 꽤 자주 발생하는 문제입니다.
이를 해결하기 위해 **페이지 분할(pagination)**을 잘 처리해야 하며, 다음과 같은 전략을 사용하면 됩니다:


✅ A4 페이지에서 내용이 짤리지 않게 하는 방법

1. html2canvas의 scrollY, scrollX을 무시하게 설정

페이지의 스크롤 위치 때문에 렌더링이 잘못되는 걸 방지합니다:

html2canvas: {
  scale: 3,
  useCORS: true,
  scrollY: 0,
  scrollX: 0,
  windowWidth: document.body.scrollWidth,
  windowHeight: document.body.scrollHeight
}

2. page-break CSS 속성 사용 (가장 중요한 방법)

PDF가 렌더링될 때, 특정 위치에서 강제로 줄바꿈하도록 합니다.

예시:

<div class="pdf-page">
  <h2>1페이지 내용</h2>
  ...
</div>

<div class="page-break"></div>

<div class="pdf-page">
  <h2>2페이지 내용</h2>
  ...
</div>
/* 페이지 나눔 표시 */
.page-break {
  page-break-before: always;
  break-before: page;
}

또는 특정 블록이 분리되지 않도록 하려면:

.avoid-break {
  page-break-inside: avoid;
  break-inside: avoid;
}

3. html2pdf 사용 시 .from()을 .from(clone) 형태로 커스터마이징하는 방법

크게 분할해야 하는 경우, DOM을 미리 A4 크기로 분할해두고 각 섹션별로 PDF로 렌더링하는 것이 안정적입니다.

html2pdf는 내부적으로 하나의 긴 이미지를 자르기 때문에, 긴 테이블이나 이미지가 잘릴 수 있습니다.
이런 경우 직접 A4 높이에 맞게 분할된 <div> 단위로 구성해야 완벽한 제어가 가능합니다.


🧪 실전 팁

  • 표나 긴 텍스트를 출력할 때는 반드시 page-break-inside: avoid;를 사용하세요.
  • html2canvas는 실제 DOM 기준이므로, 출력 요소에 overflow: visible을 유지하세요.
  • height, max-height 값을 정확히 297mm 기준(A4 세로)으로 맞춰주면 계산이 쉬워집니다.

🧩 결론

상황 해결 방법

텍스트가 중간에 짤림 .page-break 클래스로 분할
표나 이미지가 잘려서 보임 .avoid-break 클래스로 해당 요소 내부 분할 방지
전체가 스크롤된 상태로 렌더됨 scrollX, scrollY, windowHeight 직접 설정
너무 많은 콘텐츠로 PDF 오류 발생 DOM을 A4 크기로 미리 나누고 여러 번 렌더링하는 방식 고려

 

아래는 A4 용지 기준으로 긴 콘텐츠를 자동으로 분할하여 PDF로 저장하는 JavaScript 코드 예제입니다.


✅ 목적

  • HTML 요소의 길이가 A4를 넘을 경우
  • 자동으로 페이지를 나눠
  • html2pdf.js를 이용해 짤림 없이 저장합니다.

📦 전체 코드 예시


PDF 저장
function generatePDF() {
  const element = document.getElementById('pdfTarget');

  // A4 기준 높이 (mm 기준 → html2canvas px 기준으로 변환 필요)
  const A4_HEIGHT_MM = 297;
  const A4_WIDTH_MM = 210;
  const DPI = 96; // 화면 해상도 (웹 기본값)
  const MM_TO_PX = DPI / 25.4;

  const A4_HEIGHT_PX = A4_HEIGHT_MM * MM_TO_PX;
  const A4_WIDTH_PX = A4_WIDTH_MM * MM_TO_PX;

  // html2canvas + jsPDF 세팅
  const opt = {
    margin:       0,
    filename:     'output.pdf',
    image:        { type: 'jpeg', quality: 1 },
    html2canvas:  {
      scale: 2,
      useCORS: true,
      scrollX: 0,
      scrollY: 0,
      windowWidth: document.body.scrollWidth,
      windowHeight: document.body.scrollHeight
    },
    jsPDF: {
      unit: 'mm',
      format: 'a4',
      orientation: 'portrait'
    }
  };

  // 렌더링용 clone
  const cloned = element.cloneNode(true);
  const container = document.createElement('div');
  container.style.width = A4_WIDTH_PX + 'px';
  container.appendChild(cloned);
  document.body.appendChild(container);

  // html2canvas로 전체 렌더링 후 캔버스를 분할
  html2canvas(cloned, {
    scale: 2,
    useCORS: true,
    scrollX: 0,
    scrollY: 0,
    windowWidth: document.body.scrollWidth,
    windowHeight: document.body.scrollHeight
  }).then(canvas => {
    const imgHeight = canvas.height;
    const imgWidth = canvas.width;
    const pageHeight = A4_HEIGHT_PX * 2; // scale 고려
    const pdf = new jsPDF('p', 'mm', 'a4');
    let position = 0;

    let pageData;
    let pageCount = 0;

    while (position < imgHeight) {
      // 캔버스에서 자르기
      const canvasPage = document.createElement('canvas');
      canvasPage.width = imgWidth;
      canvasPage.height = Math.min(pageHeight, imgHeight - position);
      const ctx = canvasPage.getContext('2d');
      ctx.drawImage(canvas, 0, -position);

      pageData = canvasPage.toDataURL('image/jpeg', 1.0);
      if (pageCount > 0) pdf.addPage();
      pdf.addImage(pageData, 'JPEG', 0, 0, 210, 297);
      position += pageHeight;
      pageCount++;
    }

    pdf.save('output.pdf');
    document.body.removeChild(container); // cleanup
  });
}

🔍 핵심 설명

항목 설명

html2canvas(...).then(canvas => {}) 전체 DOM을 캡처
canvas.drawImage(..., -position) A4 높이만큼 잘라서 하나씩 붙임
jsPDF.addPage() 다음 페이지 추가
canvasPage.toDataURL() 잘라낸 이미지를 PDF에 삽입

📄 특징 요약

  • 실제 A4 용지 높이(mm → px로 환산)를 기준으로 자동 분할
  • 한 번에 렌더링 후, 페이지 단위로 잘라 저장
  • 깨짐/흐림 방지를 위한 scale: 2 설정
  • 전체 길이 자동 계산 → 짤림 없음

필요하시면 다음과 같은 기능도 추가 가능합니다:

  • A3, 가로 방향 대응
  • 페이지 번호 출력
  • 긴 표 자동 줄바꿈
  • DOM 내에서 특정 구역만 출력하기


html2pdf.js로 PDF를 생성할 때 페이지 하단에 페이지 번호 (1 / 5, 2 / 5 등) 를 자동으로 삽입하는 방법을 안내드리겠습니다.


✅ 방법 요약

html2pdf.js는 내부적으로 jsPDF를 사용하므로, PDF 생성 후 페이지 수를 얻고, 각 페이지마다 번호를 수동 삽입해야 합니다.


📦 전체 코드 예제 (페이지 번호 포함)

PDF 제목

내용이 길어 여러 페이지로 나뉘게 됩니다...


PDF 저장
function generatePDF() {
  const element = document.getElementById('pdfTarget');

  const opt = {
    margin:       10,
    filename:     'output_with_pagenumber.pdf',
    image:        { type: 'jpeg', quality: 1 },
    html2canvas:  {
      scale: 2,
      useCORS: true,
      scrollX: 0,
      scrollY: 0,
      windowWidth: document.body.scrollWidth,
      windowHeight: document.body.scrollHeight
    },
    jsPDF: {
      unit: 'mm',
      format: 'a4',
      orientation: 'portrait'
    }
  };

  // html2pdf → PDF 객체 직접 접근 후 페이지 번호 추가
  html2pdf().set(opt).from(element).toPdf().get('pdf').then(function (pdf) {
    const totalPages = pdf.internal.getNumberOfPages();

    for (let i = 1; i <= totalPages; i++) {
      pdf.setPage(i);
      pdf.setFontSize(10);
      pdf.setTextColor(150);

      const text = `${i} / ${totalPages}`;
      const pageWidth = pdf.internal.pageSize.getWidth();
      const pageHeight = pdf.internal.pageSize.getHeight();

      pdf.text(text, pageWidth - 30, pageHeight - 10); // (x, y)
    }
  }).save();
}

🧩 설명

항목 설명

.toPdf().get('pdf') 내부 PDF 객체에 접근
pdf.internal.getNumberOfPages() 전체 페이지 수 계산
pdf.setPage(i) 해당 페이지로 이동
pdf.text(...) 텍스트 삽입 (페이지 번호)

위치는 x = 페이지 너비 - 30, y = 페이지 하단 (297mm → 약 287) 부근에 지정하여 우측 하단에 출력됩니다.


🔧 원하는 위치 조정 팁

  • 가운데 하단:
  • const centerX = pageWidth / 2; pdf.text(text, centerX, pageHeight - 10, { align: 'center' });
  • 좌측 하단:
  • pdf.text(text, 10, pageHeight - 10);

✅ 결과 예시

페이지 하단에 다음과 같이 출력됩니다:

1 / 3
2 / 3
3 / 3

필요하다면:

  • 페이지 1 / 전체 3처럼 한국어 표현
  • 머리말에도 페이지 번호 출력
  • 특정 콘텐츠마다 Section 1 - Page 1 / 3 형식

 

**“PDF 저장 시 테이블이 잘리는 문제”**는 html2pdf.js와 html2canvas 사용 시 흔하게 발생하며, 이 문제는 CSS + JS 설정으로 해결할 수 있습니다.


✅ 요약: 현재 문제 원인

  • html2pdf.js는 DOM 전체를 캔버스로 변환한 뒤, A4 크기로 잘라서 PDF 페이지를 만듭니다.
  • 이때 테이블 행(tr)이나 전체 테이블(table)이 페이지 하단에서 분할되면, html2canvas가 이를 잘라버립니다.
  • 이로 인해 테이블 헤더와 내용이 분리되거나, 중간 행이 잘리는 현상이 발생합니다.

✅ 1단계: CSS에 page-break 관련 속성 추가

🔧 스타일 파일(style.css 또는 내부 <style>)에 아래 추가:

@media print {
  table {
    page-break-inside: auto !important;
  }

  tr {
    page-break-inside: avoid !important;
    page-break-after: auto !important;
  }

  thead {
    display: table-header-group !important; /* 페이지 넘어가도 thead 유지 */
  }

  tfoot {
    display: table-footer-group !important;
  }
}

🔹 추가로 필요한 경우 테이블 외 요소가 끊기는 것을 막고 싶다면:

.avoid-break {
  break-inside: avoid !important;
  page-break-inside: avoid !important;
}

위 클래스를 문제가 발생하는 <div> 또는 <table>에 class="avoid-break"로 추가하세요.


✅ 2단계: JS html2pdf() 호출 시 pagebreak 옵션 설정

기존 generatePDF() 함수는 아래처럼 되어 있죠:

html2pdf().from(element).set(opt).save();

여기에 다음을 추가합니다:

opt.pagebreak = {
  mode: ['css', 'legacy'],  // css 기준으로 줄바꿈
  avoid: ['tr', '.avoid-break']  // 행(tr)이 잘리지 않도록
};

🔄 수정된 전체 예시:

function generatePDF() {
    var element = document.getElementById('content-to-print');
    var opt = {
        margin: [10, 3, 12, 3],
        filename: '출고증.pdf',
        image: { type: 'jpeg', quality: 1 },
        html2canvas: {
            scale: 3,
            useCORS: true,
            scrollY: 0,
            scrollX: 0,
            windowWidth: document.body.scrollWidth,
            windowHeight: document.body.scrollHeight        
        }, 
        jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
        pagebreak: {
            mode: ['css', 'legacy'],
            avoid: ['tr', '.avoid-break'] // 핵심
        }
    };
    html2pdf().from(element).set(opt).save();
}

✅ 3단계: HTML에서 분할 방지하고 싶은 테이블에 클래스 추가

예시:

<table class="table avoid-break">
  ...
</table>

또는 내부 tr이나 tbody에도 적용 가능:

<tr class="avoid-break">

✅ 추가 팁: 줄바꿈 강제 삽입

어떤 위치에서 의도적으로 페이지를 나누고 싶다면:

<div style="page-break-before: always;"></div>

이걸 <h2> 위나 특정 구간 위에 삽입하면 페이지가 강제로 나뉩니다.


✅ 정리

항목 해결 방법

표가 잘리는 현상 tr { page-break-inside: avoid; } 설정
페이지 넘어갈 때 thead가 사라짐 thead { display: table-header-group; } 설정
한 행이 둘로 나뉘는 현상 .avoid-break 클래스 사용
PDF 내 강제 줄바꿈 <div style="page-break-before: always;"> 사용
JS 설정에서 제어 opt.pagebreak.avoid: ['tr'] 추가

필요하시면 아래 기능도 추가해드릴 수 있습니다:

  • 페이지 번호 삽입 (1 / 3)
  • 각 페이지에 상단 헤더 반복
  • A3 or landscape 출력 최적화

도움이 필요하신 부분만 말씀주세요!

 

반응형