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

IronPDF를 사용하여 .NET에서 PDF 파일 생성하는 방법 (C# 튜토리얼)

수동 반복 보고서의 문제점

IronPDF 홈페이지 모든 조직에는 동일한 월요일 아침 의식의 변형이 있습니다: 분석가가 보고 데이터베이스를 열고 지난주와 동일한 쿼리를 실행하고, 수치를 Excel 템플릿에 붙여넣고, 표를 형식화하여 PDF 형식으로 내보낸 후 리더십 배포 목록으로 이메일을 발송하며, 아무런 문제가 발생하지 않기를 바라면서 9시 스탠드업 전에 파일을 보냅니다. 작동할 때는 누군가의 아침에 보이는 세금입니다. 제때 도착하지 못한 보고서가 10시 30분에 도착하거나, 요약 행에 있는 수식 오류로 인해 제대로 작동하지 않는 경우에는 정확히 잘못된 청중에게 보입니다.

기존 BI 도구는 일정 배달을 제공하지만, 일반적으로 대시보드 스크린샷이나 로그인이 필요한 임베디드 링크 형태로 제공합니다. 통근 중인 영업 부사장은 전화에서 BI 도구에 인증하고 싶지 않습니다. 그들은 타블렛에서 읽을 수 있는 PDF 보고서를 원하며, 잠재 고객에게 전달하거나 이사회 회의 전에 인쇄하기를 원합니다. SSRS 일정 구독은 독립적인 PDF 문서를 생성하지만 출력은 2010년의 기업 소프트웨어처럼 보이며, 템플릿 업데이트는 현재 .NET 팀들이 더 이상 전문성을 가지고 있지 않은 보고서 디자이너를 탐색해야 합니다.

처음부터 사용자 정의 파이프라인을 만드는 것이 올바른 아키텍처 적인 해답이지만, 일정 계획자, 쿼리 레이어, 템플릿 엔진, 이메일 발송기를 연결해야 하며, CFO의 주간 P&L 요약이 도착하지 않았을 때 오전 6시에 누군가를 호출하는 각 기능이 잠재적인 고장 지점이 됩니다.

해결책: IronPDF를 렌더링 단계로 사용하는 일정 파이프라인

IronPDF는 보고 데이터베이스에 대해 SQL 쿼리를 실행하고 결과를 HTML 및 CSS 템플릿에 매핑하고 완료된 PDF를 크론 일정에 따라 배포 목록에 이메일로 전송하는 일정 .NET 백그라운드 작업 내에서 렌더링 엔진 역할을 하는 .NET PDF 라이브러리입니다. 수동 단계는 없고, 분석가는 필요하지 않습니다.

Hangfire, Quartz.NET 또는 BackgroundService가 작업을 트리거합니다. ADO.NET 또는 Dapper가 수치를 가져옵니다. HTML 템플릿이 이를 브랜드화된 보고서로 변환합니다. ChromePdfRenderer가 PDF를 생성합니다. 이메일은 아무도 노트북을 열기 전에 전송됩니다. BI 도구 스크린샷이나 SSRS 보고서 정의, 보고서당 API 수수료가 없습니다. 렌더링은 기존 .NET 애플리케이션 내에서 실행되며, 하나의 NuGet 패키지로 외부 프로세스는 없습니다.

실제 적용 사례

1. 크론 일정이 작업을 트리거합니다

각 보고서는 자체 일정이 있습니다: 판매 파이프라인 스냅숏은 매일 오전 6시 30분에 실행되고, P&L 요약은 월요일 오전 6시에 실행되며, 월간 임원 대시보드는 매월 1일에 실행됩니다. 작업은 애플리케이션 시작 시 Hangfire 또는 Quartz.NET에 크론 표현과 함께 등록되거나, 더 단순한 배포를 위해 시간 기반 BackgroundService로 구현됩니다.

작업이 실행되면, 보고 데이터베이스에 연결을 열고 기간별 SQL 쿼리나 저장 프로시저를 실행합니다. 수입 및 지출 수치, KPI 메트릭, 파이프라인 단계 수, 전환율 또는 이행 통계 depending on the report type.

2. 쿼리 결과가 HTML 템플릿으로 전송됩니다

쿼리 결과는 모델이나 사전에 매핑되고 HTML 템플릿에 삽입됩니다. 템플릿은 보고서의 외관에 대해 모든 것을 정의합니다: 회사 로고가 있는 브랜드화된 헤더, 기간 레이블("2025년 3월 27일 주간"), 상단에 요약 지표 카드, 교차 행 색상이 있는 하나 이상의 데이터 테이블 및 생성 시간과 기밀성 고지 사항이 있는 바닥글입니다. 이 IronPDF 예제 코드는 이를 정확히 보여주는 PDF 파일을 생성합니다.

using Dapper;
using IronPdf;
using System.Data.SqlClient;

var reportData = new List<dynamic>();

using (var conn = new SqlConnection(_connectionString))
{
    reportData = (await conn.QueryAsync(@"
        SELECT Region, SUM(Revenue) AS Revenue, COUNT(OrderId) AS Orders
        FROM SalesOrders
        WHERE OrderDate >= @Start AND OrderDate < @End
        GROUP BY Region ORDER BY Revenue DESC",
        new { Start = weekStart, End = weekStart.AddDays(7) })).ToList();
}

var tableRows = string.Concat(reportData.Select(r =>
    $"<tr><td>{r.Region}</td><td>{r.Revenue:C}</td><td>{r.Orders}</td></tr>"));
string html = (await File.ReadAllTextAsync("Templates/sales-weekly.html"))
    .Replace("{{Period}}", $"Week of {weekStart:MMMM d, yyyy}")
    .Replace("{{TotalRevenue}}", reportData.Sum(r => (decimal)r.Revenue).ToString("C"))
    .Replace("{{TableRows}}", tableRows);

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;

renderer.RenderingOptions.MarginTop = 20;

renderer.RenderingOptions.MarginBottom = 20;

PdfDocument report = renderer.RenderHtmlAsPdf(html);
using Dapper;
using IronPdf;
using System.Data.SqlClient;

var reportData = new List<dynamic>();

using (var conn = new SqlConnection(_connectionString))
{
    reportData = (await conn.QueryAsync(@"
        SELECT Region, SUM(Revenue) AS Revenue, COUNT(OrderId) AS Orders
        FROM SalesOrders
        WHERE OrderDate >= @Start AND OrderDate < @End
        GROUP BY Region ORDER BY Revenue DESC",
        new { Start = weekStart, End = weekStart.AddDays(7) })).ToList();
}

var tableRows = string.Concat(reportData.Select(r =>
    $"<tr><td>{r.Region}</td><td>{r.Revenue:C}</td><td>{r.Orders}</td></tr>"));
string html = (await File.ReadAllTextAsync("Templates/sales-weekly.html"))
    .Replace("{{Period}}", $"Week of {weekStart:MMMM d, yyyy}")
    .Replace("{{TotalRevenue}}", reportData.Sum(r => (decimal)r.Revenue).ToString("C"))
    .Replace("{{TableRows}}", tableRows);

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;

renderer.RenderingOptions.MarginTop = 20;

renderer.RenderingOptions.MarginBottom = 20;

PdfDocument report = renderer.RenderHtmlAsPdf(html);
Imports Dapper
Imports IronPdf
Imports System.Data.SqlClient

Dim reportData As New List(Of Object)()

Using conn As New SqlConnection(_connectionString)
    reportData = (Await conn.QueryAsync("
        SELECT Region, SUM(Revenue) AS Revenue, COUNT(OrderId) AS Orders
        FROM SalesOrders
        WHERE OrderDate >= @Start AND OrderDate < @End
        GROUP BY Region ORDER BY Revenue DESC",
        New With {.Start = weekStart, .End = weekStart.AddDays(7)})).ToList()
End Using

Dim tableRows = String.Concat(reportData.Select(Function(r) 
    $"<tr><td>{r.Region}</td><td>{r.Revenue:C}</td><td>{r.Orders}</td></tr>"))

Dim html As String = (Await File.ReadAllTextAsync("Templates/sales-weekly.html")) _
    .Replace("{{Period}}", $"Week of {weekStart:MMMM d, yyyy}") _
    .Replace("{{TotalRevenue}}", reportData.Sum(Function(r) CType(r.Revenue, Decimal)).ToString("C")) _
    .Replace("{{TableRows}}", tableRows)

Dim renderer As New ChromePdfRenderer()

renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter

renderer.RenderingOptions.MarginTop = 20

renderer.RenderingOptions.MarginBottom = 20

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

생성된 PDF 파일

IronPDF 예제 생성 PDF

3. PDF 문서가 이메일로 발송 및 보관됨

using System.Net.Mail;
using System.IO;

var recipients = await _configService.GetDistributionListAsync("sales-weekly");

string subject = $"Weekly Sales Report — {weekStart:MMM d, yyyy}";

using var stream = new MemoryStream(report.BinaryData);

using var attachment = new Attachment(stream, $"Sales-Report-{weekStart:yyyyMMdd}.pdf", "application/pdf");

var message = new MailMessage
{
    From = new MailAddress("reports@yourcompany.com"),
    Subject = subject,
    Body = $"Please find the weekly sales report for the week of {weekStart:MMMM d, yyyy} attached."
};

foreach (var recipient in recipients)
    message.To.Add(recipient);

message.Attachments.Add(attachment);

using var smtp = new SmtpClient("smtp.yourprovider.com");

await smtp.SendMailAsync(message);

// Archive to blob storage for historical access

string archivePath = $"reports/sales-weekly/{weekStart:yyyy/MM/dd}.pdf";

report.SaveAs(archivePath);
using System.Net.Mail;
using System.IO;

var recipients = await _configService.GetDistributionListAsync("sales-weekly");

string subject = $"Weekly Sales Report — {weekStart:MMM d, yyyy}";

using var stream = new MemoryStream(report.BinaryData);

using var attachment = new Attachment(stream, $"Sales-Report-{weekStart:yyyyMMdd}.pdf", "application/pdf");

var message = new MailMessage
{
    From = new MailAddress("reports@yourcompany.com"),
    Subject = subject,
    Body = $"Please find the weekly sales report for the week of {weekStart:MMMM d, yyyy} attached."
};

foreach (var recipient in recipients)
    message.To.Add(recipient);

message.Attachments.Add(attachment);

using var smtp = new SmtpClient("smtp.yourprovider.com");

await smtp.SendMailAsync(message);

// Archive to blob storage for historical access

string archivePath = $"reports/sales-weekly/{weekStart:yyyy/MM/dd}.pdf";

report.SaveAs(archivePath);
Imports System.Net.Mail
Imports System.IO

Dim recipients = Await _configService.GetDistributionListAsync("sales-weekly")

Dim subject As String = $"Weekly Sales Report — {weekStart:MMM d, yyyy}"

Using stream As New MemoryStream(report.BinaryData)
    Using attachment As New Attachment(stream, $"Sales-Report-{weekStart:yyyyMMdd}.pdf", "application/pdf")
        Dim message As New MailMessage With {
            .From = New MailAddress("reports@yourcompany.com"),
            .Subject = subject,
            .Body = $"Please find the weekly sales report for the week of {weekStart:MMMM d, yyyy} attached."
        }

        For Each recipient In recipients
            message.To.Add(recipient)
        Next

        message.Attachments.Add(attachment)

        Using smtp As New SmtpClient("smtp.yourprovider.com")
            Await smtp.SendMailAsync(message)
        End Using
    End Using
End Using

' Archive to blob storage for historical access

Dim archivePath As String = $"reports/sales-weekly/{weekStart:yyyy/MM/dd}.pdf"

report.SaveAs(archivePath)
$vbLabelText   $csharpLabel

첨부된 PDF가 있는 예시 이메일

Scheduled Pdf Reports Email 3 related to 첨부된 PDF가 있는 예시 이메일 배포 목록은 하드코딩 없이 구성 테이블에서 가져옵니다. 따라서 주간 판매 보고서에 새 수신자를 추가하는 것이 코드 변경이나 재배포를 필요로 하지 않습니다. 각 보관된 PDF는 보고서 이름과 날짜로 키가 지정되어 공유 드라이브 또는 내부 포털에서 액세스할 수 있는 과거 보고서의 라이브러리를 구축합니다.

작업은 쿼리 기간, 렌더링 시간, 이메일 전송 결과 및 모든 오류에 대한 실행 상태를 구조화된 로깅에 기록합니다. 작업이 완료되지 않으면 경고가 발생하여 문제가 나타나는 첫 번째 신호가 경영진이 보고서가 도착하지 않았다고 묻는 것이 아닙니다.

실제 이점

수동 노력이 제로입니다. 보고서는 일정에 따라 생성되고 자체적으로 전달됩니다. 분석가는 월요일 아침에 쿼리를 실행하지 않으며, 아무도 표를 형식화하지 않으며, 아무도 전송 버튼을 누르는 것을 기억하지 않습니다. 프로세스는 제대로 작동할 때는 보이지 않으며, 작동하지 않을 때는 경고할 수 있습니다.

시간 엄수. 보고서는 작업 시작 전에 받은 편지함에 도착합니다. 사무실로 들어가는 길에 이메일을 확인하는 CFO는 회의에 필요하기 전에 P&L 수치를 확인할 수 있으며, 분석가가 커피를 마신 후가 아닙니다.

오프라인 가독성. 임원들이 비행기에서 읽거나 이사회 회의를 위해 인쇄하거나 수신자가 어떤 시스템에 로그인할 필요 없이 전달할 수 있는 독립적인 PDF. 대시보드 URL도 없고, 인증 벽도 없으며, "이 링크는 기업 네트워크에서만 작동합니다."라는 메시지도 없습니다.

브랜드 일관성. 파이프라인에서 생성된 모든 보고서는 동일한 승인된 HTML 및 CSS 템플릿을 사용하며, 동일한 로고 배치, 타이포그래피, 테이블 스타일링 및 조직에서 배포하는 모든 주간, 일일 및 월간 보고서의 바닥글 언어를 사용합니다.

역사 기록. 각 생성된 PDF는 구조화된 경로로 날짜별로 저장됩니다. 6개월치 주간 판매 보고서는 트렌드 검토 또는 감사 요청을 위한 폴더 브라우즈 거리이며 BI 도구 쿼리는 필요하지 않습니다.

보고서당 비용 없음. 렌더링은 기존 .NET 애플리케이션 내에서 In-Process로 실행됩니다. 일정 배달을 위한 BI 도구 고급 좌석 구매가 필요하지 않으며, 보고서 볼륨이나 배포 목록 크기에 따라 확장되는 보고 API 계량도 필요하지 않습니다.

마무리

작업 시작 전에 안정적으로 도착하는 반복 보고서는 결정이 만들어지는 방법에 영향을 미치는 작은 운영적 세부사항입니다. 아침 스탠드업 전에 숫자가 있는 리더십은 분석가가 스프레드시트를 형식화할 때까지 기다리는 리더십과 다른 종류의 회의를 운영합니다.

그 장소로 가는 파이프라인 - 일정 계획자, 쿼리, 템플릿, 렌더링, 이메일 - 은 단일 .NET 백그라운드 작업 내에 편안하게 맞춰져 있습니다. IronPDF는 C#에서 ironpdf.com의 PDF 생성 전체 수명 주기를 관리하며, HTML 템플릿 렌더링에서 저장, 스트리밍, 문서 조작까지 포함됩니다. 수동 보고 프로세스를 스스로 실행되는 것으로 교체할 준비가 되었다면, 무료 30일 체험을 시작하십시오. 이는 스케줄 파이프라인을 연결하고 데이터베이스에 연결하고 올바른 PDF가 제때 올바른 받은 편지함에 도착하는지 확인하기에 충분한 시간입니다.

Curtis Chau
기술 문서 작성자

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

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

아이언 서포트 팀

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