최근 Spring Boot와 Vue2를 사용한 프론트엔드 분리 프로젝트를 개발 중이며, 국내 우수한 프레임워크인 jeecg와 ant-design-vue를 사용하고 있습니다. 프로젝트에서 파일 업로드 기능을 구현해야 했으며, 이미지와 PDF 파일을 온라인으로 미리보기하고 다운로드할 수 있어야 했습니다. 또한 페이지에서 직접 PDF 파일을 인쇄할 필요가 있었습니다. 프레임워크에 내장된 vue-print-nb-jeecg 컴포넌트는 비교적 간단하여 단일 페이지 인쇄만 지원하고 다중 페이지 인쇄는 불가능했습니다. 신중한 비교와 검토를 거쳐, 결국 vue-pdf와 print-js 컴포넌트를 사용하여 요구 사항을 충족하기로 결정했습니다.
vue-pdf는 온라인 미리보기를, print-js는 더 강력한 인쇄 기능을 제공하여 PDF, HTML, IMAGE, JSON과 같은 다양한 문서 유형을 지원합니다. 기본적으로 PDF로 설정됩니다. 사실 vue-pdf도 인쇄 기능을 구현할 수 있지만, 앞서 언급한 vue-print-nb와 마찬가지로 페이지에 표시된 첫 페이지 내용만 인쇄할 수 있습니다(미리보기 표시는 문제 없음). Print.js 공식 문서는 여기에서 확인하세요.
-
vue 프로젝트에 vue-pdf와 Print.js 설치 yarn add vue-pdf yarn add print-js
-
전역으로 가져오거나 필요한 파일에서 가져오기 import pdf from 'vue-pdf' import printJS from 'print-js'
-
주요 코드 구현
<a-modal :visible="pdfPreviewModal" :footer="null" @cancel="closePdfPreview" :width="800">
<div v-if="isPdfFile" style="overflow-y: auto;overflow-x: hidden;">
<a-button shape="round" icon="file-pdf" @click="performPrint(printConfig)" size="small">인쇄</a-button>
<div id="pdfPrintArea">
<pdf ref="pdfViewer" v-for="pageIndex in totalPages"
:src="currentPdfUrl"
:key="pageIndex"
:page="pageIndex"></pdf>
</div>
</div>
</a-modal>
인쇄 버튼 실행 메서드
// 인쇄 설정 데이터
printConfig: {
targetElementId: 'pdfPrintArea',
documentFormat: 'html',
headerTitle: '',
excludedClasses: ['no-print']
},
// 인쇄 실행 메서드
performPrint(config) {
printJS({
printable: config.targetElementId, // 인쇄할 요소의 ID
type: config.documentFormat || 'html',
header: config.headerTitle, // '문서',
targetStyles: ['*'],
style: '@page {margin:0 10mm};', // 선택 사항 - 인쇄 시 머리글과 꼬리글 제거
ignoreElements: config.excludedClasses || [], // ['no-print']
properties: config.properties || null
})
}
다른 컴포넌트에서 파일 유형에 따라 이미지는 이미지로, PDF는 PDF로 미리보기를 표시합니다.
- 전체 코드 예시 (핵심 로직)
<template>
<div>
<a-upload
name="file"
:multiple="true"
:action="uploadUrl"
:headers="authHeaders"
:data="{'biz': businessPath}"
:file-list="uploadedFiles"
@change="handleFileChange"
:list-type="'picture-card'">
<template #placeholder>
<a-icon type="plus" />
<div class="ant-upload-text">파일 선택</div>
</template>
</a-upload>
<a-modal :visible="pdfPreviewModal" :footer="null" @cancel="closePdfPreview" :width="800">
<div v-if="isPdfFile" style="overflow-y: auto;overflow-x: hidden;">
<a-button shape="round" icon="file-pdf" @click="performPrint(printConfig)" size="small">인쇄</a-button>
<div id="pdfPrintArea">
<pdf ref="pdfViewer" v-for="pageIndex in totalPages"
:src="currentPdfUrl"
:key="pageIndex"
:page="pageIndex"></pdf>
</div>
</div>
</a-modal>
</div>
</template>
<script>
import { ACCESS_TOKEN } from "@/store/mutation-types";
import { getFileAccessHttpUrl } from '@/api/manage';
import pdf from 'vue-pdf';
import printJS from 'print-js';
export default {
name: 'FileUploader',
components: { pdf },
data() {
return {
uploadUrl: window._CONFIG['domianURL'] + "/sys/common/upload",
authHeaders: {},
uploadedFiles: [],
pdfPreviewModal: false,
currentPdfUrl: '',
totalPages: 0,
isPdfFile: false,
printConfig: {
targetElementId: 'pdfPrintArea',
documentFormat: 'html',
headerTitle: '',
excludedClasses: ['no-print']
}
};
},
created() {
const token = Vue.ls.get(ACCESS_TOKEN);
this.authHeaders = {"X-Access-Token": token};
},
methods: {
handleFileChange(info) {
if (info.file.status === 'done') {
const file = info.file;
const fileType = this.determineFileType(file.name);
if (fileType === 'pdf') {
this.openPdfPreview(file.url);
}
}
},
determineFileType(fileName) {
const suffix = fileName.substr(fileName.lastIndexOf('.') + 1).toLowerCase();
const pdfTypes = ['pdf'];
return pdfTypes.includes(suffix) ? 'pdf' : 'other';
},
openPdfPreview(pdfUrl) {
this.currentPdfUrl = pdfUrl;
this.isPdfFile = true;
this.pdfPreviewModal = true;
this.fetchPdfPageCount();
},
closePdfPreview() {
this.pdfPreviewModal = false;
this.isPdfFile = false;
},
fetchPdfPageCount() {
const loadingTask = pdf.createLoadingTask(this.currentPdfUrl);
loadingTask.promise.then(pdfDoc => {
this.totalPages = pdfDoc.numPages;
}).catch(error => {
console.error("PDF 페이지 수를 가져오는 중 오류 발생:", error);
});
},
performPrint(config) {
printJS({
printable: config.targetElementId,
type: config.documentFormat || 'html',
header: config.headerTitle,
targetStyles: ['*'],
style: '@page {margin:0 10mm};',
ignoreElements: config.excludedClasses || [],
properties: config.properties || null
})
}
}
}
</script>
결론적으로, vue-pdf와 print-js를 조합하여 PDF 미리보기와 인쇄 기능을 구현하는 방법을 살펴보았습니다. 이 외에도 바이두 개발자가 만든 vue-office 컴포넌트 라이브러리도 있으며, 장점은 사용이 간단하고 초보자에게 친화적이며 파일 주소 하나만 전달하면 미리보기를 구현할 수 있다는 점입니다. docx, excel, pdf 세 가지 일반적인 파일 미리보기를 한 번에 해결하는 종합적인 솔루션을 제공합니다. 미리보기 효과도 좋으며 콘텐츠뿐만 아니라 스타일도 지원합니다. 미리보기 효과는 정말 뛰어나지만, 인쇄 기능을 지원하지 않아 요구 사항을 충족시키지 못하는 것이 아쉽습니다. vue-office의 데모 효과는 Vue 프레임워크 데모 효과에서 확인해볼 수 있습니다.