링크/백링크 기능 수정 및 안정화
- 링크 생성 기능 완전 복구
- createDocumentLink 함수를 Alpine.js 데이터 객체 내로 이동
- API 필드명 불일치 수정 (source_text → selected_text 등)
- 필수 필드 검증 및 상세 에러 로깅 추가
- API 엔드포인트 수정
- 백엔드와 일치하도록 /documents/{id}/links, /documents/{id}/backlinks 사용
- 올바른 매개변수 전달 방식 적용
- 링크/백링크 렌더링 안정화
- forEach 오류 방지를 위한 배열 타입 검증
- 데이터 없을 시 기존 링크 유지 로직
- 안전한 초기화 및 에러 처리
- UI 단순화
- 같은 서적/다른 서적 구분 제거
- 서적 선택 → 문서 선택 단순한 2단계 프로세스
- 백링크는 자동 생성되도록 수정
- 디버깅 로그 대폭 강화
- API 호출, 응답, 렌더링 각 단계별 상세 로깅
- 데이터 타입 및 개수 추적
This commit is contained in:
@@ -10,6 +10,9 @@ class LinkManager {
|
||||
this.cachedApi = window.cachedApi || api;
|
||||
this.documentLinks = [];
|
||||
this.backlinks = [];
|
||||
|
||||
// 안전한 초기화 확인
|
||||
console.log('🔧 LinkManager 초기화 - backlinks 타입:', typeof this.backlinks, Array.isArray(this.backlinks));
|
||||
this.selectedText = '';
|
||||
this.selectedRange = null;
|
||||
this.availableBooks = [];
|
||||
@@ -23,10 +26,15 @@ class LinkManager {
|
||||
*/
|
||||
async loadDocumentLinks(documentId) {
|
||||
try {
|
||||
this.documentLinks = await this.cachedApi.get('/document-links', { document_id: documentId }, { category: 'links' }).catch(() => []);
|
||||
return this.documentLinks || [];
|
||||
console.log('📡 링크 API 호출:', `/documents/${documentId}/links`);
|
||||
const response = await this.cachedApi.get(`/documents/${documentId}/links`, {}, { category: 'links' }).catch(() => []);
|
||||
this.documentLinks = Array.isArray(response) ? response : [];
|
||||
console.log('📡 API 응답 링크 개수:', this.documentLinks.length);
|
||||
console.log('📡 API 응답 타입:', typeof response, response);
|
||||
return this.documentLinks;
|
||||
} catch (error) {
|
||||
console.error('문서 링크 로드 실패:', error);
|
||||
this.documentLinks = [];
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -36,10 +44,15 @@ class LinkManager {
|
||||
*/
|
||||
async loadBacklinks(documentId) {
|
||||
try {
|
||||
this.backlinks = await this.cachedApi.get('/document-links/backlinks', { target_document_id: documentId }, { category: 'links' }).catch(() => []);
|
||||
return this.backlinks || [];
|
||||
console.log('📡 백링크 API 호출:', `/documents/${documentId}/backlinks`);
|
||||
const response = await this.cachedApi.get(`/documents/${documentId}/backlinks`, {}, { category: 'links' }).catch(() => []);
|
||||
this.backlinks = Array.isArray(response) ? response : [];
|
||||
console.log('📡 API 응답 백링크 개수:', this.backlinks.length);
|
||||
console.log('📡 API 응답 타입:', typeof response, response);
|
||||
return this.backlinks;
|
||||
} catch (error) {
|
||||
console.error('백링크 로드 실패:', error);
|
||||
this.backlinks = [];
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -51,7 +64,20 @@ class LinkManager {
|
||||
const documentContent = document.getElementById('document-content');
|
||||
if (!documentContent) return;
|
||||
|
||||
// 안전한 링크 초기화
|
||||
if (!Array.isArray(this.documentLinks)) {
|
||||
console.warn('⚠️ this.documentLinks가 배열이 아닙니다. 빈 배열로 초기화합니다.');
|
||||
console.log('🔍 기존 this.documentLinks:', typeof this.documentLinks, this.documentLinks);
|
||||
this.documentLinks = [];
|
||||
}
|
||||
|
||||
console.log('🔗 링크 렌더링 시작 - 총', this.documentLinks.length, '개');
|
||||
|
||||
// 링크 데이터가 없으면 렌더링하지 않음 (기존 링크 유지)
|
||||
if (this.documentLinks.length === 0) {
|
||||
console.log('📝 링크 데이터가 없어서 기존 링크를 유지합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 기존 링크 제거
|
||||
const existingLinks = documentContent.querySelectorAll('.document-link');
|
||||
@@ -62,9 +88,13 @@ class LinkManager {
|
||||
});
|
||||
|
||||
// 각 링크 렌더링
|
||||
this.documentLinks.forEach(link => {
|
||||
this.renderSingleLink(link);
|
||||
});
|
||||
if (Array.isArray(this.documentLinks)) {
|
||||
this.documentLinks.forEach(link => {
|
||||
this.renderSingleLink(link);
|
||||
});
|
||||
} else {
|
||||
console.warn('⚠️ this.documentLinks가 배열이 아닙니다:', typeof this.documentLinks, this.documentLinks);
|
||||
}
|
||||
|
||||
console.log('✅ 링크 렌더링 완료');
|
||||
}
|
||||
@@ -166,26 +196,43 @@ class LinkManager {
|
||||
const documentContent = document.getElementById('document-content');
|
||||
if (!documentContent) return;
|
||||
|
||||
// 안전한 백링크 초기화
|
||||
if (!Array.isArray(this.backlinks)) {
|
||||
console.warn('⚠️ this.backlinks가 배열이 아닙니다. 빈 배열로 초기화합니다.');
|
||||
console.log('🔍 기존 this.backlinks:', typeof this.backlinks, this.backlinks);
|
||||
this.backlinks = [];
|
||||
}
|
||||
|
||||
console.log('🔗 백링크 렌더링 시작 - 총', this.backlinks.length, '개');
|
||||
|
||||
// 백링크 데이터가 없으면 렌더링하지 않음 (기존 백링크 유지)
|
||||
if (this.backlinks.length === 0) {
|
||||
console.log('📝 백링크 데이터가 없어서 기존 백링크를 유지합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 기존 백링크는 제거하지 않고 중복 체크만 함
|
||||
const existingBacklinks = documentContent.querySelectorAll('.backlink-highlight');
|
||||
console.log(`🔍 기존 백링크 ${existingBacklinks.length}개 발견 (유지)`);
|
||||
|
||||
// 각 백링크 렌더링 (중복되지 않는 것만)
|
||||
this.backlinks.forEach(backlink => {
|
||||
// 이미 렌더링된 백링크인지 확인
|
||||
const existingBacklink = Array.from(existingBacklinks).find(el =>
|
||||
el.dataset.backlinkId === backlink.id.toString()
|
||||
);
|
||||
|
||||
if (!existingBacklink) {
|
||||
console.log(`🆕 새로운 백링크 렌더링: ${backlink.id}`);
|
||||
this.renderSingleBacklink(backlink);
|
||||
} else {
|
||||
console.log(`✅ 백링크 이미 존재: ${backlink.id}`);
|
||||
}
|
||||
});
|
||||
if (Array.isArray(this.backlinks)) {
|
||||
this.backlinks.forEach(backlink => {
|
||||
// 이미 렌더링된 백링크인지 확인
|
||||
const existingBacklink = Array.from(existingBacklinks).find(el =>
|
||||
el.dataset.backlinkId === backlink.id.toString()
|
||||
);
|
||||
|
||||
if (!existingBacklink) {
|
||||
console.log(`🆕 새로운 백링크 렌더링: ${backlink.id}`);
|
||||
this.renderSingleBacklink(backlink);
|
||||
} else {
|
||||
console.log(`✅ 백링크 이미 존재: ${backlink.id}`);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.warn('⚠️ this.backlinks가 배열이 아닙니다:', typeof this.backlinks, this.backlinks);
|
||||
}
|
||||
|
||||
console.log('✅ 백링크 렌더링 완료');
|
||||
}
|
||||
@@ -534,8 +581,23 @@ class LinkManager {
|
||||
/**
|
||||
* 선택된 텍스트로 링크 생성
|
||||
*/
|
||||
async createLinkFromSelection(documentId, selectedText, selectedRange) {
|
||||
if (!selectedText || !selectedRange) return;
|
||||
async createLinkFromSelection(documentId = null, selectedText = null, selectedRange = null) {
|
||||
// 매개변수가 없으면 현재 선택된 텍스트 사용
|
||||
if (!selectedText || !selectedRange) {
|
||||
selectedText = window.getSelection().toString().trim();
|
||||
const selection = window.getSelection();
|
||||
|
||||
if (!selectedText || selection.rangeCount === 0) {
|
||||
alert('텍스트를 먼저 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
selectedRange = selection.getRangeAt(0);
|
||||
}
|
||||
|
||||
if (!documentId && window.documentViewerInstance) {
|
||||
documentId = window.documentViewerInstance.documentId;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🔗 링크 생성 시작:', selectedText);
|
||||
@@ -547,6 +609,12 @@ class LinkManager {
|
||||
window.documentViewerInstance.showLinkModal = true;
|
||||
window.documentViewerInstance.linkForm.selected_text = selectedText;
|
||||
|
||||
// 서적 목록 로드
|
||||
await window.documentViewerInstance.loadAvailableBooks();
|
||||
|
||||
// 기본적으로 같은 서적 문서들 로드
|
||||
await window.documentViewerInstance.loadSameBookDocuments();
|
||||
|
||||
// 텍스트 오프셋 계산
|
||||
const documentContent = document.getElementById('document-content');
|
||||
const fullText = documentContent.textContent;
|
||||
@@ -563,6 +631,8 @@ class LinkManager {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 텍스트 오프셋 계산
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user