Skip to content

Latest commit

Β 

History

History
360 lines (276 loc) Β· 8.7 KB

File metadata and controls

360 lines (276 loc) Β· 8.7 KB

RefManager PDF Annotator

별도 ν˜ΈμŠ€νŒ…λ˜λŠ” PDF 주석 νŽΈμ§‘κΈ° - Base44 RefManager와 API둜 연동

πŸ“‹ κ°œμš”

이 앱은 Base44 RefManager와 λΆ„λ¦¬λ˜μ–΄ λ…λ¦½μ μœΌλ‘œ ν˜ΈμŠ€νŒ…λ˜λŠ” PDF 주석 νŽΈμ§‘κΈ°μž…λ‹ˆλ‹€. RefManager의 라이브러리 μ œμ•½μ‚¬ν•­μ„ μš°νšŒν•˜λ©΄μ„œ μ™„μ „ν•œ PDF 주석 κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. 데이터 μ˜μ†ν™”λŠ” μ„ νƒμ μœΌλ‘œ Supabaseλ₯Ό λ°±μ—”λ“œλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€. ν”„λŸ°νŠΈμ—”λ“œ ν˜ΈμŠ€νŒ…μ€ Vercel/Netlify λ“±μ—μ„œ μˆ˜ν–‰ν•˜μ„Έμš”.

μ£Όμš” κΈ°λŠ₯

  • πŸ“„ PDF λ·°μ–΄ (react-pdf 기반)
  • ✏️ ν…μŠ€νŠΈ ν•˜μ΄λΌμ΄νŠΈ
  • πŸ–ΌοΈ μ˜μ—­ 선택 주석
  • πŸ’Ύ RefManager API와 동기화 (선택)
  • ☁️ Supabase둜 주석 Save/Load (선택)
  • 🎨 λ‹€μ–‘ν•œ 색상 선택
  • πŸ“± λ°˜μ‘ν˜• UI

πŸ—οΈ μ•„ν‚€ν…μ²˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   RefManager (Base44)           β”‚
β”‚   - μ„œμ§€μ‚¬ν•­ 관리               β”‚
β”‚   - 인용 생성                   β”‚
β”‚   - Base44 Functions API        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚ REST API
             β”‚ (주석 데이터 κ΅ν™˜)
             ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   PDF Annotator (별도 ν˜ΈμŠ€νŒ…)   β”‚
β”‚   - react-pdf λ·°μ–΄              β”‚
β”‚   - pdf-lib 주석 처리           β”‚
β”‚   - IndexedDB μΊμ‹œ              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸš€ μ„€μΉ˜ 및 μ‹€ν–‰

1. μ˜μ‘΄μ„± μ„€μΉ˜

npm install

2. ν™˜κ²½ λ³€μˆ˜ μ„€μ •

.env.local νŒŒμΌμ„ μƒμ„±ν•˜κ³  ν•„μš”ν•œ 값을 μ„€μ •:

# RefManager Functions (선택)
VITE_REFMANAGER_API_URL=https://your-refmanager-app.base44.app/api

# Google Drive 연동 (선택)
VITE_GOOGLE_CLIENT_ID=your_google_client_id.apps.googleusercontent.com
VITE_GOOGLE_API_KEY=your_google_api_key

# Supabase (선택)
VITE_SUPABASE_URL=https://YOUR-PROJECT-REF.supabase.co
VITE_SUPABASE_ANON_KEY=your_anon_key

3. 개발 μ„œλ²„ μ‹€ν–‰

npm run dev

λΈŒλΌμš°μ €μ—μ„œ http://localhost:3000 μ—΄λ¦Ό (vite.config.jsμ—μ„œ 포트 3000 μ§€μ •)

4. λΉŒλ“œ

npm run build

λΉŒλ“œ 결과물은 dist 폴더에 μƒμ„±λ©λ‹ˆλ‹€.

πŸ› 디버깅 λͺ¨λ“œ

RefManager API 톡신 문제λ₯Ό μ§„λ‹¨ν•˜λ €λ©΄ λΈŒλΌμš°μ € μ½˜μ†”μ—μ„œ 디버깅 λͺ¨λ“œλ₯Ό ν™œμ„±ν™”ν•˜μ„Έμš”:

// ν™œμ„±ν™”
localStorage.setItem("debug_refmanager", "true");

// λΉ„ν™œμ„±ν™”
localStorage.removeItem("debug_refmanager");

디버깅 λͺ¨λ“œμ—μ„œλŠ”:

  • λͺ¨λ“  API μš”μ²­/μ‘λ‹΅μ˜ 상세 정보가 μ½˜μ†”μ— 좜λ ₯λ©λ‹ˆλ‹€
  • ν”„λ‘μ‹œ μ„œλ²„ 둜그 (Vercel Functions)에도 μΆ”κ°€ 정보가 κΈ°λ‘λ©λ‹ˆλ‹€
  • μ—…μŠ€νŠΈλ¦Ό νƒ€κ²Ÿ URL, μš”μ²­ λ°”λ””, 응닡 λ°”λ”” 미리보기 확인 κ°€λŠ₯

