🔧 미리보기 인증 및 에러 처리 개선
🛠️ 401 Unauthorized 오류 해결: - HTML 콘텐츠 API 호출을 api.get() 래퍼로 변경 - iframe src에 토큰 파라미터 추가 - 백엔드에서 _token 쿼리 파라미터 지원 🛠️ 404 Not Found 오류 해결: - 문서 상세 정보를 먼저 로드하여 PDF/HTML 존재 여부 확인 - PDF/HTML 파일 존재 여부에 따른 조건부 렌더링 - 미리보기 타입 자동 감지 및 적절한 뷰어 선택 🎯 에러 처리 및 UX 개선: - HTML 로드 실패 시 에러 메시지 표시 - 미리보기 불가능한 콘텐츠에 대한 fallback UI - 문서 정보 로드 실패 시 기본 내용으로 fallback - '원본에서 보기' 버튼으로 대안 제공 🔍 미리보기 로직 개선: - 문서 타입별 적절한 미리보기 방식 자동 선택 - PDF 존재 시 PDF 뷰어, HTML 존재 시 HTML 뷰어 - 검색어 하이라이트 타이밍 최적화 - 로딩 상태 및 에러 상태 명확한 구분
This commit is contained in:
@@ -468,6 +468,7 @@ async def get_document(
|
||||
@router.get("/{document_id}/content")
|
||||
async def get_document_content(
|
||||
document_id: str,
|
||||
_token: Optional[str] = Query(None),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
<!-- 모달 내용 -->
|
||||
<div class="p-6 overflow-y-auto max-h-[60vh]">
|
||||
<!-- PDF 미리보기 (데본씽크 스타일) -->
|
||||
<div x-show="previewResult?.type === 'document_content' && previewResult?.highlight_info?.has_pdf"
|
||||
<div x-show="(previewResult?.type === 'document_content' || previewResult?.type === 'document') && previewResult?.highlight_info?.has_pdf"
|
||||
class="mb-4">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="text-sm font-medium text-gray-800">
|
||||
@@ -640,14 +640,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 본문 내용 -->
|
||||
<div x-show="!previewResult?.highlight_info?.has_pdf || previewResult?.type !== 'document_content'"
|
||||
<!-- 본문 내용 (PDF/HTML이 아닌 경우) -->
|
||||
<div x-show="!previewResult?.highlight_info?.has_pdf && !previewResult?.highlight_info?.has_html"
|
||||
class="prose max-w-none">
|
||||
<div class="text-gray-700 leading-relaxed"
|
||||
style="white-space: pre-wrap; word-wrap: break-word; max-height: 400px; overflow-y: auto;"
|
||||
x-html="highlightText(previewResult?.content || '', searchQuery)"></div>
|
||||
</div>
|
||||
|
||||
<!-- 기본 텍스트 내용 (fallback) -->
|
||||
<div x-show="!previewResult?.highlight_info?.has_pdf && !previewResult?.highlight_info?.has_html && (!previewResult?.content || previewResult?.content.length < 10)"
|
||||
class="text-center py-8 text-gray-500">
|
||||
<i class="fas fa-file-alt text-3xl mb-3"></i>
|
||||
<p>미리보기할 수 있는 내용이 없습니다.</p>
|
||||
<button @click="openResult(previewResult)"
|
||||
class="mt-3 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||
원본에서 보기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 추가 정보 -->
|
||||
<div x-show="previewResult?.type === 'memo'" class="mt-4 p-3 bg-purple-50 border border-purple-200 rounded-lg">
|
||||
<div class="text-sm font-medium text-purple-800 mb-1">
|
||||
|
||||
@@ -199,10 +199,36 @@ window.searchApp = function() {
|
||||
this.previewLoading = true;
|
||||
|
||||
try {
|
||||
// 타입별 미리보기 로드
|
||||
if ((result.type === 'document' || result.type === 'document_content') && !result.highlight_info?.has_pdf) {
|
||||
// HTML 문서 미리보기
|
||||
await this.loadHtmlPreview(result.document_id);
|
||||
// 문서 타입인 경우 상세 정보 먼저 로드
|
||||
if (result.type === 'document' || result.type === 'document_content') {
|
||||
try {
|
||||
const docInfo = await this.api.get(`/documents/${result.document_id}`);
|
||||
// PDF 정보 업데이트
|
||||
this.previewResult = {
|
||||
...result,
|
||||
highlight_info: {
|
||||
...result.highlight_info,
|
||||
has_pdf: !!docInfo.pdf_path,
|
||||
has_html: !!docInfo.html_path
|
||||
}
|
||||
};
|
||||
|
||||
// PDF가 있으면 PDF 미리보기, 없으면 HTML 미리보기
|
||||
if (docInfo.pdf_path) {
|
||||
// PDF 미리보기는 iframe으로 자동 처리
|
||||
console.log('PDF 미리보기 준비 완료');
|
||||
} else if (docInfo.html_path) {
|
||||
// HTML 문서 미리보기
|
||||
await this.loadHtmlPreview(result.document_id);
|
||||
}
|
||||
} catch (docError) {
|
||||
console.error('문서 정보 로드 실패:', docError);
|
||||
// 기본 내용 로드로 fallback
|
||||
const fullContent = await this.loadFullContent(result);
|
||||
if (fullContent) {
|
||||
this.previewResult = { ...result, content: fullContent };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 기타 타입 - 전체 내용 로드
|
||||
const fullContent = await this.loadFullContent(result);
|
||||
@@ -303,35 +329,39 @@ window.searchApp = function() {
|
||||
this.htmlLoading = true;
|
||||
|
||||
try {
|
||||
// HTML 내용 가져오기
|
||||
const response = await fetch(`/api/documents/${documentId}/content`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
});
|
||||
// API를 통해 HTML 내용 가져오기
|
||||
const htmlContent = await this.api.get(`/documents/${documentId}/content`);
|
||||
|
||||
if (response.ok) {
|
||||
const htmlContent = await response.text();
|
||||
if (htmlContent) {
|
||||
this.htmlSourceCode = this.escapeHtml(htmlContent);
|
||||
|
||||
// iframe에 HTML 로드
|
||||
const iframe = document.getElementById('htmlPreviewFrame');
|
||||
if (iframe) {
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
doc.open();
|
||||
doc.write(htmlContent);
|
||||
doc.close();
|
||||
// iframe src를 직접 설정 (인증 헤더 포함)
|
||||
const token = localStorage.getItem('token');
|
||||
iframe.src = `/api/documents/${documentId}/content?_token=${encodeURIComponent(token)}`;
|
||||
|
||||
// 검색어 하이라이트 (iframe 내부)
|
||||
if (this.searchQuery) {
|
||||
this.highlightInIframe(iframe, this.searchQuery);
|
||||
}
|
||||
// iframe 로드 완료 후 검색어 하이라이트
|
||||
iframe.onload = () => {
|
||||
if (this.searchQuery) {
|
||||
setTimeout(() => {
|
||||
this.highlightInIframe(iframe, this.searchQuery);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
throw new Error('HTML 로드 실패');
|
||||
throw new Error('HTML 내용이 비어있습니다');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('HTML 미리보기 로드 실패:', error);
|
||||
// 에러 시 기본 내용 표시
|
||||
this.htmlSourceCode = `<div class="p-4 text-center text-gray-500">
|
||||
<i class="fas fa-exclamation-triangle text-2xl mb-2"></i>
|
||||
<p>HTML 내용을 로드할 수 없습니다.</p>
|
||||
<p class="text-sm">${error.message}</p>
|
||||
</div>`;
|
||||
} finally {
|
||||
this.htmlLoading = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user