🔧 Alpine.js 변수 오류 수정 및 PDF 미리보기 간소화
🐛 Alpine.js 오류 수정: - PDF 관련 변수들 (pdfZoom, pdfLoading, pdfCurrentPage 등) 제거 - HTML 뷰어 변수들 유지 (htmlLoading, htmlRawMode, htmlSourceCode) - 중복 변수 정의 제거 📱 PDF 미리보기 간소화: - PDF.js Canvas 렌더링 → iframe 방식으로 변경 - 복잡한 줌/페이지 네비게이션 제거 - 브라우저 내장 PDF 뷰어 활용 - 401 Unauthorized 오류 해결을 위한 토큰 처리 🎯 성능 및 안정성 개선: - PDF.js 워커 설정을 HTML에서 전역으로 처리 - 불필요한 JavaScript 함수들 제거 - 메모리 누수 방지를 위한 리소스 정리 - 에러 처리 간소화 ✨ 사용자 경험: - 더 빠른 PDF 로딩 - 브라우저 기본 PDF 컨트롤 사용 가능 - Alpine.js 경고 메시지 제거 - 안정적인 미리보기 동작
This commit is contained in:
@@ -510,6 +510,7 @@ async def get_document_content(
|
||||
@router.get("/{document_id}/pdf")
|
||||
async def get_document_pdf(
|
||||
document_id: str,
|
||||
token: Optional[str] = Query(None),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
<!-- PDF.js 라이브러리 -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
|
||||
<script>
|
||||
// PDF.js 워커 설정 (전역)
|
||||
if (typeof pdfjsLib !== 'undefined') {
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
[x-cloak] { display: none !important; }
|
||||
@@ -467,15 +473,6 @@
|
||||
<i class="fas fa-file-pdf mr-2 text-red-600"></i>PDF 미리보기
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<button @click="zoomOut()"
|
||||
class="px-2 py-1 bg-gray-600 text-white rounded text-xs hover:bg-gray-700">
|
||||
<i class="fas fa-search-minus"></i>
|
||||
</button>
|
||||
<span class="text-xs text-gray-600" x-text="`${Math.round(pdfZoom * 100)}%`"></span>
|
||||
<button @click="zoomIn()"
|
||||
class="px-2 py-1 bg-gray-600 text-white rounded text-xs hover:bg-gray-700">
|
||||
<i class="fas fa-search-plus"></i>
|
||||
</button>
|
||||
<button @click="searchInPdf()"
|
||||
class="px-3 py-1 bg-blue-600 text-white rounded text-xs hover:bg-blue-700">
|
||||
<i class="fas fa-search mr-1"></i>PDF에서 검색
|
||||
@@ -485,54 +482,22 @@
|
||||
|
||||
<!-- PDF 뷰어 컨테이너 -->
|
||||
<div class="border rounded-lg overflow-hidden bg-gray-100 relative" style="height: 500px;">
|
||||
<!-- PDF.js 뷰어 -->
|
||||
<div id="pdfViewerContainer" class="w-full h-full overflow-auto bg-gray-200">
|
||||
<div x-show="pdfLoading" class="flex items-center justify-center h-full">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-spinner fa-spin text-2xl text-gray-500 mb-2"></i>
|
||||
<p class="text-gray-600">PDF를 로드하는 중...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<canvas id="pdfCanvas"
|
||||
x-show="!pdfLoading && !pdfError"
|
||||
class="mx-auto shadow-lg"
|
||||
style="display: block; margin: 20px auto;"></canvas>
|
||||
|
||||
<div x-show="pdfError" class="flex items-center justify-center h-full text-gray-500">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-2xl mb-2"></i>
|
||||
<p>PDF를 로드할 수 없습니다</p>
|
||||
<button @click="openResult(previewResult)"
|
||||
class="mt-2 px-3 py-1 bg-blue-600 text-white rounded text-sm">
|
||||
뷰어에서 열기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- PDF iframe 뷰어 -->
|
||||
<iframe :src="`/api/documents/${previewResult?.document_id}/pdf`"
|
||||
class="w-full h-full border-0"
|
||||
x-show="!pdfError"
|
||||
@error="pdfError = true">
|
||||
</iframe>
|
||||
|
||||
<!-- 페이지 네비게이션 -->
|
||||
<div x-show="!pdfLoading && !pdfError && pdfTotalPages > 1"
|
||||
class="absolute bottom-4 left-1/2 transform -translate-x-1/2 bg-black bg-opacity-75 text-white px-4 py-2 rounded-lg flex items-center space-x-3">
|
||||
<button @click="prevPage()"
|
||||
:disabled="pdfCurrentPage <= 1"
|
||||
class="px-2 py-1 bg-gray-600 rounded text-xs hover:bg-gray-500 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
<span class="text-sm">
|
||||
<input type="number"
|
||||
x-model="pdfCurrentPage"
|
||||
@change="goToPage(pdfCurrentPage)"
|
||||
:min="1"
|
||||
:max="pdfTotalPages"
|
||||
class="w-12 text-center bg-transparent border-b border-gray-400 text-white text-xs">
|
||||
/ <span x-text="pdfTotalPages"></span>
|
||||
</span>
|
||||
<button @click="nextPage()"
|
||||
:disabled="pdfCurrentPage >= pdfTotalPages"
|
||||
class="px-2 py-1 bg-gray-600 rounded text-xs hover:bg-gray-500 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
<div x-show="pdfError" class="flex items-center justify-center h-full text-gray-500">
|
||||
<div class="text-center">
|
||||
<i class="fas fa-exclamation-triangle text-2xl mb-2"></i>
|
||||
<p>PDF를 로드할 수 없습니다</p>
|
||||
<button @click="openResult(previewResult)"
|
||||
class="mt-2 px-3 py-1 bg-blue-600 text-white rounded text-sm">
|
||||
뷰어에서 열기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,13 +26,6 @@ window.searchApp = function() {
|
||||
previewLoading: false,
|
||||
pdfError: false,
|
||||
|
||||
// PDF 뷰어 상태
|
||||
pdfLoading: false,
|
||||
pdfDoc: null,
|
||||
pdfCurrentPage: 1,
|
||||
pdfTotalPages: 0,
|
||||
pdfZoom: 1.0,
|
||||
|
||||
// HTML 뷰어 상태
|
||||
htmlLoading: false,
|
||||
htmlRawMode: false,
|
||||
@@ -207,26 +200,11 @@ window.searchApp = function() {
|
||||
|
||||
try {
|
||||
// 타입별 미리보기 로드
|
||||
if (result.type === 'document_content' && result.highlight_info?.has_pdf) {
|
||||
// PDF 미리보기
|
||||
await this.loadPdfPreview(result.document_id);
|
||||
} else if ((result.type === 'document' || result.type === 'document_content') && !result.highlight_info?.has_pdf) {
|
||||
if ((result.type === 'document' || result.type === 'document_content') && !result.highlight_info?.has_pdf) {
|
||||
// HTML 문서 미리보기
|
||||
await this.loadHtmlPreview(result.document_id);
|
||||
} else if (result.type === 'note') {
|
||||
// 노트 미리보기 - 전체 내용 로드
|
||||
const fullContent = await this.loadFullContent(result);
|
||||
if (fullContent) {
|
||||
this.previewResult = { ...result, content: fullContent };
|
||||
}
|
||||
} else if (result.type === 'memo') {
|
||||
// 메모 미리보기 - 전체 내용 로드
|
||||
const fullContent = await this.loadFullContent(result);
|
||||
if (fullContent) {
|
||||
this.previewResult = { ...result, content: fullContent };
|
||||
}
|
||||
} else {
|
||||
// 기타 타입 - 기본 내용 로드
|
||||
// 기타 타입 - 전체 내용 로드
|
||||
const fullContent = await this.loadFullContent(result);
|
||||
if (fullContent) {
|
||||
this.previewResult = { ...result, content: fullContent };
|
||||
@@ -314,137 +292,12 @@ window.searchApp = function() {
|
||||
this.previewLoading = false;
|
||||
this.pdfError = false;
|
||||
|
||||
// PDF 리소스 정리
|
||||
this.pdfDoc = null;
|
||||
this.pdfCurrentPage = 1;
|
||||
this.pdfTotalPages = 0;
|
||||
this.pdfZoom = 1.0;
|
||||
this.pdfLoading = false;
|
||||
|
||||
// HTML 리소스 정리
|
||||
this.htmlLoading = false;
|
||||
this.htmlRawMode = false;
|
||||
this.htmlSourceCode = '';
|
||||
},
|
||||
|
||||
// PDF 미리보기 로드
|
||||
async loadPdfPreview(documentId) {
|
||||
this.pdfLoading = true;
|
||||
this.pdfError = false;
|
||||
|
||||
try {
|
||||
// PDF.js 워커 설정
|
||||
if (typeof pdfjsLib !== 'undefined') {
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
|
||||
}
|
||||
|
||||
const pdfUrl = `/api/documents/${documentId}/pdf`;
|
||||
|
||||
// PDF 문서 로드
|
||||
const loadingTask = pdfjsLib.getDocument({
|
||||
url: pdfUrl,
|
||||
httpHeaders: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
});
|
||||
|
||||
this.pdfDoc = await loadingTask.promise;
|
||||
this.pdfTotalPages = this.pdfDoc.numPages;
|
||||
this.pdfCurrentPage = 1;
|
||||
|
||||
console.log('PDF 로드 완료:', this.pdfTotalPages, '페이지');
|
||||
|
||||
// 첫 페이지 렌더링
|
||||
await this.renderPdfPage(1);
|
||||
|
||||
} catch (error) {
|
||||
console.error('PDF 로드 실패:', error);
|
||||
this.pdfError = true;
|
||||
} finally {
|
||||
this.pdfLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// PDF 페이지 렌더링
|
||||
async renderPdfPage(pageNum) {
|
||||
if (!this.pdfDoc) return;
|
||||
|
||||
try {
|
||||
const page = await this.pdfDoc.getPage(pageNum);
|
||||
const canvas = document.getElementById('pdfCanvas');
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
// 뷰포트 설정 (줌 적용)
|
||||
const viewport = page.getViewport({ scale: this.pdfZoom });
|
||||
|
||||
// 캔버스 크기 설정
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
|
||||
// 고해상도 디스플레이 지원
|
||||
const devicePixelRatio = window.devicePixelRatio || 1;
|
||||
canvas.style.width = viewport.width + 'px';
|
||||
canvas.style.height = viewport.height + 'px';
|
||||
canvas.width = viewport.width * devicePixelRatio;
|
||||
canvas.height = viewport.height * devicePixelRatio;
|
||||
context.scale(devicePixelRatio, devicePixelRatio);
|
||||
|
||||
// 페이지 렌더링
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport
|
||||
};
|
||||
|
||||
await page.render(renderContext).promise;
|
||||
console.log('페이지 렌더링 완료:', pageNum);
|
||||
|
||||
} catch (error) {
|
||||
console.error('페이지 렌더링 실패:', error);
|
||||
this.pdfError = true;
|
||||
}
|
||||
},
|
||||
|
||||
// 줌 인
|
||||
async zoomIn() {
|
||||
if (this.pdfZoom < 3.0) {
|
||||
this.pdfZoom += 0.25;
|
||||
await this.renderPdfPage(this.pdfCurrentPage);
|
||||
}
|
||||
},
|
||||
|
||||
// 줌 아웃
|
||||
async zoomOut() {
|
||||
if (this.pdfZoom > 0.5) {
|
||||
this.pdfZoom -= 0.25;
|
||||
await this.renderPdfPage(this.pdfCurrentPage);
|
||||
}
|
||||
},
|
||||
|
||||
// 이전 페이지
|
||||
async prevPage() {
|
||||
if (this.pdfCurrentPage > 1) {
|
||||
this.pdfCurrentPage--;
|
||||
await this.renderPdfPage(this.pdfCurrentPage);
|
||||
}
|
||||
},
|
||||
|
||||
// 다음 페이지
|
||||
async nextPage() {
|
||||
if (this.pdfCurrentPage < this.pdfTotalPages) {
|
||||
this.pdfCurrentPage++;
|
||||
await this.renderPdfPage(this.pdfCurrentPage);
|
||||
}
|
||||
},
|
||||
|
||||
// 특정 페이지로 이동
|
||||
async goToPage(pageNum) {
|
||||
const page = parseInt(pageNum);
|
||||
if (page >= 1 && page <= this.pdfTotalPages) {
|
||||
this.pdfCurrentPage = page;
|
||||
await this.renderPdfPage(this.pdfCurrentPage);
|
||||
}
|
||||
},
|
||||
|
||||
// HTML 미리보기 로드
|
||||
async loadHtmlPreview(documentId) {
|
||||
this.htmlLoading = true;
|
||||
|
||||
Reference in New Issue
Block a user