본문으로 건너뛰기

syn.$p

개요

syn.$p는 PDF 문서를 이용한 고급 인쇄 기능을 제공하는 라이브러리입니다. 웹 페이지 내용을 PDF로 변환, 인쇄 미리보기, 다양한 인쇄 옵션 설정, 템플릿 기반 문서 생성 등의 기능을 통해 전문적인 인쇄 솔루션을 제공합니다.

주요 기능

PDF 생성 및 인쇄

syn.$p.printPage(options)

현재 페이지 또는 지정된 요소를 PDF로 변환하여 인쇄합니다.

구문

syn.$p.printPage(options)

매개변수

  • options (Object): 인쇄 설정 객체
    • selector (String): 인쇄할 요소 선택자 (기본값: 'body')
    • filename (String): PDF 파일명 (기본값: 'document.pdf')
    • orientation (String): 용지 방향 ('portrait' 또는 'landscape')
    • format (String): 용지 크기 ('A4', 'A3', 'letter' 등)
    • margins (Object): 여백 설정
    • scale (Number): 크기 조정 (0.1 ~ 2.0)
    • printBackground (Boolean): 배경 인쇄 여부

예제

// 기본 페이지 인쇄
syn.$p.printPage({
filename: '보고서.pdf',
orientation: 'portrait',
format: 'A4'
});

// 특정 요소만 인쇄
syn.$p.printPage({
selector: '#reportContent',
filename: '월간보고서.pdf',
orientation: 'landscape',
format: 'A3',
margins: {
top: '20mm',
right: '15mm',
bottom: '20mm',
left: '15mm'
},
scale: 0.8,
printBackground: true
});

// 테이블 인쇄
syn.$p.printPage({
selector: '.data-table',
filename: '데이터목록.pdf',
format: 'A4',
margins: {
top: '10mm',
right: '10mm',
bottom: '10mm',
left: '10mm'
}
});

syn.$p.printHtml(html, options)

HTML 문자열을 PDF로 변환하여 인쇄합니다.

구문

syn.$p.printHtml(html, options)

매개변수

  • html (String): 인쇄할 HTML 문자열
  • options (Object): 인쇄 설정 객체

예제

var reportHtml = `
<div style="padding: 20px;">
<h1>매출 보고서</h1>
<p>기간: 2023년 12월</p>
<table border="1" style="width: 100%; border-collapse: collapse;">
<thead>
<tr>
<th>날짜</th>
<th>상품명</th>
<th>수량</th>
<th>금액</th>
</tr>
</thead>
<tbody>
<tr>
<td>2023-12-01</td>
<td>제품A</td>
<td>10</td>
<td>100,000원</td>
</tr>
</tbody>
</table>
</div>
`;

syn.$p.printHtml(reportHtml, {
filename: '매출보고서.pdf',
orientation: 'portrait',
format: 'A4',
margins: {
top: '25mm',
right: '20mm',
bottom: '25mm',
left: '20mm'
}
});

PDF 미리보기

syn.$p.previewPdf(options)

PDF 인쇄 미리보기를 표시합니다.

구문

syn.$p.previewPdf(options)

예제

// 미리보기 팝업 표시
syn.$p.previewPdf({
selector: '#invoiceContent',
title: '송장 미리보기',
orientation: 'portrait',
format: 'A4',
onConfirm: function() {
// 사용자가 인쇄 확인을 클릭했을 때
syn.$p.printPage({
selector: '#invoiceContent',
filename: '송장.pdf'
});
},
onCancel: function() {
console.log('인쇄 취소됨');
}
});

템플릿 기반 문서 생성

syn.$p.generateFromTemplate(templateId, data, options)

템플릿을 사용하여 PDF 문서를 생성합니다.

구문

syn.$p.generateFromTemplate(templateId, data, options)

매개변수

  • templateId (String): 템플릿 ID
  • data (Object): 템플릿에 바인딩할 데이터
  • options (Object): 생성 옵션

예제

