๋ณ๋ ํธ์คํ ๋๋ 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 ํ์ํฉ๋๋ค!