μ€‘μš”: ν”„λ‘œλ•μ…˜μ—μ„œλŠ” λ°˜λ“œμ‹œ λΉ„ν™œμ„±ν™”ν•˜μ„Έμš” (토큰 정보가 λ‘œκ·Έμ— λ…ΈμΆœλ  수 있음)

πŸ”— RefManager와 연동

URL νŒŒλΌλ―Έν„°

PDF AnnotatorλŠ” λ‹€μŒ URL νŒŒλΌλ―Έν„°λ₯Ό λ°›μŠ΅λ‹ˆλ‹€:

https://your-pdf-annotator.app/?referenceId=REF123&token=AUTH_TOKEN&title=λ…Όλ¬Έμ œλͺ©&pdfUrl=https://...

νŒŒλΌλ―Έν„°:

  • referenceId (선택): RefManager μ°Έκ³ λ¬Έν—Œ ID. 미제곡 μ‹œ μž„μ‹œ λͺ¨λ“œλ‘œ λ™μž‘ν•˜λ©° URL μž…λ ₯ λ˜λŠ” 둜컬 μ—…λ‘œλ“œλ‘œ PDFλ₯Ό μ—΄ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • token (선택): Base44 인증 토큰. RefManager APIλ₯Ό ν˜ΈμΆœν•  λ•Œλ§Œ ν•„μš”ν•©λ‹ˆλ‹€.
  • title (선택): PDF 제λͺ© (APIμ—μ„œ κ°€μ Έμ˜€μ§€ μ•Šμ„ 경우)
  • pdfUrl (선택): PDF 직접 URL (APIμ—μ„œ κ°€μ Έμ˜€μ§€ μ•Šμ„ 경우)

RefManagerμ—μ„œ 호좜

방법 1: μƒˆ νƒ­μœΌλ‘œ μ—΄κΈ°

// RefManager의 References.jsx
const openPDFAnnotator = (reference) => {
  const token = getBase44AuthToken(); // Base44 μ„Έμ…˜ 토큰
  const url = new URL('https://your-pdf-annotator.app/');
  url.searchParams.set('referenceId', reference.id);
  url.searchParams.set('token', token);
  if (reference.title) url.searchParams.set('title', reference.title);
  if (reference.pdf_url) url.searchParams.set('pdfUrl', reference.pdf_url);
  
  window.open(url.toString(), '_blank');
};

방법 2: iframe μž„λ² λ“œ

// RefManager의 PDFViewModal.jsx
<iframe
  src={`https://your-pdf-annotator.app/?referenceId=${refId}&token=${token}`}
  style={{ width: '100%', height: '100%', border: 'none' }}
  allow="fullscreen"
/>

πŸ”§ Base44 Functions API

RefManager 앱에 λ‹€μŒ Functionsλ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•©λ‹ˆλ‹€:

1. getPdfInfo

μš”μ²­:

POST /api/functions/getPdfInfo
{
  "referenceId": "REF123"
}

응닡:

{
  "referenceId": "REF123",
  "title": "연ꡬ λ…Όλ¬Έ 제λͺ©",
  "pdfUrl": "https://drive.google.com/...",
  "author_ids": ["AUTH1", "AUTH2"],
  "year": 2023
}

2. getAnnotations

μš”μ²­:

POST /api/functions/getAnnotations
{
  "referenceId": "REF123"
}

응닡:

{
  "success": true,
  "annotations": [
    {
      "id": "ANNOT1",
      "reference_id": "REF123",
      "type": "highlight",
      "page_number": 1,
      "content": "ν•˜μ΄λΌμ΄νŠΈλœ ν…μŠ€νŠΈ",
      "position": { "rects": [...] },
      "color": "#FFFF00"
    }
  ]
}

3. saveAnnotation

μš”μ²­:

POST /api/functions/saveAnnotation
{
  "reference_id": "REF123",
  "type": "highlight",
  "page_number": 1,
  "content": "ν•˜μ΄λΌμ΄νŠΈ ν…μŠ€νŠΈ",
  "position": { "rects": [...] },
  "color": "#FFFF00"
}

응닡:

{
  "success": true,
  "annotation": {
    "id": "ANNOT2",
    "reference_id": "REF123",
    ...
  }
}

4. deleteAnnotation

μš”μ²­:

POST /api/functions/deleteAnnotation
{
  "annotationId": "ANNOT1"
}

응닡:

{
  "success": true
}

πŸ—„οΈ Base44 데이터 λͺ¨λΈ

RefManager에 PdfAnnotation μ—”ν‹°ν‹° 생성:

// Base44μ—μ„œ
const PdfAnnotation = Entity({
  name: 'PdfAnnotation',
  fields: {
    reference_id: { type: 'reference', entity: 'Reference' },
    type: { type: 'string' }, // 'highlight', 'text_note', 'drawing'
    page_number: { type: 'number' },
    content: { type: 'text' },
    position: { type: 'json' },
    color: { type: 'string' },
    created_at: { type: 'datetime', default: 'now' },
  }
});

πŸ” 인증

PDF AnnotatorλŠ” RefManagerμ—μ„œ 전달받은 Base44 인증 토큰을 μ‚¬μš©ν•©λ‹ˆλ‹€:

// API ν΄λΌμ΄μ–ΈνŠΈ (src/api/refManagerClient.js)
const token = localStorage.getItem('base44_auth_token');
fetch(API_URL, {
  headers: {
    'Authorization': `Bearer ${token}`
  }
});

πŸ“¦ 배포

Netlify

npm run build

dist 폴더λ₯Ό Netlify에 배포

Vercel

vercel --prod

ν™˜κ²½ λ³€μˆ˜ μ„€μ • (배포 μ‹œ)

배포 ν”Œλž«νΌ(Vercel/Netlify)의 ν”„λ‘œμ νŠΈ μ„€μ •μ—μ„œ μœ„ ν™˜κ²½ λ³€μˆ˜λ“€μ„ λ™μΌν•˜κ²Œ λ“±λ‘ν•˜μ„Έμš”. Drive λ˜λŠ” Supabaseλ₯Ό μ“°μ§€ μ•ŠμœΌλ©΄ ν•΄λ‹Ή ν‚€λŠ” μƒλž΅ κ°€λŠ₯ν•©λ‹ˆλ‹€.

μΆ”κ°€λ‘œ, Drive 연동을 μ“΄λ‹€λ©΄ Google Cloud OAuth의 Authorized JavaScript origins에 배포 도메인을 등둝해야 ν•©λ‹ˆλ‹€.

πŸ› οΈ 개발

ν”„λ‘œμ νŠΈ ꡬ쑰

pdf_annotator/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ api/
β”‚   β”‚   └── refManagerClient.js   # RefManager API ν΄λΌμ΄μ–ΈνŠΈ
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   └── pdf/
β”‚   β”‚       β”œβ”€β”€ PDFViewer.jsx      # 메인 PDF λ·°μ–΄
β”‚   β”‚       β”œβ”€β”€ PDFHighlight.jsx   # ν•˜μ΄λΌμ΄νŠΈ νˆ΄λ°”
β”‚   β”‚       └── PageHighlightOverlay.jsx  # 주석 μ˜€λ²„λ ˆμ΄
β”‚   β”œβ”€β”€ utils/
β”‚   β”‚   β”œβ”€β”€ pdfExport.js          # PDF 내보내기
β”‚   β”‚   └── pdfManager.js         # PDF 관리
β”‚   β”œβ”€β”€ db/
β”‚   β”‚   └── localDB.js            # IndexedDB (μ˜€ν”„λΌμΈ μΊμ‹œ)
β”‚   β”œβ”€β”€ App.jsx                   # 메인 μ•±
β”‚   └── main.jsx                  # μ—”νŠΈλ¦¬ 포인트
β”œβ”€β”€ package.json
β”œβ”€β”€ vite.config.js
└── index.html

μ£Όμš” μ˜μ‘΄μ„±

  • react: ^18.3.1
  • react-pdf: ^9.1.1
  • pdfjs-dist: ^4.8.69
  • pdf-lib: ^1.17.1
  • idb: ^8.0.0
  • @supabase/supabase-js: ^2.x (선택)

πŸ› 문제 ν•΄κ²°

CORS μ—λŸ¬

RefManager APIμ—μ„œ CORS 헀더 μ„€μ •:

// Base44 Functionμ—μ„œ
response.headers['Access-Control-Allow-Origin'] = 'https://your-pdf-annotator.app';

PDF λ‘œλ“œ μ‹€νŒ¨

  1. pdfUrl이 μ˜¬λ°”λ₯Έμ§€ 확인
  2. Google Drive 파일의 곡유 μ„€μ • 확인
  3. CORS ν”„λ‘μ‹œ μ‚¬μš© κ³ λ €

Supabase Save/Load λ²„νŠΌμ΄ 보이지 μ•ŠμŒ

  • VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEYκ°€ μ„€μ •λ˜μ–΄ μžˆλŠ”μ§€ 확인
  • ν™˜κ²½ λ³€μˆ˜ λ³€κ²½ ν›„μ—λŠ” 개발 μ„œλ²„ μž¬μ‹œμž‘/배포 μž¬μ‹€ν–‰ ν•„μš”

πŸ“ λΌμ΄μ„ μŠ€

MIT License

🀝 κΈ°μ—¬

이슈 및 PR ν™˜μ˜ν•©λ‹ˆλ‹€!