// HTML 템플릿 정의
var invoiceTemplate = `
<div class="invoice">
<div class="header">
<h1>{{companyName}}</h1>
<p>{{companyAddress}}</p>
</div>
<div class="customer">
<h3>청구서</h3>
<p>고객명: {{customerName}}</p>
<p>주소: {{customerAddress}}</p>
<p>발행일: {{issueDate}}</p>
</div>
<table class="items">
<thead>
<tr>
<th>항목</th>
<th>수량</th>
<th>단가</th>
<th>금액</th>
</tr>
</thead>
<tbody>
{{#items}}
<tr>
<td>{{name}}</td>
<td>{{quantity}}</td>
<td>{{unitPrice}}</td>
<td>{{totalPrice}}</td>
</tr>
{{/items}}
</tbody>
</table>
<div class="total">
<h3>총 금액: {{totalAmount}}원</h3>
</div>
</div>
<style>
.invoice { font-family: '맑은 고딕', Arial; padding: 20px; }
.header { text-align: center; margin-bottom: 30px; }
.customer { margin-bottom: 20px; }
.items { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
.items th, .items td { border: 1px solid #ddd; padding: 8px; text-align: left; }
.items th { background-color: #f2f2f2; }
.total { text-align: right; }
</style>
`;

// 데이터 바인딩 및 PDF 생성
var invoiceData = {
companyName: '(주)한국소프트웨어',
companyAddress: '서울특별시 강남구 테헤란로 123',
customerName: '홍길동',
customerAddress: '서울특별시 송파구 잠실로 456',
issueDate: '2023년 12월 25일',
items: [
{ name: '웹 개발 서비스', quantity: 1, unitPrice: '1,000,000', totalPrice: '1,000,000' },
{ name: '유지보수 서비스', quantity: 12, unitPrice: '100,000', totalPrice: '1,200,000' }
],
totalAmount: '2,200,000'
};

syn.$p.generateFromTemplate(invoiceTemplate, invoiceData, {
filename: '청구서_' + invoiceData.customerName + '.pdf',
orientation: 'portrait',
format: 'A4'
});

일괄 인쇄

syn.$p.printBatch(documents, options)

여러 문서를 일괄적으로 PDF로 생성합니다.

구문

syn.$p.printBatch(documents, options)

매개변수

  • documents (Array): 인쇄할 문서 배열
  • options (Object): 일괄 인쇄 옵션

예제

var documents = [
{
selector: '#report1',
filename: '보고서1.pdf'
},
{
selector: '#report2',
filename: '보고서2.pdf'
},
{
html: '<h1>추가 문서</h1><p>내용...</p>',
filename: '추가문서.pdf'
}
];

syn.$p.printBatch(documents, {
format: 'A4',
orientation: 'portrait',
onProgress: function(current, total) {
console.log('진행률: ' + (current / total * 100) + '%');
},
onComplete: function(results) {
console.log('일괄 인쇄 완료:', results);

// 압축파일로 다운로드
syn.$p.downloadAsZip(results, '보고서모음.zip');
}
});

실전 활용 예제

1. 동적 보고서 생성 시스템

