ASP.NET Core에서 PDF 파일의 데이터 읽는 방법
수동 슬립 및 라벨 생성의 문제
하루에 500개의 주문을 두 번의 교대로 처리하는 창고 작업 현장에서, 피킹 및 배송 주기는 마찰을 허용하지 않습니다. OMS 스크린에서 하나씩 출고 명세서를 인쇄하고 주문 세부 사항을 레이블 템플릿에 수작업으로 복사하거나 .NET 백엔드와 깔끔하게 통합되지 않는 라벨 소프트웨어 도구를 기다리는 등의 과정은 각 주문당 초과 시간을 추가하며 이는 교대 종료 시 실질적인 처리량 손실로 이어집니다.
시간 비용과 함께 오류 위험도 증가합니다. 운송 주소가 주문 기록에서 직접 가져오는 것이 아니라 수작업으로 전사될 때, 배송에 문제가 발생합니다. 잘못된 주소로 인한 반송 배송은 대부분의 개인 주문에 대한 마진보다 인건비와 운송료에서 더 많이 들게 됩니다. 운송사 제공 라벨 도구는 주소 문제를 해결하지만, 창고 바닥에서 상자 내용을 봉인하기 전에 확인이 필요한 항목화된 출고 명세서를 생성하지 않으며, 여전히 다른 시스템에서 별도의 인쇄가 필요합니다.
배치 인쇄는 대부분의 접근 방식에서 완전히 붕괴됩니다. 200개의 주문을 발행하는 운영 관리자에게는 교대 시작 시 출고 명세서가 준비되어 있어야 하며, 웹 인터페이스를 통해 주문당 새 인쇄 대화 상자가 열리지 않고 하나씩 생성되어야 합니다. 형식은 Razor, 잉크젯 또는 열전사 형식 모두에서 창고의 모든 프린터에서 작동해야 하며, 창고 작업대에 전용 드라이버를 설치할 필요가 없어야 합니다.
시나리오는 일관됩니다: 하루에 수백 개의 주문을 포장하는 직접 소비자 브랜드, 팔레트 배송을 위한 멀티라인 슬립을 생성하는 B2B 유통업체, 조립 라인 작업자를 위한 항목 체크리스트를 인쇄하는 구독 상자 서비스, 여러 상인 클라이언트를 대신하여 브랜드 슬립을 생성하는 3PL 제공업체 - 서로 다른 로고와 반품 주소가 있습니다.
해결책: IronPDF 라이브러리로 주문 데이터에서 직접 생성된 인쇄 가능 PDF 파일
IronPDF는 .NET OMS가 주문 데이터에서 직접 인쇄 준비 PDF 출고 명세서 및 레이블을 생성할 수 있게 합니다. 주문이 '포장 준비됨' 단계에 들어서면, 애플리케이션은 품목, 배송 주소, 운송사 정보 및 특별 지침을 가져와 대상 용지 형식에 맞게 크기 조정된 HTML 템플릿에 입력하고, ChromePdfRenderer가 PDF를 생성합니다.
창고 직원은 표준 또는 열 전사 프린터에서 출력물 하나당 한 주문 또는 전체 피킹 웨이브를 커버하는 배치된 다중 페이지 PDF를 인쇄합니다. 설치할 전용 라벨 소프트웨어가 없고, 수동 데이터 입력 단계도 없으며, 라벨당 API 요금도 없습니다. 렌더링은 외부 프로세스 없이 기존 .NET 애플리케이션 내에서 단일 NuGet 패키지로 실행됩니다.
실제 적용 사례
1. 주문이 포장 대기열에 들어감
주문은 결제 확인 후 자동으로 '포장 준비됨' 상태로 전환되거나 창고 리드가 OMS에서 피킹 웨이브를 발행할 때 전환됩니다. 애플리케이션은 배치의 모든 주문에 대해 주문 데이터베이스를 쿼리하고, 품목 번호, 제품명, 수량과 같은 품목 정보를 가져옵니다. 배송 주소; 운송사와 서비스 레벨; 가능한 경우 미리 할당된 추적 번호; 및 특별 처리 지침이나 선물 메시지.
웨이브의 각 주문은 자체 데이터로 채워진 동일한 HTML 템플릿에서 생성된 PDF를 받습니다. 200개의 주문 웨이브에 대해 200번의 렌더링이 이루어지며, 각 렌더링은 밀리초 단위로 수행되어 인쇄 작업을 위해 결과가 하나의 문서로 병합됩니다.
2. 템플릿은 두 가지 형식 유형을 모두 처리
출고 명세서 템플릿(편지 또는 A4)에는 각 품목을 검증하기 위한 체크박스가 있는 품목 테이블, 주문 번호, 배송 주소 및 스캔 가능한 주문 ID가 포함되어 있습니다. 레이블 템플릿은 4×6 인치(표준 열 전사 레이블 크기)를 목표로 하며, 배송 및 반송 주소, 운송 바코드 영역 및 발송 중량이 포함됩니다.
두 가지 템플릿 모두 애플리케이션 팀이 소유한 HTML 및 CSS 파일입니다. 출고 명세서에 새 열을 추가하거나, 3PL 슬립의 상인 로고를 변경하거나, 새로운 열 전사 프린터 모델에 대한 레이블 여백을 조정하는 것은 템플릿 파일 변경이며, 재컴파일이나 비즈니스 로직 재배포가 필요 없습니다.
예시 HTML 파일 템플릿

