푸터 콘텐츠로 바로가기
IRONPDF 사용하기

C# 인쇄 폼을 PDF로 -- 완전한 개발자 가이드

병렬 PDF 템플릿의 문제

IronPDF 홈페이지 Razor 보기는 이미 생성되었습니다. 청구서 세부사항 페이지는 항목 내역을 렌더링하고 합계를 계산하며 회사의 스타일시트를 적용합니다. 프로젝트 상태 페이지는 일정이 넘쳐야만 나타나는 조건부 섹션과 함께 작업 분류를 보여줍니다. 급여 명세서 보기는 HR 팀이 올바르게 실행하기 위해 두 주간 작업한 수입, 공제, YTD 수치를 테이블 형식으로 만듭니다. 모든 작업이 완료되면 이해관계자가 "PDF로 다운로드" 버튼을 요청합니다.

표준 응답은 두 번째 템플릿을 작성하는 것입니다: PDF 경로에 동일한 레이아웃을 재현하는 HTML 문자열이나 보고서 정의. 두 번째 템플릿은 처음에는 첫 번째 템플릿의 복사본으로 시작하게 되며, 즉시 차이가 발생합니다. UI 디자이너가 스프린트 14에서 청구서 보기의 테이블 스타일을 업데이트합니다. 아무도 PDF 템플릿을 업데이트하지 않다가 사용자가 다운로드한 청구서가 화면과 다르다고 보고할 때까지. 지금 두 개의 진리 원천이 존재하며, 둘 중 하나는 항상 약간 틀립니다.

클라이언트 측 JavaScript PDF 라이브러리는 복제를 피하지만 서버 렌더링 데이터, 인증된 데이터, 서버 측 계산된 총계, ViewModel에 의해 구동되는 조건부 섹션은 브라우저 측 렌더러로 전달되지 않습니다. 서버에서 무두한 브라우저 자동화는 불안정하며, 인프라 오버헤드를 추가하고, 컨테이너화된 환경에서 예측할 수 없이 실패합니다. 브라우저의 PDF로 인쇄는 수동으로 인쇄하는 사용자는 가능합니다. 이는 프로덕션 애플리케이션의 "PDF 다운로드" 버튼이 아닙니다.

실제 시나리오는 실제 비용을 드러냅니다: 이커머스 관리자가 주문 세부 페이지를 다운로드하여 이행하기, 클라이언트가 프로젝트 관리 도구에서 프로젝트 상태 페이지를 내보내기, 직원이 급여 명세서를 다운로드하기, 디스패처가 경로 요약을 인쇄하기. 모두 PDF가 화면에서 보는 것과 정확히 같은 모양을 갖기를 기대합니다.

해결책: 내용을 복사하지 말고 기존 보기를 렌더링하기

IronPDF는 ASP.NET Core 애플리케이션이 기존 Razor 보기, 즉 브라우저에 제공되는 동일한 보기를 직접 PDF로 렌더링할 수 있도록 합니다. PDF 컨트롤러 액션은 Razor 보기를 표준 보기 엔진을 사용하여 HTML 문자열로 렌더링하고, 그 문자열을 ChromePdfRenderer.RenderHtmlAsPdf()에 전달하여 결과를 파일 다운로드로 반환합니다.

하나의 보기, 두 개의 출력. Razor 보기가 변경되면, PDF 출력도 자동으로 변경되며, 조정 필요 없이. 유지할 병렬 템플릿이 없고, 디버깅할 클라이언트 측 해결 방법이 없으며, 생명유지해야 하는 무두한 브라우저 프로세스가 없습니다. 렌더링은 기존 .NET 애플리케이션 내에서 하나의 NuGet 패키지로 실행됩니다.

실제 적용 사례

1. 이미 존재하는 보기: 새로 추가된 것은 PDF 액션입니다

/invoices/{id}의 청구서 세부사항 페이지는 브라우저를 제공하든 PDF를 생성하든 동일한 데이터 모델을 렌더링합니다. 모델에는 항목 내역, 총계, 고객 세부사항, 회사 브랜드 등이 포함되어 있어, 보기가 필요한 모든 데이터를 제공합니다. 기존 InvoicesController는 해당 모델을 채우는 Details 액션을 갖고 있습니다. PDF 액션은 그것의 형제가 되며, 대체품이 아닙니다.

사용자가 "PDF 다운로드"를 클릭하면, 요청이 /invoices/{id}/pdf에 도달합니다. PDF 액션은 동일한 서비스 호출을 사용하여 동일한 ViewModel을 가져옵니다. 모델은 동일합니다. 다음에 일어나는 것이 다릅니다.

2. HTML 문자열로 렌더링된 Razor 보기