let $reportGenerator = {
prop: {
templates: {
salesReport: `
<div class="report">
<header>
<h1>{{title}}</h1>
<p class="period">기간: {{startDate}} ~ {{endDate}}</p>
<p class="generated">생성일: {{generatedDate}}</p>
</header>
<main>
<div class="summary">
<h2>요약</h2>
<div class="summary-cards">
<div class="card">
<h3>총 매출</h3>
<p class="amount">{{totalSales}}원</p>
</div>
<div class="card">
<h3>거래 건수</h3>
<p class="amount">{{totalTransactions}}건</p>
</div>
<div class="card">
<h3>평균 거래액</h3>
<p class="amount">{{averageAmount}}원</p>
</div>
</div>
</div>
<div class="details">
<h2>상세 내역</h2>
<table class="data-table">
<thead>
<tr>
<th>날짜</th>
<th>상품명</th>
<th>수량</th>
<th>단가</th>
<th>금액</th>
</tr>
</thead>
<tbody>
{{#details}}
<tr>
<td>{{date}}</td>
<td>{{productName}}</td>
<td>{{quantity}}</td>
<td>{{unitPrice}}</td>
<td>{{amount}}</td>
</tr>
{{/details}}
</tbody>
</table>
</div>
{{#hasChart}}
<div class="chart-section">
<h2>차트</h2>
<div class="chart-placeholder">
<p>차트 이미지가 여기에 표시됩니다</p>
</div>
</div>
{{/hasChart}}
</main>
<footer>
<p>본 보고서는 자동 생성된 문서입니다.</p>
<p class="page-number">페이지 {{pageNumber}}</p>
</footer>
</div>
<style>
.report {
font-family: '맑은 고딕', Arial;
max-width: 800px;
margin: 0 auto;
padding: 40px;
line-height: 1.6;
}
header {
text-align: center;
border-bottom: 2px solid #333;
padding-bottom: 20px;
margin-bottom: 30px;
}
header h1 {
color: #333;
margin-bottom: 10px;
}
.period, .generated {
color: #666;
margin: 5px 0;
}
.summary-cards {
display: flex;
justify-content: space-between;
margin: 20px 0;
}
.card {
flex: 1;
background: #f8f9fa;
padding: 20px;
margin: 0 10px;
text-align: center;
border-radius: 8px;
}
.card h3 {
margin-bottom: 10px;
color: #495057;
}
.card .amount {
font-size: 24px;
font-weight: bold;
color: #007bff;
}
.data-table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.data-table th, .data-table td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
.data-table th {
background-color: #f2f2f2;
font-weight: bold;
}
.data-table tr:nth-child(even) {
background-color: #f9f9f9;
}
.chart-section {
margin: 30px 0;
}
.chart-placeholder {
height: 300px;
background: #f8f9fa;
border: 2px dashed #ddd;
display: flex;
align-items: center;
justify-content: center;
}
footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #ddd;
text-align: center;
font-size: 12px;
color: #666;
}
@media print {
.report { padding: 20px; }
.summary-cards { flex-direction: column; }
.card { margin: 10px 0; }
}
</style>
`
}
},

method: {
generateSalesReport(salesData, options) {
// 데이터 전처리
var processedData = $this.method.processSalesData(salesData);

// 템플릿 데이터 준비
var templateData = {
title: options.title || '매출 보고서',
startDate: options.startDate,
endDate: options.endDate,
generatedDate: new Date().toLocaleDateString('ko-KR'),
totalSales: processedData.totalSales.toLocaleString(),
totalTransactions: processedData.totalTransactions,
averageAmount: processedData.averageAmount.toLocaleString(),
details: processedData.details,
hasChart: options.includeChart || false,
pageNumber: 1
};

// PDF 생성
return syn.$p.generateFromTemplate(
$this.prop.templates.salesReport,
templateData,
{
filename: '매출보고서_' + options.startDate + '_' + options.endDate + '.pdf',
orientation: 'portrait',
format: 'A4',
margins: {
top: '20mm',
right: '15mm',
bottom: '20mm',
left: '15mm'
}
}
);
},

processSalesData(rawData) {
var totalSales = 0;
var totalTransactions = rawData.length;
var details = [];

rawData.forEach(function(item) {
var amount = item.quantity * item.unitPrice;
totalSales += amount;

details.push({
date: item.date,
productName: item.productName,
quantity: item.quantity,
unitPrice: item.unitPrice.toLocaleString(),
amount: amount.toLocaleString()
});
});

var averageAmount = totalTransactions > 0 ? totalSales / totalTransactions : 0;

return {
totalSales: totalSales,
totalTransactions: totalTransactions,
averageAmount: Math.round(averageAmount),
details: details
};
},

generateMonthlyReports(year, month) {
var promises = [];

// 각 부서별 보고서 생성
var departments = ['영업팀', '마케팅팀', '개발팀'];

departments.forEach(function(department) {
var promise = $this.method.fetchDepartmentData(department, year, month)
.then(function(data) {
return $this.method.generateSalesReport(data, {
title: department + ' 월간 보고서',
startDate: year + '-' + month + '-01',
endDate: year + '-' + month + '-31',
includeChart: true
});
});
promises.push(promise);
});

return Promise.all(promises);
},

fetchDepartmentData(department, year, month) {
// 실제 구현에서는 API 호출
return new Promise(function(resolve) {
setTimeout(function() {
var mockData = [
{ date: year + '-' + month + '-05', productName: '제품A', quantity: 10, unitPrice: 50000 },
{ date: year + '-' + month + '-10', productName: '제품B', quantity: 5, unitPrice: 80000 },
{ date: year + '-' + month + '-15', productName: '제품C', quantity: 8, unitPrice: 30000 }
];
resolve(mockData);
}, 1000);
});
}
},

event: {
btnGenerateReport_click() {
var startDate = document.getElementById('startDate').value;
var endDate = document.getElementById('endDate').value;

// 로딩 표시
document.getElementById('loadingSpinner').style.display = 'block';

$this.method.fetchDepartmentData('전체', startDate.substring(0, 4), startDate.substring(5, 7))
.then(function(data) {
return $this.method.generateSalesReport(data, {
title: '통합 매출 보고서',
startDate: startDate,
endDate: endDate,
includeChart: true
});
})
.then(function(result) {
document.getElementById('loadingSpinner').style.display = 'none';
console.log('보고서 생성 완료:', result);
})
.catch(function(error) {
document.getElementById('loadingSpinner').style.display = 'none';
console.error('보고서 생성 실패:', error);
});
},

btnPreviewReport_click() {
syn.$p.previewPdf({
selector: '#reportPreview',
title: '보고서 미리보기',
onConfirm: function() {
$this.event.btnGenerateReport_click();
}
});
}
}
};