3. 배치 렌더링되고 하나의 인쇄 작업으로 병합
using IronPdf;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
string slipTemplate = await File.ReadAllTextAsync("Templates/packing-slip.html");
var slipPdfs = new List<PdfDocument>();
foreach (var order in pickWave)
{
string lineItemRows = string.Concat(order.Items.Select(i =>
$"<tr><td><input type='checkbox'></td><td>{i.Sku}</td><td>{i.ProductName}</td><td>{i.Quantity}</td></tr>"));
string html = slipTemplate
.Replace("{{OrderNumber}}", order.OrderNumber)
.Replace("{{ShipTo}}", order.ShippingAddress.Formatted)
.Replace("{{LineItems}}", lineItemRows)
.Replace("{{SpecialInstructions}}", order.SpecialInstructions ?? "None");
slipPdfs.Add(renderer.RenderHtmlAsPdf(html));
}
PdfDocument waveBatch = PdfDocument.Merge(slipPdfs);
waveBatch.SaveAs($"wave-{pickWave.WaveId}-slips.pdf");
using IronPdf;
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
string slipTemplate = await File.ReadAllTextAsync("Templates/packing-slip.html");
var slipPdfs = new List<PdfDocument>();
foreach (var order in pickWave)
{
string lineItemRows = string.Concat(order.Items.Select(i =>
$"<tr><td><input type='checkbox'></td><td>{i.Sku}</td><td>{i.ProductName}</td><td>{i.Quantity}</td></tr>"));
string html = slipTemplate
.Replace("{{OrderNumber}}", order.OrderNumber)
.Replace("{{ShipTo}}", order.ShippingAddress.Formatted)
.Replace("{{LineItems}}", lineItemRows)
.Replace("{{SpecialInstructions}}", order.SpecialInstructions ?? "None");
slipPdfs.Add(renderer.RenderHtmlAsPdf(html));
}
PdfDocument waveBatch = PdfDocument.Merge(slipPdfs);
waveBatch.SaveAs($"wave-{pickWave.WaveId}-slips.pdf");
Imports IronPdf
Imports System.IO
Dim renderer As New ChromePdfRenderer()
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter
renderer.RenderingOptions.MarginTop = 10
renderer.RenderingOptions.MarginBottom = 10
Dim slipTemplate As String = Await File.ReadAllTextAsync("Templates/packing-slip.html")
Dim slipPdfs As New List(Of PdfDocument)()
For Each order In pickWave
Dim lineItemRows As String = String.Concat(order.Items.Select(Function(i) $"<tr><td><input type='checkbox'></td><td>{i.Sku}</td><td>{i.ProductName}</td><td>{i.Quantity}</td></tr>"))
Dim html As String = slipTemplate _
.Replace("{{OrderNumber}}", order.OrderNumber) _
.Replace("{{ShipTo}}", order.ShippingAddress.Formatted) _
.Replace("{{LineItems}}", lineItemRows) _
.Replace("{{SpecialInstructions}}", If(order.SpecialInstructions, "None"))
slipPdfs.Add(renderer.RenderHtmlAsPdf(html))
Next
Dim waveBatch As PdfDocument = PdfDocument.Merge(slipPdfs)
waveBatch.SaveAs($"wave-{pickWave.WaveId}-slips.pdf")
예제 출력 PDF 문서
PdfDocument.Merge()는 모든 개별 슬립을 하나의 다중 페이지 PDF로 결합합니다. 창고 팀은 전체 웨이브에 대해 하나의 인쇄 작업을 전송하며, 워크스테이션에서 한 번의 클릭으로 한 출력 트레이에서 수집합니다.
4. 렌더링 옵션을 통해 레이블 크기 설정
using IronPdf;
var renderer = new ChromePdfRenderer();
// 4x6 inch thermal label — 288 x 432 points
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Custom;
renderer.RenderingOptions.CustomPaperWidth = 4;
renderer.RenderingOptions.CustomPaperHeight = 6;
renderer.RenderingOptions.MarginTop = 0;
renderer.RenderingOptions.MarginBottom = 0;
renderer.RenderingOptions.MarginLeft = 0;
renderer.RenderingOptions.MarginRight = 0;
string labelHtml = $@"
<div style='font-family:monospace; padding:4px;'>
<p><strong>SHIP TO:</strong><br/>{order.ShippingAddress.Formatted}</p>
<p><strong>FROM:</strong><br/>{order.ReturnAddress.Formatted}</p>
<p>Order: {order.OrderNumber} | Weight: {order.WeightLbs:F1} lbs</p>
<p>Carrier: {order.CarrierName} — {order.ServiceLevel}</p>
</div>";
PdfDocument label = renderer.RenderHtmlAsPdf(labelHtml);
label.SaveAs($"label-{order.OrderNumber}.pdf");
using IronPdf;
var renderer = new ChromePdfRenderer();
// 4x6 inch thermal label — 288 x 432 points
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Custom;
renderer.RenderingOptions.CustomPaperWidth = 4;
renderer.RenderingOptions.CustomPaperHeight = 6;
renderer.RenderingOptions.MarginTop = 0;
renderer.RenderingOptions.MarginBottom = 0;
renderer.RenderingOptions.MarginLeft = 0;
renderer.RenderingOptions.MarginRight = 0;
string labelHtml = $@"
<div style='font-family:monospace; padding:4px;'>
<p><strong>SHIP TO:</strong><br/>{order.ShippingAddress.Formatted}</p>
<p><strong>FROM:</strong><br/>{order.ReturnAddress.Formatted}</p>
<p>Order: {order.OrderNumber} | Weight: {order.WeightLbs:F1} lbs</p>
<p>Carrier: {order.CarrierName} — {order.ServiceLevel}</p>
</div>";
PdfDocument label = renderer.RenderHtmlAsPdf(labelHtml);
label.SaveAs($"label-{order.OrderNumber}.pdf");
Imports IronPdf
Dim renderer As New ChromePdfRenderer()
' 4x6 inch thermal label — 288 x 432 points
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Custom
renderer.RenderingOptions.CustomPaperWidth = 4
renderer.RenderingOptions.CustomPaperHeight = 6
renderer.RenderingOptions.MarginTop = 0
renderer.RenderingOptions.MarginBottom = 0
renderer.RenderingOptions.MarginLeft = 0
renderer.RenderingOptions.MarginRight = 0
Dim labelHtml As String = $"
<div style='font-family:monospace; padding:4px;'>
<p><strong>SHIP TO:</strong><br/>{order.ShippingAddress.Formatted}</p>
<p><strong>FROM:</strong><br/>{order.ReturnAddress.Formatted}</p>
<p>Order: {order.OrderNumber} | Weight: {order.WeightLbs:F1} lbs</p>
<p>Carrier: {order.CarrierName} — {order.ServiceLevel}</p>
</div>"
Dim label As PdfDocument = renderer.RenderHtmlAsPdf(labelHtml)
label.SaveAs($"label-{order.OrderNumber}.pdf")
예제 출력 PDF 파일 및 배송 레이블
제로 여백은 모든 공간을 라벨 가장자리까지 제어하는 것을 가능하게 하며, 이는 운송사 바코드 영역이 용지 경계에서 몇 밀리미터 이내로 도달해야 하는 열 전사 라벨 형식에 매우 중요합니다.
실제 이점
배치 효율성. 전체 피킹 웨이브를 하나의 PDF로 병합하면 수백 개의 인쇄 작업 대신 하나의 인쇄 작업이 됩니다. 창고 직원이 주문당 개별 인쇄 대화 상자를 로드하는 데 기다릴 필요가 없을 때 교대 시작 시간이 줄어듭니다.
인쇄 호환성. PDF는 별도의 드라이버나 창고 작업대에 설치된 전용 레이블 소프트웨어 없이도 Razor, 잉크젯, 열 전사 프린터에서 일관되게 렌더링됩니다. OMS가 생성하는 형식은 바닥의 모든 프린터에서 수용하는 형식입니다.
정확성. 품목과 주소는 주문 기록에서 직접 가져옵니다. 수동 전사 단계가 없으며, 포장기에게 도달하기 전에 주소나 SKU가 잘못 입력될 경로가 없습니다.
유연한 형식. 동일한 생성 파이프라인이 HTML 템플릿과 페이지 크기 옵션을 교체하여 레터 크기의 출고 명세서와 4×6 레이블을 생성합니다. 별도의 도구, 별도의 통합이 없습니다.
멀티 테넌트 브랜딩. 3PL 제공자는 템플릿 경로에 merchantId 변수를 채우고, 특정 상인 HTML 파일을 로드합니다. 각 클라이언트의 슬립에는 자체 로고, 반송 주소 및 브랜드 색상이 동일한 파이프라인을 통해 추가 논리 없이 적용됩니다.
라벨당 비용 없음. 렌더링은 프로세스 내에서 실행됩니다. 각 인쇄당 호출되는 별도의 라벨 API가 없으며, 사용량 측정이나 낮은 볼륨보다 고볼륨 시프트에 더 비용이 많이 드는 가격 모델도 없습니다.
마무리
피킹-팩-쉬프 주기는 처리량 문제입니다. 주문이 포장 대기열에 들어오고 라벨이 붙은 박스가 도크를 떠나는 사이의 모든 수동 단계는 제약이며, 출고 명세서 및 라벨 생성은 OMS 자체 내에서 자동화하기 간단한 작업입니다.
형식당 하나의 HTML 템플릿, 주문당 한 번의 렌더링 호출, 웨이브당 하나의 병합된 PDF, 시프트당 하나의 인쇄 작업. IronPDF는 C#에서 PDF 생성의 전체 수명을 ironpdf.com에서 처리하며, HTML 렌더링 템플릿부터 문서를 병합, 저장 및 스트리밍합니다. 주문 데이터와 인쇄 설정에 대해 파이프라인을 테스트할 준비가 되었다면, 무료 30일 체험을 시작하고 한 주가 지나기 전에 작동하는 배치 슬립 생성기를 실행하세요.




