백엔드 연동 (fetcher)
1.fetcher 계약
| 함수 | 호출 시점 | 반환 |
|---|---|---|
open() | viewer.open() | 문서 모델 JSON |
pageRender({ page, renderScale }) | 페이지 표시 | 페이지 이미지 Blob (PNG) |
annotationRender({ annotation, renderScale }) | 이미지 주석 표시 | 주석 이미지 Blob |
save({ saveFormData }) | viewer.save() | 저장 후 문서 모델 JSON |
digest({ saveFormData }) | viewer.sign() 1단계 | 서명 대상 해시 (hex 문자열) |
sign({ saveFormData }) | viewer.sign() 2단계 | 서명 완료 문서 모델 JSON |
2.백엔드 엔드포인트
백엔드 라이브러리(JAR)를 사용해 아래 형태의 엔드포인트를 구성합니다.
| 메서드 | 경로 (예시) | 역할 |
|---|---|---|
| GET | /document/{이름} | PDF 로드 → 문서 모델 JSON |
| GET | /document/{이름}/page/{번호}/render?scale= | 페이지 이미지 |
| GET | /document/{이름}/annotation/{번호}/{세대}/render | 주석 이미지 |
| POST | /document/{이름}/save/{저장이름} | 문서 모델 반영 후 저장 |
| POST | /document/{이름}/digitalsignature/digest | 서명 해시 생성 |
| POST | /document/{이름}/digitalsignature/sign | 서명 주입, 저장 |
3.구현 예
JSfetcher 구현
const BACKEND = 'https://내-서버';
const fileName = 'devsite.pdf';
const fetcher = {
open: async () => {
const response = await fetch(`${BACKEND}/document/${encodeURIComponent(fileName)}`);
return await response.json();
},
pageRender: async ({ page, renderScale = 2 }) => {
const response = await fetch(
`${BACKEND}/document/${encodeURIComponent(fileName)}/page/${page.pageIndex}/render?scale=${renderScale}`);
return await response.blob();
},
save: async ({ saveFormData }) => {
const response = await fetch(
`${BACKEND}/document/${encodeURIComponent(fileName)}/save/${encodeURIComponent(fileName)}`,
{ method: 'POST', body: saveFormData });
return await response.json();
},
};
viewer.setFetcher(fetcher);• 참고
saveFormData는 뷰어가 만들어 넘겨주는 FormData입니다. document 파트(문서 모델 JSON)와 imageFileList 파트(추가된 이미지)가 들어 있으며, 백엔드에서 @RequestPart로 받으면 됩니다.
4.백엔드 쪽 구현 (Java)
JAVA문서 열기/저장 엔드포인트
// 열기: PDF 로드 → 문서 모델 JSON 반환
@GetMapping("/document/{name}")
public Document open(@PathVariable String name) throws IOException {
return Document.loadFromPDF(pdfPath.resolve(name), cachePath.resolve(name));
}
// 저장: 모델 반영(applyToPDF) 후 파일로 저장(saveToPDF)
@PostMapping("/document/{name}/save/{saveName}")
public Document save(@PathVariable String name, @PathVariable String saveName,
@RequestPart("document") Document document,
@RequestPart(name = "imageFileList", required = false) MultipartFile[] imageFileList) throws IOException {
HashMap<String, byte[]> imageFileMap = toMap(imageFileList);
document.applyToPDF(pdfPath.resolve(name), imageFileMap);
document.saveToPDF(pdfPath.resolve(saveName), cachePath.resolve(saveName));
return document;
}5.에러 처리
fetcher 함수가 예외를 던지지 않으면 뷰어는 실패를 인지하지 못합니다. 특히 open에서 서버 에러(500 등)를 그대로 통과시키면 에러 응답 본문을 문서로 오인할 수 있으므로, 반드시 response.ok를 확인하고 실패 시 throw 하십시오.
JS응답 검사와 예외 처리
open: async () => {
const response = await fetch(`${BACKEND}/document/${u}`);
if (!response.ok) {
const body = await response.json().catch(() => ({}));
throw new Error(`문서 열기 실패 (HTTP ${response.status}): ${body.message ?? response.statusText}`);
}
return await response.json();
},호출 측에서는 viewer.open()/viewer.save()를 try/catch로 감싸 사용자에게 안내합니다.
JS호출 측
try {
await viewer.save();
} catch (err) {
alert(err.message);
}