2. 송장/견적서 생성 시스템

let $invoiceGenerator = {
prop: {
companyInfo: {
name: '(주)한국소프트웨어',
address: '서울특별시 강남구 테헤란로 123',
phone: '02-1234-5678',
email: 'info@company.com',
logo: '/images/company-logo.png'
}
},

method: {
generateInvoice(invoiceData) {
var template = $this.method.getInvoiceTemplate();

var templateData = {
...$this.prop.companyInfo,
invoiceNumber: invoiceData.invoiceNumber,
issueDate: invoiceData.issueDate,
dueDate: invoiceData.dueDate,
customer: invoiceData.customer,
items: invoiceData.items,
subtotal: $this.method.calculateSubtotal(invoiceData.items),
tax: $this.method.calculateTax(invoiceData.items),
total: $this.method.calculateTotal(invoiceData.items)
};

return syn.$p.generateFromTemplate(template, templateData, {
filename: '송장_' + invoiceData.invoiceNumber + '.pdf',
orientation: 'portrait',
format: 'A4'
});
},

generateQuote(quoteData) {
var template = $this.method.getQuoteTemplate();

var templateData = {
...$this.prop.companyInfo,
quoteNumber: quoteData.quoteNumber,
issueDate: quoteData.issueDate,
validUntil: quoteData.validUntil,
customer: quoteData.customer,
items: quoteData.items,
subtotal: $this.method.calculateSubtotal(quoteData.items),
tax: $this.method.calculateTax(quoteData.items),
total: $this.method.calculateTotal(quoteData.items),
terms: quoteData.terms || '본 견적서는 30일간 유효합니다.'
};

return syn.$p.generateFromTemplate(template, templateData, {
filename: '견적서_' + quoteData.quoteNumber + '.pdf',
orientation: 'portrait',
format: 'A4'
});
},

calculateSubtotal(items) {
return items.reduce(function(sum, item) {
return sum + (item.quantity * item.unitPrice);
}, 0);
},

calculateTax(items) {
var subtotal = $this.method.calculateSubtotal(items);
return subtotal * 0.1; // 10% 세금
},

calculateTotal(items) {
var subtotal = $this.method.calculateSubtotal(items);
var tax = $this.method.calculateTax(items);
return subtotal + tax;
},

getInvoiceTemplate() {
return `
<div class="invoice">
<div class="header">
<div class="company-info">
<h1>{{name}}</h1>
<p>{{address}}</p>
<p>전화: {{phone}} | 이메일: {{email}}</p>
</div>
<div class="invoice-info">
<h2>송장</h2>
<p>송장번호: {{invoiceNumber}}</p>
<p>발행일: {{issueDate}}</p>
<p>지불기한: {{dueDate}}</p>
</div>
</div>

<div class="customer-info">
<h3>고객 정보</h3>
<p>{{customer.name}}</p>
<p>{{customer.address}}</p>
<p>{{customer.phone}}</p>
</div>

<table class="items-table">
<thead>
<tr>
<th>항목</th>
<th>설명</th>
<th>수량</th>
<th>단가</th>
<th>금액</th>
</tr>
</thead>
<tbody>
{{#items}}
<tr>
<td>{{name}}</td>
<td>{{description}}</td>
<td>{{quantity}}</td>
<td>{{unitPrice}}</td>
<td>{{totalPrice}}</td>
</tr>
{{/items}}
</tbody>
</table>

<div class="totals">
<div class="total-row">
<span>소계:</span>
<span>{{subtotal}}원</span>
</div>
<div class="total-row">
<span>부가세(10%):</span>
<span>{{tax}}원</span>
</div>
<div class="total-row total-amount">
<span>총 금액:</span>
<span>{{total}}원</span>
</div>
</div>

<div class="footer">
<p>감사합니다.</p>
</div>
</div>

<style>
.invoice {
font-family: '맑은 고딕', Arial;
max-width: 800px;
margin: 0 auto;
padding: 30px;
}
.header {
display: flex;
justify-content: space-between;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #333;
}
.company-info h1 {
color: #333;
margin-bottom: 10px;
}
.invoice-info {
text-align: right;
}
.customer-info {
margin-bottom: 30px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 5px;
}
.items-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 30px;
}
.items-table th, .items-table td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
.items-table th {
background-color: #f2f2f2;
font-weight: bold;
}
.totals {
width: 300px;
margin-left: auto;
margin-bottom: 30px;
}
.total-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #eee;
}
.total-amount {
font-weight: bold;
font-size: 18px;
border-bottom: 2px solid #333;
padding-top: 12px;
}
.footer {
text-align: center;
margin-top: 40px;
font-style: italic;
}
</style>
`;
},

getQuoteTemplate() {
// 견적서 템플릿 (송장과 유사하지만 다른 문구 사용)
return $this.method.getInvoiceTemplate()
.replace('송장', '견적서')
.replace('송장번호', '견적번호')
.replace('지불기한', '유효기간');
}
},

event: {
btnGenerateInvoice_click() {
var invoiceData = {
invoiceNumber: 'INV-' + Date.now(),
issueDate: new Date().toLocaleDateString('ko-KR'),
dueDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toLocaleDateString('ko-KR'),
customer: {
name: '홍길동',
address: '서울시 송파구 잠실로 456',
phone: '010-1234-5678'
},
items: [
{
name: '웹사이트 개발',
description: '반응형 웹사이트 제작',
quantity: 1,
unitPrice: 2000000,
totalPrice: 2000000
},
{
name: '유지보수',
description: '월간 유지보수 서비스',
quantity: 12,
unitPrice: 100000,
totalPrice: 1200000
}
]
};

$this.method.generateInvoice(invoiceData);
}
}
};

