HTML 요소 및 페이지 부분을 C#에서 PDF로 변환하는 방법
IronPDF는 특정 HTML 요소를 타겟팅하기 위한 내장 SelectElement 또는 SelectCss 메서드를 제공하지 않습니다. ChromePdfRenderer은 완전한 HTML 문서, 즉 전체 페이지, 전체 URL, 전체 HTML 문자열을 렌더링합니다. 페이지의 특정 섹션에서 PDF를 생성하기 위해 JavaScript DOM 조작, CSS 주입, 서버 측 HTML 조각 추출 또는 JS 타겟팅으로 URL 렌더링 중 하나의 방법을 사용하여 대상 요소를 렌더링 전에 분리합니다.
각 접근법은 다른 제약에 맞습니다. JavaScript DOM 격리는 URL 또는 전체 페이지를 렌더링할 때 대상 외의 모든 것을 제거해야 할 때 작동합니다. CSS 주입은 DOM을 변경하지 않고 불필요한 콘텐츠를 숨깁니다. 서버 측 추출은 원시 HTML에 액세스할 수 있을 때 가장 깨끗한 결과를 제공합니다. JS 타겟팅을 사용한 URL 렌더링은 소스 HTML을 사용할 수 없는 라이브 대시보드 및 타사 페이지를 처리합니다.
무료 30일 체험판 시작하기를 통해 모든 네 가지 방법을 테스트하세요.
빠른 시작: HTML 요소를 PDF로 변환하여 추출하기
JavaScript DOM 격리와 WaitFor를 사용하여 CSS 선택자로 아무 요소나 대상 지정하고, 해당 조각만 PDF로 렌더링하세요.
-
NuGet 패키지 관리자를 사용하여 https://www.nuget.org/packages/IronPdf 설치하기
PM > Install-Package IronPdf -
다음 코드 조각을 복사하여 실행하세요.
using IronPdf; var renderer = new ChromePdfRenderer(); renderer.RenderingOptions.EnableJavaScript = true; renderer.RenderingOptions.JavaScript = @" var target = document.querySelector('#invoice-summary'); document.body.innerHTML = target.outerHTML; "; renderer.RenderingOptions.WaitFor.HtmlQuerySelector("#invoice-summary", 10000); var pdf = renderer.RenderHtmlAsPdf(fullPageHtml); pdf.SaveAs("invoice-summary.pdf"); -
실제 운영 환경에서 테스트할 수 있도록 배포하세요.
무료 체험판으로 오늘 프로젝트에서 IronPDF 사용 시작하기
최소 워크플로 (3단계)
- IronPDF를 NuGet으로 설치:
Install-Package IronPdf ChromePdfRenderOptions.JavaScript를 구성하여 대상 요소를 격리하고WaitFor를 통해 존재를 보장합니다.RenderHtmlAsPdf()또는RenderUrlAsPdf()를 호출하세요 — PDF에는 격리된 콘텐츠만 포함됩니다.
JavaScript DOM 조작으로 요소를 어떻게 격리하나요?
ChromePdfRenderOptions.JavaScript 속성은 HTML이 로드된 후 PDF가 렌더링되기 전에 실행되는 JavaScript 문자열을 허용합니다. document.body.innerHTML을(를) 대상 요소의 outerHTML으로 교체하여 렌더된 페이지에서 다른 모든 것을 제거합니다. 이 접근은 가장 다재다능하며 RenderHtmlAsPdf()와 RenderUrlAsPdf() 모두에서 작동합니다.
WaitFor.HtmlQuerySelector() 메서드는 JavaScript가 실행되기 전에 대상 요소가 DOM에 존재하는지 보장합니다. 이는 비동기 콘텐츠가 있는 페이지 — React 컴포넌트, Angular 템플릿, API 구동 데이터가 초기 페이지 로드 후 채워지는 경우 — 에 중요합니다.
using IronPdf;
string fullPageHtml = @"
<html>
<body>
<header><h1>Acme Corp Invoice</h1></header>
<nav>Navigation links...</nav>
<div id='invoice-summary'>
<h2>Invoice #12345</h2>
<table>
<tr><td>Widget A</td><td>$49.99</td></tr>
<tr><td>Widget B</td><td>$29.99</td></tr>
<tr><td><strong>Total</strong></td><td><strong>$79.98</strong></td></tr>
</table>
</div>
<footer>Footer content...</footer>
</body>
</html>";
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;
// Replace the body with only the target element
renderer.RenderingOptions.JavaScript = @"
var el = document.querySelector('#invoice-summary');
if (el) {
document.body.innerHTML = el.outerHTML;
}
";
// Wait for the target element to exist before JS executes
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("#invoice-summary", 10000);
PdfDocument pdf = renderer.RenderHtmlAsPdf(fullPageHtml);
pdf.SaveAs("invoice-summary-only.pdf");
using IronPdf;
string fullPageHtml = @"
<html>
<body>
<header><h1>Acme Corp Invoice</h1></header>
<nav>Navigation links...</nav>
<div id='invoice-summary'>
<h2>Invoice #12345</h2>
<table>
<tr><td>Widget A</td><td>$49.99</td></tr>
<tr><td>Widget B</td><td>$29.99</td></tr>
<tr><td><strong>Total</strong></td><td><strong>$79.98</strong></td></tr>
</table>
</div>
<footer>Footer content...</footer>
</body>
</html>";
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;
// Replace the body with only the target element
renderer.RenderingOptions.JavaScript = @"
var el = document.querySelector('#invoice-summary');
if (el) {
document.body.innerHTML = el.outerHTML;
}
";
// Wait for the target element to exist before JS executes
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("#invoice-summary", 10000);
PdfDocument pdf = renderer.RenderHtmlAsPdf(fullPageHtml);
pdf.SaveAs("invoice-summary-only.pdf");
Imports IronPdf
Dim fullPageHtml As String = "
<html>
<body>
<header><h1>Acme Corp Invoice</h1></header>
<nav>Navigation links...</nav>
<div id='invoice-summary'>
<h2>Invoice #12345</h2>
<table>
<tr><td>Widget A</td><td>$49.99</td></tr>
<tr><td>Widget B</td><td>$29.99</td></tr>
<tr><td><strong>Total</strong></td><td><strong>$79.98</strong></td></tr>
</table>
</div>
<footer>Footer content...</footer>
</body>
</html>"
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.EnableJavaScript = True
' Replace the body with only the target element
renderer.RenderingOptions.JavaScript = "
var el = document.querySelector('#invoice-summary');
if (el) {
document.body.innerHTML = el.outerHTML;
}
"
' Wait for the target element to exist before JS executes
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("#invoice-summary", 10000)
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fullPageHtml)
pdf.SaveAs("invoice-summary-only.pdf")
JavaScript는 #invoice-summary div의 outerHTML으로 전체 본문을 교체합니다. 결과 PDF는 인보이스 테이블만 포함하며, 헤더, 내비게이션, 풋터는 포함하지 않습니다. WaitFor.HtmlElementById() 메서드는 ID로 대상으로 할 때 간단한 대안을 제공합니다:
// Alternative: wait by ID directly
renderer.RenderingOptions.WaitFor.HtmlElementById("invoice-summary", 10000);
// Alternative: wait by ID directly
renderer.RenderingOptions.WaitFor.HtmlElementById("invoice-summary", 10000);
' Alternative: wait by ID directly
renderer.RenderingOptions.WaitFor.HtmlElementById("invoice-summary", 10000)
복잡한 선택자 (클래스 이름, 데이터 속성, 중첩된 요소)의 경우, HtmlQuerySelector()는 document.querySelector()에서 허용하는 모든 유효한 CSS 선택자 문자열을 허용합니다. 추가 WaitFor 편의 메서드는 HtmlElementByClassName(), HtmlElementByName(), HtmlElementByTagName()를 포함하며 각각 내부적으로 HtmlQuerySelector()에 위임되지만 코드에서 더 명확한 의도를 제공합니다.
대상 요소가 상위 컨테이너에서 상속된 스타일에 의존하는 경우, outerHTML 교체는 상위 셀렉터에 의존하는 CSS 규칙을 잃을 수 있습니다 (예: .dashboard .widget table { ... }). 이를 유지하려면, 관련 <style> 및 <link> 태그를 <head>에서 JS 격리에 복사하세요.
renderer.RenderingOptions.JavaScript = @"
var el = document.querySelector('#invoice-summary');
if (el) {
var head = document.head.innerHTML;
document.body.innerHTML = el.outerHTML;
document.head.innerHTML = head;
}
";
renderer.RenderingOptions.JavaScript = @"
var el = document.querySelector('#invoice-summary');
if (el) {
var head = document.head.innerHTML;
document.body.innerHTML = el.outerHTML;
document.head.innerHTML = head;
}
";
renderer.RenderingOptions.JavaScript = "
var el = document.querySelector('#invoice-summary');
if (el) {
var head = document.head.innerHTML;
document.body.innerHTML = el.outerHTML;
document.head.innerHTML = head;
}
"
이렇게 하면 원래의 <head> 콘텐츠 (스타일시트, 폰트, 메타 태그)를 유지하면서 본문만 교체합니다. JavaScript를 PDF로 변환하는 방법 및 WaitFor 사용법은 추가 구성을 다루며, 여러 비동기 데이터 소스가 있는 페이지에 대한 NetworkIdle0()를 포함합니다.
CSS 주입으로 요소를 어떻게 격리하나요?
ChromePdfRenderOptions.CustomCssUrl 속성은 IronPDF가 렌더링 전에 적용하는 스타일시트의 파일 경로나 URL을 허용합니다. DOM을 조작하는 대신, 우리는 모든 것을 숨기고 대상 요소만 CSS display: none를 사용하여 나타냅니다. 이 방법은 원래의 DOM 구조를 유지하고 JavaScript 실행을 완전히 피합니다.
using IronPdf;
// Create a CSS file that hides everything except #invoice-summary
string cssContent = @"
body > *:not(#invoice-summary) {
display: none !important;
}
#invoice-summary {
display: block !important;
margin: 0;
padding: 20px;
}
";
File.WriteAllText("isolate-element.css", cssContent);
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CustomCssUrl = "isolate-element.css";
PdfDocument pdf = renderer.RenderHtmlAsPdf(fullPageHtml);
pdf.SaveAs("invoice-css-isolated.pdf");
using IronPdf;
// Create a CSS file that hides everything except #invoice-summary
string cssContent = @"
body > *:not(#invoice-summary) {
display: none !important;
}
#invoice-summary {
display: block !important;
margin: 0;
padding: 20px;
}
";
File.WriteAllText("isolate-element.css", cssContent);
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.CustomCssUrl = "isolate-element.css";
PdfDocument pdf = renderer.RenderHtmlAsPdf(fullPageHtml);
pdf.SaveAs("invoice-css-isolated.pdf");
Imports IronPdf
Imports System.IO
' Create a CSS file that hides everything except #invoice-summary
Dim cssContent As String = "
body > *:not(#invoice-summary) {
display: none !important;
}
#invoice-summary {
display: block !important;
margin: 0;
padding: 20px;
}
"
File.WriteAllText("isolate-element.css", cssContent)
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.CustomCssUrl = "isolate-element.css"
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fullPageHtml)
pdf.SaveAs("invoice-css-isolated.pdf")
참고: CustomCssUrl 속성은 HTML 문자열에서 렌더링할 때 RenderHtmlAsPdf()와 함께만 작동합니다. URL 렌더링의 경우, CSS 주입을 JavaScript 속성에 포함시키세요.
renderer.RenderingOptions.JavaScript = @"
var style = document.createElement('style');
style.textContent = 'body > *:not(#invoice-summary) { display: none !important; }';
document.head.appendChild(style);
";
renderer.RenderingOptions.JavaScript = @"
var style = document.createElement('style');
style.textContent = 'body > *:not(#invoice-summary) { display: none !important; }';
document.head.appendChild(style);
";
renderer.RenderingOptions.JavaScript = "
var style = document.createElement('style');
style.textContent = 'body > *:not(#invoice-summary) { display: none !important; }';
document.head.appendChild(style);
"
소스 HTML을 제어할 때, @media print 규칙은 가장 가벼운 대안을 제공합니다 — 외부 종속성 없음, 런타임 주입 없음:
@media print {
header, nav, footer, .sidebar { display: none !important; }
#invoice-summary { width: 100%; margin: 0; }
}
렌더러의 CssMediaType을 PdfCssMediaType.Print로 설정하여 이 규칙을 활성화하세요:
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print
이는 애플리케이션이 이미 인쇄 전용 레이아웃을 정의하는 양식 섹션 인쇄 시나리오에 이상적입니다. 보험 청구 양식, 다단계 마법사 및 결제 요약은 네비게이션과 진행 표시기를 숨기고 관련 콘텐츠 섹션을 전체 너비로 확장하기 위해 일반적으로 @media print 규칙을 사용합니다.
CSS 접근에는 중요한 제한이 하나 있습니다: 숨겨진 요소는 잘못된 특정 수준에서 display: none을 사용할 경우 문서 흐름에서 여전히 공간을 차지합니다. 항상 !important를 사용하여 프레임워크 스타일(Bootstrap, Tailwind)을 재정의하세요. 이 스타일은 특정 중단점에서 요소를 다시 표시할 수 있습니다. 깊이 중첩된 목표를 위해서는 더 정밀한 선택자가 동반되지 않은 숨기기를 피하도록 합니다.
body > *:not(#target),
body > *:not(#target) ~ * {
display: none !important;
}
서버 측에서 HTML 조각을 추출하는 방법은 무엇입니까?
원시 HTML에 액세스 할 수 있을 때 — 파일에서 읽거나, 데이터베이스, 콘텐츠 관리 시스템, HTTP 응답에서 안 — 가장 깨끗한 접근 방식은 HTML 구문 분석기를 사용하여 서버 측에서 대상 요소를 추출한 다음 RenderHtmlAsPdf()에 조각을 전달하는 것입니다. JavaScript, CSS 주입 또는 런타임 DOM 조작이 아닙니다.
AngleSharp는 이 패턴에 필요한 .NET HTML 파서 표준입니다.
using IronPdf;
using AngleSharp;
using AngleSharp.Html.Parser;
string fullPageHtml = @"
<html>
<head>
<style>
table { border-collapse: collapse; width: 100%; }
td, th { border: 1px solid #ddd; padding: 8px; text-align: left; }
</style>
</head>
<body>
<header><h1>Dashboard</h1></header>
<div id='revenue-widget'>
<h3>Q4 Revenue</h3>
<table>
<tr><th>Month</th><th>Revenue</th></tr>
<tr><td>October</td><td>$1.2M</td></tr>
<tr><td>November</td><td>$1.5M</td></tr>
<tr><td>December</td><td>$1.8M</td></tr>
</table>
</div>
<div id='other-content'>Other widgets...</div>
</body>
</html>";
// Parse and extract the target element
var parser = new HtmlParser();
var document = parser.ParseDocument(fullPageHtml);
var targetElement = document.QuerySelector("#revenue-widget");
if (targetElement is null)
{
Console.WriteLine("Target element not found.");
return;
}
// Wrap the fragment in a minimal HTML document to preserve styles
string fragmentHtml = $@"
<html>
<head>
<style>
table {{ border-collapse: collapse; width: 100%; }}
td, th {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
</style>
</head>
<body>
{targetElement.OuterHtml}
</body>
</html>";
var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml);
pdf.SaveAs("revenue-widget.pdf");
using IronPdf;
using AngleSharp;
using AngleSharp.Html.Parser;
string fullPageHtml = @"
<html>
<head>
<style>
table { border-collapse: collapse; width: 100%; }
td, th { border: 1px solid #ddd; padding: 8px; text-align: left; }
</style>
</head>
<body>
<header><h1>Dashboard</h1></header>
<div id='revenue-widget'>
<h3>Q4 Revenue</h3>
<table>
<tr><th>Month</th><th>Revenue</th></tr>
<tr><td>October</td><td>$1.2M</td></tr>
<tr><td>November</td><td>$1.5M</td></tr>
<tr><td>December</td><td>$1.8M</td></tr>
</table>
</div>
<div id='other-content'>Other widgets...</div>
</body>
</html>";
// Parse and extract the target element
var parser = new HtmlParser();
var document = parser.ParseDocument(fullPageHtml);
var targetElement = document.QuerySelector("#revenue-widget");
if (targetElement is null)
{
Console.WriteLine("Target element not found.");
return;
}
// Wrap the fragment in a minimal HTML document to preserve styles
string fragmentHtml = $@"
<html>
<head>
<style>
table {{ border-collapse: collapse; width: 100%; }}
td, th {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
</style>
</head>
<body>
{targetElement.OuterHtml}
</body>
</html>";
var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml);
pdf.SaveAs("revenue-widget.pdf");
Imports IronPdf
Imports AngleSharp
Imports AngleSharp.Html.Parser
Dim fullPageHtml As String = "
<html>
<head>
<style>
table { border-collapse: collapse; width: 100%; }
td, th { border: 1px solid #ddd; padding: 8px; text-align: left; }
</style>
</head>
<body>
<header><h1>Dashboard</h1></header>
<div id='revenue-widget'>
<h3>Q4 Revenue</h3>
<table>
<tr><th>Month</th><th>Revenue</th></tr>
<tr><td>October</td><td>$1.2M</td></tr>
<tr><td>November</td><td>$1.5M</td></tr>
<tr><td>December</td><td>$1.8M</td></tr>
</table>
</div>
<div id='other-content'>Other widgets...</div>
</body>
</html>"
' Parse and extract the target element
Dim parser As New HtmlParser()
Dim document = parser.ParseDocument(fullPageHtml)
Dim targetElement = document.QuerySelector("#revenue-widget")
If targetElement Is Nothing Then
Console.WriteLine("Target element not found.")
Return
End If
' Wrap the fragment in a minimal HTML document to preserve styles
Dim fragmentHtml As String = $"
<html>
<head>
<style>
table {{ border-collapse: collapse; width: 100%; }}
td, th {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
</style>
</head>
<body>
{targetElement.OuterHtml}
</body>
</html>"
Dim renderer As New ChromePdfRenderer()
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fragmentHtml)
pdf.SaveAs("revenue-widget.pdf")
핵심 세부사항은 추출된 조각을 관련 <style> 또는 <link> 태그가 포함된 완전한 HTML 문서로 감싸는 것입니다. 이 래퍼 없이는 인라인 스타일이 올바르게 렌더링되지만, 외부 스타일시트 및 상속된 CSS 규칙은 손실됩니다. 이메일 템플릿 미리보기 렌더링 — 템플릿 HTML이 이미 문자열로 저장된 경우 — 이 추출 패턴은 우리가 렌더링된 콘텐츠의 모든 측면을 제어하기 때문에 픽셀 단위로 정확한 결과를 제공합니다.
HtmlAgilityPack를 대안 파서로 사용하는 동일한 패턴도 작동합니다.
using HtmlAgilityPack;
using IronPdf;
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(fullPageHtml);
var targetNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='revenue-widget']");
if (targetNode is null)
{
Console.WriteLine("Target element not found.");
return;
}
string fragmentHtml = $"<html><body>{targetNode.OuterHtml}</body></html>";
var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml);
pdf.SaveAs("revenue-widget-hap.pdf");
using HtmlAgilityPack;
using IronPdf;
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(fullPageHtml);
var targetNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='revenue-widget']");
if (targetNode is null)
{
Console.WriteLine("Target element not found.");
return;
}
string fragmentHtml = $"<html><body>{targetNode.OuterHtml}</body></html>";
var renderer = new ChromePdfRenderer();
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml);
pdf.SaveAs("revenue-widget-hap.pdf");
Imports HtmlAgilityPack
Imports IronPdf
Dim htmlDoc As New HtmlDocument()
htmlDoc.LoadHtml(fullPageHtml)
Dim targetNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='revenue-widget']")
If targetNode Is Nothing Then
Console.WriteLine("Target element not found.")
Return
End If
Dim fragmentHtml As String = $"<html><body>{targetNode.OuterHtml}</body></html>"
Dim renderer As New ChromePdfRenderer()
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fragmentHtml)
pdf.SaveAs("revenue-widget-hap.pdf")
AngleSharp와 HtmlAgilityPack의 선택은 주로 선호도에 달려 있습니다. AngleSharp는 CSS 선택자(QuerySelector)를 사용하여 프론트엔드 개발자의 사고 방식에 맞습니다. HtmlAgilityPack는 XPath(SelectSingleNode)를 사용하여 XML 중심의 .NET 코드베이스에서 더 익숙합니다.
이메일 템플릿 미리보기 렌더링 — 템플릿 HTML이 데이터베이스 또는 콘텐츠 관리 시스템의 문자열로 저장된 경우 — 서버측 추출은 우리가 렌더링된 content의 모든 부면을 제어하기 때문에 픽셀 단위로 정확한 결과를 제공합니다. 두 번째 매개변수를 로컬 자산 디렉토리를 가리키도록 하여, 상대 경로로 참조된 이미지와 스타일시트가 올바르게 해결되도록 보장하면서 RenderHtmlAsPdf(string Html, string BaseUrlOrPath)을 사용할 수 있습니다.
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml, @"C:\templates\assets\");
PdfDocument pdf = renderer.RenderHtmlAsPdf(fragmentHtml, @"C:\templates\assets\");
Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(fragmentHtml, "C:\templates\assets\")
JavaScript DOM 격리와의 trade-off는 서버측 추출이 클라이언트측 렌더링이 필요한 content를 처리할 수 없다는 것입니다 (JavaScript 생성된 요소, SPA 컴포넌트, API에서 가져온 데이터). HTML에 <div id="app"></div>이(가) 포함된 경우, React 또는 Vue 애플리케이션이 런타임에 채우는 경우, 추출된 조각은 비어 있을 것입니다. 이런 경우, 방식 1 또는 4를 사용하세요.
라이브 URL을 렌더링할 때 요소를 어떻게 타겟화하나요?
소스 HTML을 액세스할 수 없는 라이브 URL — 타사 대시보드, 외부 보고서, 호스팅된 애플리케이션 — 에 대해 우리는 RenderUrlAsPdf()를 JavaScript 속성과 WaitFor와 함께 결합하여 페이지 로드 후 특정 섹션을 격리합니다.
이는 대시보드 위젯 내보내기 시나리오입니다: BI 도구는 웹 페이지에 차트 및 표를 렌더링하며, 우리는 이해 관계자 배포를 위해 단일 위젯을 PDF로 내보내야 합니다.
using IronPdf;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;
// Wait for the dashboard widget to render (async content)
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("[data-widget='revenue-chart']", 15000);
// Isolate the widget after it renders
renderer.RenderingOptions.JavaScript = @"
var widget = document.querySelector('[data-widget=""revenue-chart""]');
if (widget) {
// Preserve computed styles by cloning into a clean body
document.body.innerHTML = '';
document.body.appendChild(widget);
}
";
PdfDocument pdf = renderer.RenderUrlAsPdf("https://dashboard.example.com/q4-report");
pdf.SaveAs("revenue-chart-export.pdf");
using IronPdf;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;
// Wait for the dashboard widget to render (async content)
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("[data-widget='revenue-chart']", 15000);
// Isolate the widget after it renders
renderer.RenderingOptions.JavaScript = @"
var widget = document.querySelector('[data-widget=""revenue-chart""]');
if (widget) {
// Preserve computed styles by cloning into a clean body
document.body.innerHTML = '';
document.body.appendChild(widget);
}
";
PdfDocument pdf = renderer.RenderUrlAsPdf("https://dashboard.example.com/q4-report");
pdf.SaveAs("revenue-chart-export.pdf");
Imports IronPdf
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.EnableJavaScript = True
' Wait for the dashboard widget to render (async content)
renderer.RenderingOptions.WaitFor.HtmlQuerySelector("[data-widget='revenue-chart']", 15000)
' Isolate the widget after it renders
renderer.RenderingOptions.JavaScript = "
var widget = document.querySelector('[data-widget=""revenue-chart""]);
if (widget) {
// Preserve computed styles by cloning into a clean body
document.body.innerHTML = '';
document.body.appendChild(widget);
}
"
Dim pdf As PdfDocument = renderer.RenderUrlAsPdf("https://dashboard.example.com/q4-report")
pdf.SaveAs("revenue-chart-export.pdf")
WaitFor.HtmlQuerySelector() 호출은 JavaScript가 실행되기 전에 위젯이 DOM에 존재한다는 것을 보장합니다. 따라서 15초 타임아웃은 느린 API 요청이 대시보드 데이터를 채우는 경우를 수용합니다. JavaScript는 그런 다음 페이지를 위젯만 남도록 제거합니다.
복잡한 CSS 종속성이 있는 페이지의 경우, outerHTML을 복사하는 대신 노드를 이동하는 appendChild 방식은 요소가 CSSOM에서 위치를 유지하기 때문에 계산된 스타일을 더 많이 보존합니다. 접근 방식 1의 innerHTML 교체 방식은 더 단순하지만 상위 선택자에 의존하는 스타일을 잃을 수 있습니다.
대상 페이지가 인증을 요구하는 경우, RenderUrlAsPdf()을 호출하기 전에 렌더러에 대한 쿠키를 설정하세요:
renderer.RenderingOptions.CustomCookies = new Dictionary<string, string>
{
{ "session_id", "abc123" },
{ "auth_token", "bearer-xyz" }
};
renderer.RenderingOptions.CustomCookies = new Dictionary<string, string>
{
{ "session_id", "abc123" },
{ "auth_token", "bearer-xyz" }
};
Imports System.Collections.Generic
renderer.RenderingOptions.CustomCookies = New Dictionary(Of String, String) From {
{"session_id", "abc123"},
{"auth_token", "bearer-xyz"}
}
WaitFor 클래스는 HtmlQuerySelector() 외에도 추가 대기 전략을 제공합니다. 네트워크 요청이 제로 남은 연결로 완료될 때까지 대기하며, 여러 API 끝점에서 데이터를 로드하는 대시보드에 유용합니다. 최대 두 개의 열린 연결을 허용하여, 지속적인 WebSocket 연결 또는 긴 폴링이 있는 페이지를 처리합니다. 페이지가 window.ironpdf.notifyRender() 바로콜 때까지 대기합니다 — 모든 데이터가 로드하고 애니메이션이 완료된 후 렌더링 준비가 되었음을 명확하게 신호할 수 있는 경우 가장 정밀한 옵션입니다.
반복적인 대시보드 내보내기 (예: 매일 밤 PDF 생성하여 이해관계자에게 이메일 배포)를 위해, 타임아웃 예외를 catch하는 retry 루프에 렌더링을 감싸세요. WaitFor이(가) 그 maxWaitTime을(를) 초과하면, IronPDF는 이용 가능한 content로 렌더링을 진행합니다 — 이는 불완전할 수 있습니다. 타임아웃을 증가시키거나 NetworkIdle0()로 전환하여 일반적으로 느린 네트워크에서의 간헐적인 실패를 해결합니다.
모든 4가지 접근 방식의 비교
| 접근 방식 | 최적 대상 | 소스 HTML을 요구함 | JS 의존성 | 복잡성 |
|---|---|---|---|---|
| JavaScript DOM 격리 | 모든 소스에서의 일반적인 요소 추출 | 아니요 | 예 | 중간 |
| CSS 주입 | DOM 변경 없이 섹션 숨기기; @media print 레이아웃 |
부분적 (CustomCssUrl은 RenderHtmlAsPdf 필요) | 아니요 (URL을 위한 JS 주입이 아니라면) | 낮음 |
| 서버 측 조각 추출 | CMS 내용, 저장된 템플릿, 이메일 미리보기 | 예 | 아니요 | 낮음–중간 |
| JS 타겟팅으로 URL 렌더링 | 라이브 대시보드, 타사 페이지, SPA 위젯 | 아니요 | 예 | 중간–높음 |
올바른 접근 방법 선택하기
결정은 두 가지 요소에 따라 달라집니다: 원시 HTML에 액세스할 수 있는지 여부, 및 대상 내용이 렌더링에 JavaScript가 필요한지 여부.
인보이스 항목 추출은 가장 일반적인 사용 사례입니다. 인보이스 HTML이 서버측에서 생성될 때 (Razor 뷰, Handlebars 템플릿, 저장된 HTML 문자열), 방식 3(서버측 추출)은 가장 깨끗한 결과를 제공하고 런타임 부담이 없습니다. #line-items 테이블을 추출하고, 스타일 있는 HTML 셸로 감싼 다음 렌더링합니다.
대시보드 위젯 내보내기는 위젯 내용이 페이지 초기 로드 후 API 호출에 의해 채워지므로 JavaScript 실행이 필요합니다. 방식 1 (JS DOM 격리)은 대시보드가 로컬 또는 인증 뒤에서 실행될 때 이를 처리합니다. 방식 4 (JS 타겟팅으로 URL 렌더링)는 우리는 URL만을 가지고 있는 경우 호스팅된 타사 앱 대시보드가 필요합니다.
양식 섹션 인쇄 — 사용자 검토 또는 규정 준수를 위해 다단계 양식의 특정 섹션을 추출하는 것 — 응용 프로그램이 이미 @media print 규칙을 정의하고 있는 경우 방식 2 (CSS 주입) 또는 서버 측에서 양식 HTML이 조립되는 경우 방식 3에 자연스럽게 매핑됩니다.
이메일 템플릿 미리보기 렌더링 — 보내기 전에 HTML 이메일 템플릿의 PDF 미리보기를 생성하는 것 — 는 순수한 방식 3 시나리오입니다. 템플릿 HTML은 저장된 문자열이고, 외부 자원(이미지, 폰트)는 알려진 URL에 호스팅되며, BaseUrlOrPath 매개변수를 RenderHtmlAsPdf()에 설정하면 모든 상대 경로를 해결합니다.
여러 시나리오를 지원해야 하는 응용 프로그램들은 전략 매개변수를 수용하는 서비스 인터페이스 뒤에 렌더링 논리를 캡슐화합니다:
public enum ElementExtractionStrategy
{
JavaScriptIsolation,
CssInjection,
ServerSideExtraction,
UrlWithJsTargeting
}
public enum ElementExtractionStrategy
{
JavaScriptIsolation,
CssInjection,
ServerSideExtraction,
UrlWithJsTargeting
}
Public Enum ElementExtractionStrategy
JavaScriptIsolation
CssInjection
ServerSideExtraction
UrlWithJsTargeting
End Enum
이를 통해 호출 코드는 렌더러 구성을 복제하지 않고 입력 유형에 따라 적절한 접근 방식을 선택할 수 있습니다.
다음 단계
IronPDF의 HTML 요소 격리는 렌더링-시간 문제이며, 내장 API 기능이 아닙니다. 위의 네 가지 접근은 전체 범위를 포괄합니다 — 서버 측 템플릿 추출 (제로 JS, 가장 깨끗한 출력)에서 라이브 URL 타겟팅 (전체 JS 실행, SPA 및 비동기 콘텐츠를 처리). 비교 테이블은 빠른 참조를 제공하고, 실제 사례는 일반적인 비지니스 요구에 적합한 전략을 매핑합니다.
프로덕션 배포를 위한 몇 가지 추가 고려 사항:
성능: 서버 측 추출 (방식 3)은 JS 실행을 완전히 생략하므로 가장 빠릅니다. JavaScript 기반 접근 (1 및 4)은 페이지 복잡성과 WaitFor 타임아웃에 비례하여 오버헤드를 추가합니다. 배치 처리 (예: 500개 인보이스 PDF 생성)에는 서버측 추출과 Parallel.ForEach 및 여러 ChromePdfRenderer 인스턴스를 사용하여 최고의 처리량을 제공합니다.
디버깅: PDF 출력이 빈칸이거나 내용이 누락된 경우, EnableJavaScript = true을 활성화하고 WaitFor 타임아웃을 늘리세요. 대상 요소가 비동기 데이터에 의존하는 경우, WaitFor.NetworkIdle0()은 고정 RenderDelay보다 더 신뢰할 수 있습니다. 렌더링 옵션 안내서에는 레이아웃에 영향을 미치는 뷰포트 너비 및 페이퍼 맞춤 구성이 포함되어 있습니다.
접근 방식 결합: 전략을 혼합하는 것을 막는 것은 없습니다. 우리는 여러 조각 (한 소스의 헤더, 또 다른 소스의 데이터 테이블, 세 번째 소스의 차트 SVG)에서 구성된 HTML 문서를 빌드하기 위해 서버 측 추출을 사용할 수 있으며, 나중에 단일 PDF로 렌더링된 문서를 조립하여 롯전합니다. RenderHtmlAsPdf(string Html, string BaseUrlOrPath) 메서드는 기본 URL에서 상대 자산 경로를 해결하므로 이기종 소스의 문서를 구성하는 것이 간단합니다.
JavaScript를 PDF로 변환하는 방법을 탐색하여 고급 JS 실행 패턴에서 WaitFor 문서 대기 전략을 확인하고 렌더링 옵션 안내서 및 사용자 지정 JavaScript 코드 예시는 바로 실행할 수 있는 스니펫을 제공합니다.
라이선스 옵션 보기는 $749부터 시작합니다. ChromePdfRenderOptions API 레퍼런스와 WaitFor API 레퍼런스는 모든 속성과 메서드를 문서화합니다.
자주 묻는 질문
C#에서 HTML 요소를 PDF로 변환하는 주요 방법은 무엇인가요?
주요 접근 방식으로는 JS 격리, CSS 숨기기, 서버 측 추출, 실시간 URL 타겟팅 등이 있으며, 이 모든 기능은 IronPDF를 사용하여 구현할 수 있습니다.
HTML을 PDF로 변환하는 과정에서 JS 격리 기능은 어떻게 작동합니까?
JS 격리(JS isolation)는 HTML 문서를 PDF로 변환하기 전에 JavaScript를 실행하여 문서를 동적으로 조작하는 것을 의미합니다. IronPDF를 사용하면 특정 요소만 렌더링되도록 하여 이를 구현할 수 있습니다.
CSS 숨김이란 무엇이며, PDF 변환에서 어떻게 사용됩니까?
CSS 숨기기는 PDF에 표시되어서는 안 되는 요소를 CSS 스타일을 사용하여 숨기는 것을 의미합니다. IronPDF는 개발자가 변환 과정에서 스타일시트나 스타일 규칙을 지정할 수 있도록 지원함으로써 이 기능을 제공합니다.
IronPDF는 PDF 생성을 위해 서버 측에서 특정 HTML 요소를 추출할 수 있습니까?
네, IronPDF는 서버 측에서 특정 HTML 요소를 추출할 수 있어, 웹 페이지의 어느 부분을 PDF로 변환할지 정밀하게 제어할 수 있습니다.
HTML을 PDF로 변환할 때 실시간 URL 타겟팅의 장점은 무엇입니까?
라이브 URL 타겟팅 기능을 통해 IronPDF는 라이브 웹페이지 URL의 요소를 직접 PDF로 변환할 수 있어, 로컬 HTML 파일 없이도 최신 콘텐츠를 정확하게 캡처할 수 있습니다.
IronPDF를 사용하여 웹 페이지의 일부만 PDF로 변환할 수 있습니까?
네, IronPDF는 웹 페이지의 특정 섹션이나 요소를 PDF로 변환하는 기능을 제공하여 관련 콘텐츠에 집중하기 쉽게 해줍니다.
IronPDF는 HTML을 PDF로 변환할 때 동적 콘텐츠를 어떻게 처리할 수 있나요?
IronPDF는 변환 과정에서 JavaScript를 실행하여 동적 콘텐츠를 렌더링할 수 있으므로, 클라이언트 측 스크립트에 의존하는 요소들이 PDF에 정확하게 반영됩니다.

