핵심 가이드
저장과 버전 관리
Inko의 저장 모델은 단순합니다 — 뷰어는 편집 결과를 canvasData 문자열로 내보내고, 저장소는 고객사 DB입니다. 받은 문자열을 INSERT만 하면
그 테이블이 곧 Git처럼 쌓이는 버전 이력이 됩니다.
저장 흐름 — save()와 onSave
사용자가 뷰어의 저장 버튼을 누르거나 호스트 코드가 viewer.save()를 호출하면,
뷰어가 현재 편집 상태를 직렬화해(문자열 하나로 변환해) onSave(canvasData, ok, msg) 콜백으로 전달합니다.
var viewer = Inko.mount('#pdf-container', {
src: '/pdfv/index.html',
pdfUrl: '/files/contract.pdf',
onSave: function (canvasData, ok, msg) {
if (!ok) { console.error('저장 실패:', msg); return; }
// append-only: UPDATE가 아니라 항상 INSERT
fetch('/api/annotations', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
docId: 123,
userId: 'kim',
canvasData: canvasData
})
});
}
});
// 뷰어 내장 저장 버튼 외에, 호스트 쪽 버튼·단축키에서도 트리거 가능
document.querySelector('#my-save-btn').onclick = function () {
viewer.save(); // 응답은 위 onSave 콜백으로 전달
};| onSave 인자 | 타입 | 설명 |
|---|---|---|
canvasData | string | 직렬화된 편집 데이터. 내용을 해석하지 말고 그대로 저장합니다. |
ok | boolean | 직렬화 성공 여부. |
msg | string | 실패 시 사유 메시지. |
내부 형식은 SDK 버전에 따라 최적화될 수 있습니다. 파싱·가공하지 말고 받은 그대로 저장하고, 그대로 돌려주는 것이 호환성을 보장하는 사용 방식입니다.
DB 컬럼은 길이 제한이 없는 TEXT(또는 동급) 타입을 권장합니다.
append-only 버전 테이블 설계
"저장 = 새 버전 INSERT" 규칙만 지키면 별도의 버전 관리 시스템 없이 이력이 완성됩니다.
-- append-only 버전 테이블 예시 (PostgreSQL)
CREATE TABLE doc_annotations (
id BIGSERIAL PRIMARY KEY,
doc_id BIGINT NOT NULL, -- 원본 PDF 식별자
user_id VARCHAR(64) NOT NULL, -- 작성자
version INT NOT NULL, -- 문서 내 증가 번호
canvas_data TEXT NOT NULL, -- onSave로 받은 값 그대로
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 조회는 "이 문서의 모든 버전" 또는 "최신 1건"
CREATE INDEX idx_doc_annotations_doc
ON doc_annotations (doc_id, version DESC);
-- UPDATE · DELETE 없이 INSERT만 사용 — 이력은 절대 사라지지 않습니다.이 구조에서 자연스럽게 얻어지는 것들:
- 감사 추적(audit trail) — 누가·언제·어떤 상태로 저장했는지 행 단위로 남습니다.
- 시점 복원 — 어떤 버전이든
canvas_data를 꺼내 다시 열면 그 시점이 그대로 재현됩니다. - 버전 비교 화면 — 여러 버전을 레이어로 겹쳐 시각적으로 비교할 수 있습니다.
이어서 편집 — initialCanvasData
마운트 옵션 initialCanvasData에 저장해 둔 버전을 넘기면,
뷰어가 그 상태를 편집 캔버스에 복원한 채로 열립니다. 사용자는 중단한 지점부터 계속 작업합니다.
// 1) 서버: 최신 버전 1건 조회
// SELECT canvas_data FROM doc_annotations
// WHERE doc_id = 123 ORDER BY version DESC LIMIT 1;
// 2) 클라이언트: 받은 값을 그대로 주입
Inko.mount('#pdf-container', {
src: '/pdfv/index.html',
pdfUrl: '/files/contract.pdf',
initialCanvasData: latestCanvasData // ← 그 시점부터 이어서 편집
});
// 특정 과거 버전부터 다시 작업하려면 그 버전의 canvas_data를 넘기면 됩니다.
// 저장하면 새 버전으로 INSERT되므로 기존 이력은 그대로 보존됩니다.loadPdfUrl(url, fileName, canvasData) · loadPdfBase64(base64, fileName, canvasData)의 세 번째 인자가 initialCanvasData와 같은 역할을 합니다.
변경 감지와 자동저장 — onChange · getLastCanvasData
편집이 발생할 때마다 onChange(canvasData)가 호출됩니다.
SDK는 마지막 변경분을 내부에 캐시하므로, viewer.getLastCanvasData()로
언제든 최신 상태를 꺼내 자동저장에 활용할 수 있습니다.
var viewer = Inko.mount('#pdf-container', {
src: '/pdfv/index.html',
pdfUrl: '/files/contract.pdf',
onChange: function (canvasData) {
// 편집이 발생할 때마다 호출 — 임시 저장에 활용
sessionStorage.setItem('draft-123', canvasData);
}
});
// 또는 주기적 자동저장: 마지막 변경분 캐시를 읽어 서버로
setInterval(function () {
var draft = viewer.getLastCanvasData();
if (draft) navigator.sendBeacon('/api/annotations/draft', draft);
}, 30000);현재 페이지 편집 초기화 — clear()
viewer.clear()는 현재 보고 있는 페이지의 편집 캔버스를 비웁니다.
이미 DB에 저장된 과거 버전에는 영향이 없습니다 — append-only 모델에서 "지우기"도
지워진 상태를 새 버전으로 저장하는 것일 뿐입니다.