3. 대량 문서 일괄 처리 시스템

let $batchProcessor = {
prop: {
processingQueue: [],
isProcessing: false,
maxConcurrent: 3
},

method: {
addToQueue(documentConfig) {
$this.prop.processingQueue.push({
id: Date.now() + Math.random(),
config: documentConfig,
status: 'pending',
createdAt: new Date()
});
},

processQueue() {
if ($this.prop.isProcessing) {
return;
}

$this.prop.isProcessing = true;
var pendingJobs = $this.prop.processingQueue.filter(function(job) {
return job.status === 'pending';
});

if (pendingJobs.length === 0) {
$this.prop.isProcessing = false;
return;
}

// 동시 처리할 작업 수 제한
var batchSize = Math.min(pendingJobs.length, $this.prop.maxConcurrent);
var currentBatch = pendingJobs.slice(0, batchSize);

var promises = currentBatch.map(function(job) {
job.status = 'processing';
job.startedAt = new Date();

return $this.method.processDocument(job)
.then(function(result) {
job.status = 'completed';
job.completedAt = new Date();
job.result = result;
return job;
})
.catch(function(error) {
job.status = 'failed';
job.error = error;
job.completedAt = new Date();
return job;
});
});

Promise.all(promises)
.then(function(completedJobs) {
$this.method.updateProgress();

// 큐에 남은 작업이 있으면 계속 처리
setTimeout(function() {
$this.prop.isProcessing = false;
$this.method.processQueue();
}, 1000);
})
.catch(function(error) {
console.error('배치 처리 오류:', error);
$this.prop.isProcessing = false;
});
},

processDocument(job) {
return new Promise(function(resolve, reject) {
var config = job.config;

switch(config.type) {
case 'element':
syn.$p.printPage({
selector: config.selector,
filename: config.filename,
orientation: config.orientation || 'portrait',
format: config.format || 'A4'
}).then(resolve).catch(reject);
break;

case 'html':
syn.$p.printHtml(config.html, {
filename: config.filename,
orientation: config.orientation || 'portrait',
format: config.format || 'A4'
}).then(resolve).catch(reject);
break;

case 'template':
syn.$p.generateFromTemplate(config.template, config.data, {
filename: config.filename,
orientation: config.orientation || 'portrait',
format: config.format || 'A4'
}).then(resolve).catch(reject);
break;

default:
reject(new Error('지원하지 않는 문서 타입: ' + config.type));
}
});
},

updateProgress() {
var total = $this.prop.processingQueue.length;
var completed = $this.prop.processingQueue.filter(function(job) {
return job.status === 'completed' || job.status === 'failed';
}).length;

var percentage = Math.round((completed / total) * 100);

// UI 업데이트
document.getElementById('progressBar').style.width = percentage + '%';
document.getElementById('progressText').textContent = completed + '/' + total + ' 완료';

if (completed === total) {
$this.method.onAllCompleted();
}
},

onAllCompleted() {
var completedJobs = $this.prop.processingQueue.filter(function(job) {
return job.status === 'completed';
});

var failedJobs = $this.prop.processingQueue.filter(function(job) {
return job.status === 'failed';
});

console.log('일괄 처리 완료');
console.log('성공:', completedJobs.length + '개');
console.log('실패:', failedJobs.length + '개');

// 결과 다운로드
if (completedJobs.length > 1) {
$this.method.downloadAsZip(completedJobs);
}

// 큐 초기화
$this.prop.processingQueue = [];
},

downloadAsZip(jobs) {
// 실제 구현에서는 JSZip 등의 라이브러리 사용
console.log('ZIP 파일로 다운로드:', jobs.length + '개 파일');

var zipFilename = '일괄처리결과_' +
new Date().toISOString().substring(0, 10) + '.zip';

// ZIP 파일 생성 및 다운로드 로직
},

generateMonthlyReports(year, month) {
var reportTypes = ['sales', 'inventory', 'customer', 'financial'];
var departments = ['총무팀', '영업팀', '마케팅팀', '개발팀'];

reportTypes.forEach(function(reportType) {
departments.forEach(function(department) {
$this.method.addToQueue({
type: 'template',
template: $this.method.getReportTemplate(reportType),
data: {
reportType: reportType,
department: department,
year: year,
month: month,
generatedDate: new Date().toLocaleDateString('ko-KR')
},
filename: reportType + '_' + department + '_' + year + month + '.pdf'
});
});
});

$this.method.processQueue();
}
},

event: {
btnStartBatchProcess_click() {
// 예제 문서들을 큐에 추가
var documents = [
{
type: 'element',
selector: '#report1',
filename: '보고서1.pdf'
},
{
type: 'element',
selector: '#report2',
filename: '보고서2.pdf'
},
{
type: 'html',
html: '<h1>HTML 문서</h1><p>내용...</p>',
filename: 'HTML문서.pdf'
}
];

documents.forEach(function(doc) {
$this.method.addToQueue(doc);
});

$this.method.processQueue();
},

btnGenerateMonthlyReports_click() {
var year = document.getElementById('reportYear').value;
var month = document.getElementById('reportMonth').value;

$this.method.generateMonthlyReports(year, month);
}
}
};

