북곽툴즈 개발기 ④ 브라우저에서 끝내는 PDF·이미지 도구
북곽툴즈 개발기.
문서 변환은 한컴 때문에 서버가 필요했지만, 나머지 도구는 굳이 서버를 거칠 이유가 없었다. 브라우저 안에서 처리하면 세 가지가 좋다. 파일이 기기를 떠나지 않아 안전하고, 서버 부하가 없고, 업로드·다운로드 왕복이 없어 빠르다. 그래서 가능한 건 전부 클라이언트에서 끝냈다.
PDF 도구
- PDF 합치기와 분할은 pdf-lib로 처리한다. 여러 PDF의 페이지를 새 문서에 복사해 붙이거나, 페이지 단위로 잘라 새 PDF를 만든다.
- 이미지를 PDF로 묶는 것도 pdf-lib다. 사진·스캔 이미지를 한 장씩 페이지로 넣는다.
- PDF를 이미지로 내보내는 건 pdf.js(pdfjs-dist)로 각 페이지를 canvas에 렌더한 뒤 PNG나 JPG로 뽑는다. 페이지가 여러 장이면 JSZip으로 묶어 ZIP으로 받는다.
브라우저가 PDF를 직접 그릴 수 있다는 게 핵심이다. 서버에 보낼 필요 없이 화면에 렌더한 캔버스를 그대로 이미지로 저장하면 된다.
이미지 형식 변환
JPG, PNG, WebP는 Canvas로 상호 변환한다. 이미지를 캔버스에 그린 뒤 원하는 포맷으로 다시 내보내는 방식이다.
문제는 아이폰 사진의 HEIC였다. 브라우저가 HEIC를 기본으로 못 읽어서, heic2any로 먼저 디코딩한 다음 캔버스로 넘긴다. 아이폰으로 찍은 사진을 그대로 올려도 변환되도록 한 부분이다.
구슬 뽑기
조를 짜거나 한 명을 뽑을 때 쓰는 도구인데, 단순한 난수 추첨으로 만들기는 심심했다. 그래서 물리 엔진을 썼다.
- box2d-wasm 물리 엔진으로 구슬이 실제로 떨어지고 부딪히고 굴러간다.
- GSAP로 연출을 다듬었다.
도착 순서대로 모둠을 편성하거나 한 명을 뽑는 식인데, 결과가 물리로 정해지니 보는 재미가 있다. 기능만 보면 난수 하나면 되지만, 학생들이 실제로 쓰는 도구라 "보는 맛"이 의외로 중요했다.
공통 부분
드래그 앤 드롭은 react-dropzone, 아이콘은 lucide-react를 썼다. 클라이언트 도구는 node 모듈(파일 시스템 등)을 import하면 안 된다. 그런 코드가 클라이언트 번들에 섞이면 Turbopack이 빌드 단계에서 에러를 낸다. 그래서 서버 전용 로직과 브라우저 전용 로직의 경계를 처음부터 분명히 그어 뒀다.