ViewResult를 반환하는 대신, PDF 액션은 보기 렌더링 서비스를 사용하여 Razor 엔진을 보기 파일과 ViewModel에 대하여 호출하여 출력을 문자열로 캡처합니다. 이는 ASP.NET Core에서 일반적이며, 컨트롤러에 주입된 IViewRenderService가 ICompositeViewEngine을 호출하고, 가상 ActionContext에서 보기를 실행하여 렌더링된 HTML을 반환합니다.

렌더링된 HTML 문자열은 완전합니다: 모든 데이터가 채워져 있으며, 모든 조건부 섹션이 해결되었으며, 모든 CSS 클래스 이름이 있습니다. 브라우저가 받을 동일한 HTML이 서버 측에서 캡처된 것입니다.

3. ChromePdfRenderer가 HTML 문자열을 PDF 형식으로 변환

using IronPdf;

[HttpGet("{id}/pdf")]
public async Task<IActionResult> DownloadInvoicePdf(int id)
{
    var model = await _invoiceService.GetInvoiceViewModelAsync(id);

    // Render the existing Razor view to an HTML string
    string html = await _viewRenderer.RenderToStringAsync("Invoices/Details", model);

    var renderer = new ChromePdfRenderer();

    renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
    renderer.RenderingOptions.MarginTop = 15;
    renderer.RenderingOptions.MarginBottom = 15;

    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    return File(pdf.BinaryData, "application/pdf"
        $"Invoice-{model.InvoiceNumber}.pdf");
}
using IronPdf;

[HttpGet("{id}/pdf")]
public async Task<IActionResult> DownloadInvoicePdf(int id)
{
    var model = await _invoiceService.GetInvoiceViewModelAsync(id);

    // Render the existing Razor view to an HTML string
    string html = await _viewRenderer.RenderToStringAsync("Invoices/Details", model);

    var renderer = new ChromePdfRenderer();

    renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
    renderer.RenderingOptions.MarginTop = 15;
    renderer.RenderingOptions.MarginBottom = 15;

    PdfDocument pdf = renderer.RenderHtmlAsPdf(html);

    return File(pdf.BinaryData, "application/pdf"
        $"Invoice-{model.InvoiceNumber}.pdf");
}
Imports IronPdf
Imports Microsoft.AspNetCore.Mvc

<HttpGet("{id}/pdf")>
Public Async Function DownloadInvoicePdf(id As Integer) As Task(Of IActionResult)
    Dim model = Await _invoiceService.GetInvoiceViewModelAsync(id)

    ' Render the existing Razor view to an HTML string
    Dim html As String = Await _viewRenderer.RenderToStringAsync("Invoices/Details", model)

    Dim renderer As New ChromePdfRenderer()

    renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print
    renderer.RenderingOptions.MarginTop = 15
    renderer.RenderingOptions.MarginBottom = 15

    Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(html)

    Return File(pdf.BinaryData, "application/pdf", $"Invoice-{model.InvoiceNumber}.pdf")
End Function
$vbLabelText   $csharpLabel

생성된 PDF 문서

IronPDF 예시 출력 PDF @media print 규칙을 이미 보기의 스타일시트에 적용합니다: 네비게이션 바를 숨기고, 액션 버튼을 억제하고, 인쇄 전용 간격을 적용하며, Razor 보기 자체에서 아무런 변경이 필요하지 않습니다.

Razor 보기가 상대 경로를 통해 스타일시트나 이미지를 참조하는 경우, IronPDF가 렌더링 중에 해당 자산을 올바르게 해결하도록 하기 위해 BaseUrlPath를 RenderHtmlAsPdf()의 두 번째 매개변수로 설정하십시오. 그렇지 않으면, 브라우저 컨텍스트에서 작동하는 CSS 및 이미지 참조가 서버 측 렌더러에서 로드되지 않습니다.

4. 보기를 수정하지 않고 PDF 출력 세부 조정

페이지 번호, 사용자 정의 여백, 문서 제목이 포함된 헤더와 같은 PDF 전용 조정은 렌더러에 구성되며, Razor 보기 내에는 없습니다. 이로 인해 템플릿에서 인쇄 논리를 분리합니다.

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = @"
        <div style='font-size:9px; color:#888; text-align:center; width:100%;'>
            Invoice — Page {page} of {total-pages}
        </div>",
    DrawDividerLine = true
};

PdfDocument pdf = renderer.RenderHtmlAsPdf(html);
var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print;
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = @"
        <div style='font-size:9px; color:#888; text-align:center; width:100%;'>
            Invoice — Page {page} of {total-pages}
        </div>",
    DrawDividerLine = true
};

PdfDocument pdf = renderer.RenderHtmlAsPdf(html);
Imports IronPdf

Dim renderer As New ChromePdfRenderer()