참고사항

  1. 브라우저 호환성: 일부 고급 인쇄 기능은 최신 브라우저에서만 지원됩니다
  2. 파일 크기: 대용량 문서나 고해상도 이미지 포함 시 처리 시간이 길어질 수 있습니다
  3. 메모리 관리: 대량 문서 처리 시 메모리 사용량을 모니터링해야 합니다
  4. 보안: PDF 생성 시 민감한 정보가 포함되지 않도록 주의해야 합니다
  5. 성능 최적화: 템플릿 캐싱과 배치 처리를 통해 성능을 향상시킬 수 있습니다

데모

Javascript 예제

await syn.$p.getSchemeText(syn.$p.getDocumentTemplateUrl(syn.$w.pageScript.substring(1)), true);

syn.$p.renderPrint(syn.$w.pageScript.substring(1), {
excelUrl: syn.$p.getDocumentTemplateUrl(syn.$w.pageScript.substring(1)),
workData: {
FormData0: {
}
}
});

syn.$p.requestReportValue(moduleID, 'Row|Form', pdfOptions, async (error, response) => {
if (error) {
result.DataTable1[0].ERROR_TEXT = error.message;
syn.$l.moduleEventLog(moduleID, 'GF01', 'pdfOptions: {0}, message: {1}'.format(JSON.stringify(pdfOptions), error.message), 'Error');
return callback(null, result);
}

var errorMessage = null;
try {
var payLoad = await syn.$p.generate(REPORT_ID, `${functionModule.config.ReportExcelBasePath}/${REPORT_ID}.xlsx`);
payLoad.workData = {
FormData0: response.FormData0
};

errorMessage = await module.exports.reportifyPdfDownload(syn.$p.getReportifyUrl(syn.$p.pageExcelToPdf), payLoad, {
dest: saveFilePath
});
} catch (error) {
errorMessage = error.message;
}

if (errorMessage) {
result.DataTable1[0].ERROR_TEXT = errorMessage;
syn.$l.moduleEventLog(moduleID, 'GF01', 'error: {0}'.format(errorMessage), 'Error');
}
else {
result.DataTable1[0].FILE_ID = saveFileID;
}

return callback(null, result);
});

