λ³λ νΈμ€ν λλ 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 μΊμ β
βββββββββββββββββββββββββββββββββββ
npm install.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_keynpm run devλΈλΌμ°μ μμ http://localhost:3000 μ΄λ¦Ό (vite.config.jsμμ ν¬νΈ 3000 μ§μ )
npm run buildλΉλ κ²°κ³Όλ¬Όμ dist ν΄λμ μμ±λ©λλ€.
RefManager API ν΅μ λ¬Έμ λ₯Ό μ§λ¨νλ €λ©΄ λΈλΌμ°μ μ½μμμ λλ²κΉ λͺ¨λλ₯Ό νμ±ννμΈμ:
// νμ±ν
localStorage.setItem("debug_refmanager", "true");
// λΉνμ±ν
localStorage.removeItem("debug_refmanager");λλ²κΉ λͺ¨λμμλ:
- λͺ¨λ API μμ²/μλ΅μ μμΈ μ λ³΄κ° μ½μμ μΆλ ₯λ©λλ€
- νλ‘μ μλ² λ‘κ·Έ (Vercel Functions)μλ μΆκ° μ λ³΄κ° κΈ°λ‘λ©λλ€
- μ μ€νΈλ¦Ό νκ² 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μ 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');
};// RefManagerμ PDFViewModal.jsx
<iframe
src={`https://your-pdf-annotator.app/?referenceId=${refId}&token=${token}`}
style={{ width: '100%', height: '100%', border: 'none' }}
allow="fullscreen"
/>RefManager μ±μ λ€μ Functionsλ₯Ό ꡬνν΄μΌ ν©λλ€:
μμ²:
POST /api/functions/getPdfInfo
{
"referenceId": "REF123"
}μλ΅:
{
"referenceId": "REF123",
"title": "μ°κ΅¬ λ
Όλ¬Έ μ λͺ©",
"pdfUrl": "https://drive.google.com/...",
"author_ids": ["AUTH1", "AUTH2"],
"year": 2023
}μμ²:
POST /api/functions/getAnnotations
{
"referenceId": "REF123"
}μλ΅:
{
"success": true,
"annotations": [
{
"id": "ANNOT1",
"reference_id": "REF123",
"type": "highlight",
"page_number": 1,
"content": "νμ΄λΌμ΄νΈλ ν
μ€νΈ",
"position": { "rects": [...] },
"color": "#FFFF00"
}
]
}μμ²:
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",
...
}
}μμ²:
POST /api/functions/deleteAnnotation
{
"annotationId": "ANNOT1"
}μλ΅:
{
"success": true
}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}`
}
});npm run builddist ν΄λλ₯Ό Netlifyμ λ°°ν¬
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.1react-pdf: ^9.1.1pdfjs-dist: ^4.8.69pdf-lib: ^1.17.1idb: ^8.0.0@supabase/supabase-js: ^2.x (μ ν)
RefManager APIμμ CORS ν€λ μ€μ :
// Base44 Functionμμ
response.headers['Access-Control-Allow-Origin'] = 'https://your-pdf-annotator.app';pdfUrlμ΄ μ¬λ°λ₯Έμ§ νμΈ- Google Drive νμΌμ 곡μ μ€μ νμΈ
- CORS νλ‘μ μ¬μ© κ³ λ €
- VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEYκ° μ€μ λμ΄ μλμ§ νμΈ
- νκ²½ λ³μ λ³κ²½ νμλ κ°λ° μλ² μ¬μμ/λ°°ν¬ μ¬μ€ν νμ
MIT License
μ΄μ λ° PR νμν©λλ€!