renderer.RenderingOptions.CssMediaType = IronPdf.Rendering.PdfCssMediaType.Print
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4
renderer.RenderingOptions.MarginTop = 20
renderer.RenderingOptions.MarginBottom = 20
renderer.RenderingOptions.HtmlFooter = New HtmlHeaderFooter With {
    .HtmlFragment = "
        <div style='font-size:9px; color:#888; text-align:center; width:100%;'>
            Invoice — Page {page} of {total-pages}
        </div>",
    .DrawDividerLine = True
}

Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(html)
$vbLabelText   $csharpLabel

출력 PDF 파일

사용자 지정을 통해 출력 조정된 PDF 파일 Razor 보기는 브라우저 또는 PDF로 렌더링될지를 알 필요가 없습니다. 컨트롤러 액션이 PDF 전용 구성을 소유하고, 보기는 순수한 표시 템플릿으로 남아 있습니다.

실제 이점

템플릿 복제가 없음. Razor 보기가 문서 레이아웃 및 콘텐츠의 단일 진리의 원천입니다. 브라우저와 PDF가 동일한 파일에서 렌더링되며, 유지할 두 번째 템플릿이 없으며, 수정해야 할 드리프트도 없습니다.

즉각적인 도입. 보기가 이미 존재하는 경우, PDF 내보내기는 컨트롤러 액션이 한 걸음 내에 있습니다. 레이아웃을 재설계하거나, 템플릿을 다시 빌드하거나, 조건부 논리를 다른 렌더링 시스템으로 포팅할 필요가 없습니다.

픽셀 단위로 정확한 출력. Chromium 기반 렌더링으로 인해 CSS 그리드, 플렉스박스, 웹 글꼴 및 미디어 쿼리가 PDF에서 모두 작동합니다. 출력은 브라우저가 생성하는 것과 일치하며, 열화된 근사치가 아닙니다.

인쇄 전용 스타일링. 이미 보기의 스타일시트에 있는 @media print 규칙이 PDF에 나타나는 내용을 제어합니다: 네비게이션 숨기기, 종이에 맞는 열 너비 조정 또는 콘텐츠 재배치. 별도의 템플릿 없이, 별도로 관리할 인라인 인쇄 스타일도 없습니다.

유지보수성. Razor 보기를 업데이트하면, 브라우저 출력과 PDF 출력 모두 그 변경 사항을 반영합니다. 업데이트를 조정할 두 번째 시스템이 없으며, 디자이너의 변경 사항이 브라우저에 도달했지만 PDF에는 도달하지 않는 위험도 없습니다.

문서당 비용 없음. 렌더링은 웹 애플리케이션 내부에서 처리됩니다. 외부 API 호출, 사용 측정 및 다운로드 볼륨에 따라 확장되는 비용 모델이 없습니다.

마무리

Razor 보기가 이미 구축되어 있다면, PDF 내보내기는 새로운 기능이 아니라, 기존 작업의 새로운 전달 경로입니다. 동일한 모델, 동일한 보기, 동일한 스타일링: 추가 항목은 보기의 HTML 출력을 캡처하고 렌더러를 통해 전달한 후 파일로 반환하는 컨트롤러 액션뿐입니다.

그 아키텍처는 코드베이스를 깔끔하게 유지하며 PDF 출력을 브라우저와 영구적으로 동기화 상태로 유지합니다. IronPDF는 ironpdf.com에서 HTML 렌더링부터 저장, 스트리밍 및 문서 조작을 포함하여 C#로 PDF 생성의 전체 수명 주기를 처리합니다. 기존 Razor 보기에 PDF 내보내기를 추가할 준비가 되었다면, 무료 30일 체험판을 시작하여 브라우저 렌더링에 대해 출력을 검증한 후 기능을 제공하십시오.

Curtis Chau
기술 문서 작성자

커티스 차우는 칼턴 대학교에서 컴퓨터 과학 학사 학위를 취득했으며, Node.js, TypeScript, JavaScript, React를 전문으로 하는 프론트엔드 개발자입니다. 직관적이고 미적으로 뛰어난 사용자 인터페이스를 만드는 데 열정을 가진 그는 최신 프레임워크를 활용하고, 잘 구성되고 시각적으로 매력적인 매뉴얼을 제작하는 것을 즐깁니다.

커티스는 개발 분야 외에도 사물 인터넷(IoT)에 깊은 관심을 가지고 있으며, 하드웨어와 소프트웨어를 통합하는 혁신적인 방법을 연구합니다. 여가 시간에는 게임을 즐기거나 디스코드 봇을 만들면서 기술에 대한 애정과 창의성을 결합합니다.

아이언 서포트 팀

저희는 주 5일, 24시간 온라인으로 운영합니다.
채팅
이메일
전화해