syn.$rpt.updateSettings({
templateID: '새로운이력서.xlsx',
reportName: `새로운보고서.${formatDate(new Date(), 'yyyy-MM-dd')}.pdf`,
datetimeFormat: 'dd-MM-yyyy',
boolTrue: 'Yes',
boolFalse: 'No'
});

syn.$rpt.addWorkItem(0, 0, 'cell', 3, 'K', 'string', '조준철', null, null);
syn.$rpt.addWorkItem(0, 0, 'cell', 1, 'A', 'image', 'C:/projects/poc/QrameReport/reportify/wwwroot/img/profile.png', null, null);
syn.$rpt.addWorkItem(0, 0, 'cell:StoreForm1', 13, 'A', 'url', 'https://handstack.kr/docs/startup/%EA%B0%9C%EC%9A%94', '2:A3', null);
syn.$rpt.addWorkItem(0, 0, 'item:StoreGrid1', 43, ['A', 'G', 'AC'], ['date', 'string', 'string'], [
['2025-01-18', '자격증 1', '블라블라'],
['2025-01-18', '자격증 2', '블라블라'],
['2025-01-18', '자격증 3', '블라블라']
], null, 2);
syn.$rpt.addWorkItem(1, 2, 'cell', 13, 'A', 'html', '<div class=\'container\'>...</div>', '2:A3', null);
syn.$rpt.addWorkItem(2, 3, 'cell', 51, 'M', 'image', 'https://easywork.qcnservice.co.kr/assets/img/company/logo/easywork.qcnservice.co.kr.png', null, null);
syn.$rpt.addWorkItem(3, 1, 'list', 3, ['A', 'G', 'AC'], ['string', 'string', 'string'], [
['큐씨엔', '프로젝트 1', '블라블라'],
['큐씨엔', '프로젝트 2', '블라블라'],
['큐씨엔', '프로젝트 3', '블라블라'],
['큐씨엔', '프로젝트 4', '블라블라'],
['큐씨엔', '프로젝트 5', '블라블라'],
['큐씨엔', '프로젝트 6', '블라블라'],
['큐씨엔', '프로젝트 7', '블라블라'],
['큐씨엔', '프로젝트 8', '블라블라']
], null, 2);

syn.$rpt.addWorkItem({
document: 0,
worksheet: 0,
bind: 'cell',
row: 3,
col: 'K',
type: 'string',
data: '조준철'
});

console.log(JSON.stringify(syn.$rpt.generate(), null, 2));

소스) syn.$p Javascript 예제